/*************************************************************************** qgsvectorlayer.cpp -------------------- begin : Oct 29, 2003 copyright : (C) 2003 by Gary E.Sherman email : sherman at mrcc.com This class implements a generic means to display vector layers. The features and attributes are read from the data store using a "data provider" plugin. QgsVectorLayer can be used with any data store for which an appropriate plugin is available. ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "qgsvectorlayer.h" #include "qgsactionmanager.h" #include "qgis.h" //for globals #include "qgsapplication.h" #include "qgsclipper.h" #include "qgsconditionalstyle.h" #include "qgscoordinatereferencesystem.h" #include "qgscoordinatetransform.h" #include "qgscurvev2.h" #include "qgsdatasourceuri.h" #include "qgsexpressionfieldbuffer.h" #include "qgsfeature.h" #include "qgsfeaturerequest.h" #include "qgsfield.h" #include "qgsgeometrycache.h" #include "qgsgeometry.h" #include "qgslabel.h" #include "qgslegacyhelpers.h" #include "qgslogger.h" #include "qgsmaplayerlegend.h" #include "qgsmaplayerregistry.h" #include "qgsmaptopixel.h" #include "qgsmessagelog.h" #include "qgsogcutils.h" #include "qgspoint.h" #include "qgsproject.h" #include "qgsproviderregistry.h" #include "qgsrectangle.h" #include "qgsrelationmanager.h" #include "qgsrendercontext.h" #include "qgsvectordataprovider.h" #include "qgsvectorlayereditbuffer.h" #include "qgsvectorlayereditpassthrough.h" #include "qgsvectorlayereditutils.h" #include "qgsvectorlayerfeatureiterator.h" #include "qgsvectorlayerjoinbuffer.h" #include "qgsvectorlayerlabeling.h" #include "qgsvectorlayerrenderer.h" #include "qgsvectorlayerundocommand.h" #include "qgspointv2.h" #include "qgsrendererv2.h" #include "qgssymbolv2.h" #include "qgssymbollayerv2.h" #include "qgssinglesymbolrendererv2.h" #include "qgsdiagramrendererv2.h" #include "qgsstylev2.h" #include "qgssymbologyv2conversion.h" #include "qgspallabeling.h" #include "qgssimplifymethod.h" #include "qgsexpressioncontext.h" #include "diagram/qgsdiagram.h" #ifdef TESTPROVIDERLIB #include #endif typedef bool saveStyle_t( const QString& uri, const QString& qmlStyle, const QString& sldStyle, const QString& styleName, const QString& styleDescription, const QString& uiFileContent, bool useAsDefault, QString& errCause ); typedef QString loadStyle_t( const QString& uri, QString& errCause ); typedef int listStyles_t( const QString& uri, QStringList &ids, QStringList &names, QStringList &descriptions, QString& errCause ); typedef QString getStyleById_t( const QString& uri, QString styleID, QString& errCause ); QgsVectorLayer::QgsVectorLayer( const QString& vectorLayerPath, const QString& baseName, const QString& providerKey, bool loadDefaultStyleFlag ) : QgsMapLayer( VectorLayer, baseName, vectorLayerPath ) , mDataProvider( nullptr ) , mProviderKey( providerKey ) , mReadOnly( false ) , mEditFormConfig( new QgsEditFormConfig( this ) ) , mWkbType( QGis::WKBUnknown ) , mRendererV2( nullptr ) , mLabel( nullptr ) , mLabelOn( false ) , mLabeling( new QgsVectorLayerSimpleLabeling ) , mLabelFontNotFoundNotified( false ) , mFeatureBlendMode( QPainter::CompositionMode_SourceOver ) // Default to normal feature blending , mLayerTransparency( 0 ) , mVertexMarkerOnlyForSelection( false ) , mCache( new QgsGeometryCache() ) , mEditBuffer( nullptr ) , mJoinBuffer( nullptr ) , mExpressionFieldBuffer( nullptr ) , mDiagramRenderer( nullptr ) , mDiagramLayerSettings( nullptr ) , mValidExtent( false ) , mLazyExtent( true ) , mSymbolFeatureCounted( false ) , mEditCommandActive( false ) { mActions = new QgsActionManager( this ); mConditionalStyles = new QgsConditionalLayerStyles(); // if we're given a provider type, try to create and bind one to this layer if ( ! mProviderKey.isEmpty() ) { setDataSource( vectorLayerPath, baseName, providerKey, loadDefaultStyleFlag ); } connect( this, SIGNAL( selectionChanged( QgsFeatureIds, QgsFeatureIds, bool ) ), this, SIGNAL( selectionChanged() ) ); connect( this, SIGNAL( selectionChanged( QgsFeatureIds, QgsFeatureIds, bool ) ), this, SIGNAL( repaintRequested() ) ); // Default simplify drawing settings QSettings settings; mSimplifyMethod.setSimplifyHints( static_cast< QgsVectorSimplifyMethod::SimplifyHints >( settings.value( "/qgis/simplifyDrawingHints", static_cast< int>( mSimplifyMethod.simplifyHints() ) ).toInt() ) ); mSimplifyMethod.setSimplifyAlgorithm( static_cast< QgsVectorSimplifyMethod::SimplifyAlgorithm >( settings.value( "/qgis/simplifyAlgorithm", static_cast< int>( mSimplifyMethod.simplifyAlgorithm() ) ).toInt() ) ); mSimplifyMethod.setThreshold( settings.value( "/qgis/simplifyDrawingTol", mSimplifyMethod.threshold() ).toFloat() ); mSimplifyMethod.setForceLocalOptimization( settings.value( "/qgis/simplifyLocal", mSimplifyMethod.forceLocalOptimization() ).toBool() ); mSimplifyMethod.setMaximumScale( settings.value( "/qgis/simplifyMaxScale", mSimplifyMethod.maximumScale() ).toFloat() ); } // QgsVectorLayer ctor QgsVectorLayer::~QgsVectorLayer() { QgsDebugMsg( "entered." ); emit layerDeleted(); mValid = false; delete mDataProvider; delete mEditBuffer; delete mJoinBuffer; delete mExpressionFieldBuffer; delete mCache; delete mLabel; // old deprecated implementation delete mLabeling; delete mDiagramLayerSettings; delete mDiagramRenderer; delete mActions; delete mRendererV2; delete mConditionalStyles; } QString QgsVectorLayer::storageType() const { if ( mDataProvider ) { return mDataProvider->storageType(); } return nullptr; } QString QgsVectorLayer::capabilitiesString() const { if ( mDataProvider ) { return mDataProvider->capabilitiesString(); } return nullptr; } QString QgsVectorLayer::dataComment() const { if ( mDataProvider ) { return mDataProvider->dataComment(); } return QString(); } QString QgsVectorLayer::providerType() const { return mProviderKey; } /** * sets the preferred display field based on some fuzzy logic */ void QgsVectorLayer::setDisplayField( const QString& fldName ) { if ( !hasGeometryType() ) return; // If fldName is provided, use it as the display field, otherwise // determine the field index for the feature column of the identify // dialog. We look for fields containing "name" first and second for // fields containing "id". If neither are found, the first field // is used as the node. QString idxName = ""; QString idxId = ""; if ( !fldName.isEmpty() ) { mDisplayField = fldName; } else { int fieldsSize = mUpdatedFields.size(); Q_FOREACH ( const QgsField& field, mUpdatedFields ) { QString fldName = field.name(); QgsDebugMsg( "Checking field " + fldName + " of " + QString::number( fieldsSize ) + " total" ); // Check the fields and keep the first one that matches. // We assume that the user has organized the data with the // more "interesting" field names first. As such, name should // be selected before oldname, othername, etc. if ( fldName.indexOf( "name", 0, Qt::CaseInsensitive ) > -1 ) { if ( idxName.isEmpty() ) { idxName = fldName; } } if ( fldName.indexOf( "descrip", 0, Qt::CaseInsensitive ) > -1 ) { if ( idxName.isEmpty() ) { idxName = fldName; } } if ( fldName.indexOf( "id", 0, Qt::CaseInsensitive ) > -1 ) { if ( idxId.isEmpty() ) { idxId = fldName; } } } //if there were no fields in the dbf just return - otherwise qgis segfaults! if ( fieldsSize == 0 ) return; if ( idxName.length() > 0 ) { mDisplayField = idxName; } else { if ( idxId.length() > 0 ) { mDisplayField = idxId; } else { mDisplayField = mUpdatedFields.at( 0 ).name(); } } } } // NOTE this is a temporary method added by Tim to prevent label clipping // which was occurring when labeller was called in the main draw loop // This method will probably be removed again in the near future! void QgsVectorLayer::drawLabels( QgsRenderContext& rendererContext ) { if ( !hasGeometryType() ) return; QgsDebugMsg( "Starting draw of labels: " + id() ); if ( mRendererV2 && mLabelOn && mLabel && mLabel->isInScaleRange( rendererContext.rendererScale() ) ) { QgsAttributeList attributes; Q_FOREACH ( const QString& attrName, mRendererV2->usedAttributes() ) { int attrNum = fieldNameIndex( attrName ); attributes.append( attrNum ); } // make sure the renderer is ready for classification ("symbolForFeature") mRendererV2->startRender( rendererContext, fields() ); // Add fields required for labels mLabel->addRequiredFields( attributes ); QgsDebugMsg( "Selecting features based on view extent" ); int featureCount = 0; try { // select the records in the extent. The provider sets a spatial filter // and sets up the selection set for retrieval QgsFeatureIterator fit = getFeatures( QgsFeatureRequest() .setFilterRect( rendererContext.extent() ) .setSubsetOfAttributes( attributes ) ); QgsFeature fet; while ( fit.nextFeature( fet ) ) { if ( mRendererV2->willRenderFeature( fet, rendererContext ) ) { bool sel = mSelectedFeatureIds.contains( fet.id() ); mLabel->renderLabel( rendererContext, fet, sel, nullptr ); } featureCount++; } } catch ( QgsCsException &e ) { Q_UNUSED( e ); QgsDebugMsg( "Error projecting label locations" ); } if ( mRendererV2 ) { mRendererV2->stopRender( rendererContext ); } QgsDebugMsg( QString( "Total features processed %1" ).arg( featureCount ) ); } } void QgsVectorLayer::reload() { if ( mDataProvider ) { mDataProvider->reloadData(); updateFields(); } } QgsMapLayerRenderer* QgsVectorLayer::createMapRenderer( QgsRenderContext& rendererContext ) { return new QgsVectorLayerRenderer( this, rendererContext ); } bool QgsVectorLayer::draw( QgsRenderContext& rendererContext ) { QgsVectorLayerRenderer renderer( this, rendererContext ); return renderer.render(); } void QgsVectorLayer::drawVertexMarker( double x, double y, QPainter& p, QgsVectorLayer::VertexMarkerType type, int m ) { if ( type == QgsVectorLayer::SemiTransparentCircle ) { p.setPen( QColor( 50, 100, 120, 200 ) ); p.setBrush( QColor( 200, 200, 210, 120 ) ); p.drawEllipse( x - m, y - m, m * 2 + 1, m * 2 + 1 ); } else if ( type == QgsVectorLayer::Cross ) { p.setPen( QColor( 255, 0, 0 ) ); p.drawLine( x - m, y + m, x + m, y - m ); p.drawLine( x - m, y - m, x + m, y + m ); } } void QgsVectorLayer::select( QgsFeatureId fid ) { mSelectedFeatureIds.insert( fid ); emit selectionChanged( QgsFeatureIds() << fid, QgsFeatureIds(), false ); } void QgsVectorLayer::select( const QgsFeatureIds& featureIds ) { mSelectedFeatureIds.unite( featureIds ); emit selectionChanged( featureIds, QgsFeatureIds(), false ); } void QgsVectorLayer::deselect( const QgsFeatureId fid ) { mSelectedFeatureIds.remove( fid ); emit selectionChanged( QgsFeatureIds(), QgsFeatureIds() << fid, false ); } void QgsVectorLayer::deselect( const QgsFeatureIds& featureIds ) { mSelectedFeatureIds.subtract( featureIds ); emit selectionChanged( QgsFeatureIds(), featureIds, false ); } void QgsVectorLayer::select( QgsRectangle & rect, bool addToSelection ) { selectByRect( rect, addToSelection ? AddToSelection : SetSelection ); } void QgsVectorLayer::selectByRect( QgsRectangle& rect, QgsVectorLayer::SelectBehaviour behaviour ) { // normalize the rectangle rect.normalize(); QgsFeatureIds newSelection; QgsFeatureIterator features = getFeatures( QgsFeatureRequest() .setFilterRect( rect ) .setFlags( QgsFeatureRequest::ExactIntersect | QgsFeatureRequest::NoGeometry ) .setSubsetOfAttributes( QgsAttributeList() ) ); QgsFeature feat; while ( features.nextFeature( feat ) ) { newSelection << feat.id(); } features.close(); selectByIds( newSelection, behaviour ); } void QgsVectorLayer::selectByExpression( const QString& expression, QgsVectorLayer::SelectBehaviour behaviour ) { QgsFeatureIds newSelection; QgsExpressionContext context; context << QgsExpressionContextUtils::globalScope() << QgsExpressionContextUtils::projectScope() << QgsExpressionContextUtils::layerScope( this ); if ( behaviour == SetSelection || behaviour == AddToSelection ) { QgsFeatureRequest request = QgsFeatureRequest().setFilterExpression( expression ) .setExpressionContext( context ) .setFlags( QgsFeatureRequest::NoGeometry ) .setSubsetOfAttributes( QgsAttributeList() ); QgsFeatureIterator features = getFeatures( request ); if ( behaviour == AddToSelection ) { newSelection = selectedFeaturesIds(); } QgsFeature feat; while ( features.nextFeature( feat ) ) { newSelection << feat.id(); } features.close(); } else if ( behaviour == IntersectSelection || behaviour == RemoveFromSelection ) { QgsExpression exp( expression ); exp.prepare( &context ); QgsFeatureIds oldSelection = selectedFeaturesIds(); QgsFeatureRequest request = QgsFeatureRequest().setFilterFids( oldSelection ); //refine request if ( !exp.needsGeometry() ) request.setFlags( QgsFeatureRequest::NoGeometry ); request.setSubsetOfAttributes( exp.referencedColumns(), fields() ); QgsFeatureIterator features = getFeatures( request ); QgsFeature feat; while ( features.nextFeature( feat ) ) { context.setFeature( feat ); bool matches = exp.evaluate( &context ).toBool(); if ( matches && behaviour == IntersectSelection ) { newSelection << feat.id(); } else if ( !matches && behaviour == RemoveFromSelection ) { newSelection << feat.id(); } } } selectByIds( newSelection ); } void QgsVectorLayer::selectByIds( const QgsFeatureIds& ids, QgsVectorLayer::SelectBehaviour behaviour ) { QgsFeatureIds newSelection; switch ( behaviour ) { case SetSelection: newSelection = ids; break; case AddToSelection: newSelection = mSelectedFeatureIds + ids; break; case RemoveFromSelection: newSelection = mSelectedFeatureIds - ids; break; case IntersectSelection: newSelection = mSelectedFeatureIds.intersect( ids ); break; } QgsFeatureIds deselectedFeatures = mSelectedFeatureIds - newSelection; mSelectedFeatureIds = newSelection; emit selectionChanged( newSelection, deselectedFeatures, true ); } void QgsVectorLayer::modifySelection( QgsFeatureIds selectIds, QgsFeatureIds deselectIds ) { QgsFeatureIds intersectingIds = selectIds & deselectIds; if ( !intersectingIds.isEmpty() ) { QgsDebugMsg( "Trying to select and deselect the same item at the same time. Unsure what to do. Selecting dubious items." ); } mSelectedFeatureIds -= deselectIds; mSelectedFeatureIds += selectIds; emit selectionChanged( selectIds, deselectIds - intersectingIds, false ); } void QgsVectorLayer::invertSelection() { QgsFeatureIds ids = allFeatureIds(); ids.subtract( mSelectedFeatureIds ); selectByIds( ids ); } void QgsVectorLayer::selectAll() { selectByIds( allFeatureIds() ); } QgsFeatureIds QgsVectorLayer::allFeatureIds() { QgsFeatureIterator fit = getFeatures( QgsFeatureRequest() .setFlags( QgsFeatureRequest::NoGeometry ) .setSubsetOfAttributes( QgsAttributeList() ) ); QgsFeatureIds ids; QgsFeature fet; while ( fit.nextFeature( fet ) ) { ids << fet.id(); } return ids; } void QgsVectorLayer::invertSelectionInRectangle( QgsRectangle & rect ) { // normalize the rectangle rect.normalize(); QgsFeatureIterator fit = getFeatures( QgsFeatureRequest() .setFilterRect( rect ) .setFlags( QgsFeatureRequest::NoGeometry | QgsFeatureRequest::ExactIntersect ) .setSubsetOfAttributes( QgsAttributeList() ) ); QgsFeatureIds selectIds; QgsFeatureIds deselectIds; QgsFeature fet; while ( fit.nextFeature( fet ) ) { if ( mSelectedFeatureIds.contains( fet.id() ) ) { deselectIds << fet.id(); } else { selectIds << fet.id(); } } modifySelection( selectIds, deselectIds ); } void QgsVectorLayer::removeSelection() { if ( mSelectedFeatureIds.isEmpty() ) return; selectByIds( QgsFeatureIds() ); } QgsVectorDataProvider* QgsVectorLayer::dataProvider() { return mDataProvider; } const QgsVectorDataProvider* QgsVectorLayer::dataProvider() const { return mDataProvider; } void QgsVectorLayer::setProviderEncoding( const QString& encoding ) { if ( mValid && mDataProvider && mDataProvider->encoding() != encoding ) { mDataProvider->setEncoding( encoding ); updateFields(); } } void QgsVectorLayer::setDiagramRenderer( QgsDiagramRendererV2* r ) { delete mDiagramRenderer; mDiagramRenderer = r; emit rendererChanged(); } QGis::GeometryType QgsVectorLayer::geometryType() const { if ( mValid && mDataProvider ) { QGis::WkbType type = mDataProvider->geometryType(); return static_cast< QGis::GeometryType >( QgsWKBTypes::geometryType( static_cast< QgsWKBTypes::Type >( type ) ) ); } else { QgsDebugMsg( "invalid layer or pointer to mDataProvider is null" ); } // We shouldn't get here, and if we have, other things are likely to // go wrong. Code that uses the type() return value should be // rewritten to cope with a value of QGis::Unknown. To make this // need known, the following message is printed every time we get // here. QgsDebugMsg( "WARNING: This code should never be reached. Problems may occur..." ); return QGis::UnknownGeometry; } bool QgsVectorLayer::hasGeometryType() const { QGis::GeometryType t = geometryType(); return t != QGis::NoGeometry && t != QGis::UnknownGeometry; } QGis::WkbType QgsVectorLayer::wkbType() const { return mWkbType; } QgsRectangle QgsVectorLayer::boundingBoxOfSelected() { if ( !mValid || mSelectedFeatureIds.isEmpty() ) //no selected features { return QgsRectangle( 0, 0, 0, 0 ); } QgsRectangle r, retval; retval.setMinimal(); QgsFeature fet; if ( mDataProvider->capabilities() & QgsVectorDataProvider::SelectAtId ) { QgsFeatureIterator fit = getFeatures( QgsFeatureRequest() .setFilterFids( mSelectedFeatureIds ) .setSubsetOfAttributes( QgsAttributeList() ) ); while ( fit.nextFeature( fet ) ) { if ( !fet.constGeometry() || fet.constGeometry()->isEmpty() ) continue; r = fet.constGeometry()->boundingBox(); retval.combineExtentWith( r ); } } else { QgsFeatureIterator fit = getFeatures( QgsFeatureRequest() .setSubsetOfAttributes( QgsAttributeList() ) ); while ( fit.nextFeature( fet ) ) { if ( mSelectedFeatureIds.contains( fet.id() ) ) { if ( fet.constGeometry() ) { r = fet.constGeometry()->boundingBox(); retval.combineExtentWith( r ); } } } } if ( retval.width() == 0.0 || retval.height() == 0.0 ) { // If all of the features are at the one point, buffer the // rectangle a bit. If they are all at zero, do something a bit // more crude. if ( retval.xMinimum() == 0.0 && retval.xMaximum() == 0.0 && retval.yMinimum() == 0.0 && retval.yMaximum() == 0.0 ) { retval.set( -1.0, -1.0, 1.0, 1.0 ); } } return retval; } bool QgsVectorLayer::labelsEnabled() const { if ( !mLabeling ) return false; // for simple labeling the mode can be "no labels" - so we need to check // in properties whether we are really enabled or not if ( mLabeling->type() == "simple" ) return customProperty( "labeling/enabled", QVariant( false ) ).toBool(); // for other labeling implementations we always assume that labeling is enabled return true; } bool QgsVectorLayer::diagramsEnabled() const { if ( !mDiagramRenderer || !mDiagramLayerSettings ) return false; QList settingList = mDiagramRenderer->diagramSettings(); if ( !settingList.isEmpty() ) { return settingList.at( 0 ).enabled; } return false; } long QgsVectorLayer::featureCount( QgsSymbolV2* symbol ) { if ( !mSymbolFeatureCounted ) return -1; return mSymbolFeatureCountMap.value( symbol ); } /** Used by QgsVectorLayer::countSymbolFeatures() to provide an interruption checker * @note not available in Python bindings */ class QgsVectorLayerInterruptionCheckerDuringCountSymbolFeatures: public QgsInterruptionChecker { public: /** Constructor */ explicit QgsVectorLayerInterruptionCheckerDuringCountSymbolFeatures( QProgressDialog* dialog ) : mDialog( dialog ) { } virtual bool mustStop() const { if ( mDialog->isVisible() ) { // So that we get a chance of hitting the Abort button #ifdef Q_OS_LINUX // For some reason on Windows hasPendingEvents() always return true, // but one iteration is actually enough on Windows to get good interactivity // whereas on Linux we must allow for far more iterations. // For safety limit the number of iterations int nIters = 0; while ( QCoreApplication::hasPendingEvents() && ++nIters < 100 ) #endif { QCoreApplication::processEvents(); } return mDialog->wasCanceled(); } return false; } private: QProgressDialog* mDialog; }; bool QgsVectorLayer::countSymbolFeatures( bool showProgress ) { if ( mSymbolFeatureCounted ) return true; mSymbolFeatureCountMap.clear(); if ( !mValid ) { QgsDebugMsg( "invoked with invalid layer" ); return false; } if ( !mDataProvider ) { QgsDebugMsg( "invoked with null mDataProvider" ); return false; } if ( !mRendererV2 ) { QgsDebugMsg( "invoked with null mRendererV2" ); return false; } QgsLegendSymbolList symbolList = mRendererV2->legendSymbolItems(); QgsLegendSymbolList::const_iterator symbolIt = symbolList.constBegin(); for ( ; symbolIt != symbolList.constEnd(); ++symbolIt ) { mSymbolFeatureCountMap.insert( symbolIt->second, 0 ); } long nFeatures = featureCount(); QWidget* mainWindow = nullptr; Q_FOREACH ( QWidget* widget, qApp->topLevelWidgets() ) { if ( widget->objectName() == "QgisApp" ) { mainWindow = widget; break; } } QProgressDialog progressDialog( tr( "Updating feature count for layer %1" ).arg( name() ), tr( "Abort" ), 0, nFeatures, mainWindow ); progressDialog.setWindowTitle( tr( "QGIS" ) ); progressDialog.setWindowModality( Qt::WindowModal ); if ( showProgress ) { // Properly initialize to 0 as recommended in doc so that the evaluation // of the total time properly works progressDialog.setValue( 0 ); } int featuresCounted = 0; QgsFeatureIterator fit = getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ) ); QgsVectorLayerInterruptionCheckerDuringCountSymbolFeatures interruptionCheck( &progressDialog ); if ( showProgress ) { fit.setInterruptionChecker( &interruptionCheck ); } // Renderer (rule based) may depend on context scale, with scale is ignored if 0 QgsRenderContext renderContext; renderContext.setRendererScale( 0 ); renderContext.expressionContext() << QgsExpressionContextUtils::globalScope() << QgsExpressionContextUtils::projectScope() << QgsExpressionContextUtils::layerScope( this ); mRendererV2->startRender( renderContext, fields() ); QgsFeature f; QTime time; time.start(); while ( fit.nextFeature( f ) ) { renderContext.expressionContext().setFeature( f ); QgsSymbolV2List featureSymbolList = mRendererV2->originalSymbolsForFeature( f, renderContext ); for ( QgsSymbolV2List::iterator symbolIt = featureSymbolList.begin(); symbolIt != featureSymbolList.end(); ++symbolIt ) { mSymbolFeatureCountMap[*symbolIt] += 1; } ++featuresCounted; if ( showProgress ) { // Refresh progress every 50 features or second if (( featuresCounted % 50 == 0 ) || time.elapsed() > 1000 ) { time.restart(); if ( featuresCounted > nFeatures ) //sometimes the feature count is not correct { progressDialog.setMaximum( 0 ); } progressDialog.setValue( featuresCounted ); } // So that we get a chance of hitting the Abort button #ifdef Q_OS_LINUX // For some reason on Windows hasPendingEvents() always return true, // but one iteration is actually enough on Windows to get good interactivity // whereas on Linux we must allow for far more iterations. // For safety limit the number of iterations int nIters = 0; while ( QCoreApplication::hasPendingEvents() && ++nIters < 100 ) #endif { QCoreApplication::processEvents(); } if ( progressDialog.wasCanceled() ) { mSymbolFeatureCountMap.clear(); mRendererV2->stopRender( renderContext ); return false; } } } mRendererV2->stopRender( renderContext ); progressDialog.setValue( nFeatures ); mSymbolFeatureCounted = true; return true; } void QgsVectorLayer::updateExtents() { mValidExtent = false; } void QgsVectorLayer::setExtent( const QgsRectangle &r ) { QgsMapLayer::setExtent( r ); mValidExtent = true; } QgsRectangle QgsVectorLayer::extent() { QgsRectangle rect; rect.setMinimal(); if ( !hasGeometryType() ) return rect; if ( !mValidExtent && mLazyExtent && mDataProvider ) { // get the extent QgsRectangle mbr = mDataProvider->extent(); // show the extent QgsDebugMsg( "Extent of layer: " + mbr.toString() ); // store the extent setExtent( mbr ); mLazyExtent = false; } if ( mValidExtent ) return QgsMapLayer::extent(); if ( !mValid || !mDataProvider ) { QgsDebugMsg( "invoked with invalid layer or null mDataProvider" ); return rect; } if ( !mEditBuffer || ( mEditBuffer->mDeletedFeatureIds.isEmpty() && mEditBuffer->mChangedGeometries.isEmpty() ) || QgsDataSourceURI( mDataProvider->dataSourceUri() ).useEstimatedMetadata() ) { mDataProvider->updateExtents(); // get the extent of the layer from the provider // but only when there are some features already if ( mDataProvider->featureCount() != 0 ) { QgsRectangle r = mDataProvider->extent(); rect.combineExtentWith( r ); } if ( mEditBuffer ) { for ( QgsFeatureMap::const_iterator it = mEditBuffer->mAddedFeatures.constBegin(); it != mEditBuffer->mAddedFeatures.constEnd(); ++it ) { if ( it->constGeometry() ) { QgsRectangle r = it->constGeometry()->boundingBox(); rect.combineExtentWith( r ); } } } } else { QgsFeatureIterator fit = getFeatures( QgsFeatureRequest() .setSubsetOfAttributes( QgsAttributeList() ) ); QgsFeature fet; while ( fit.nextFeature( fet ) ) { if ( fet.constGeometry() && fet.constGeometry()->type() != QGis::UnknownGeometry ) { QgsRectangle bb = fet.constGeometry()->boundingBox(); rect.combineExtentWith( bb ); } } } if ( rect.xMinimum() > rect.xMaximum() && rect.yMinimum() > rect.yMaximum() ) { // special case when there are no features in provider nor any added rect = QgsRectangle(); // use rectangle with zero coordinates } setExtent( rect ); // Send this (hopefully) up the chain to the map canvas emit recalculateExtents(); return rect; } QString QgsVectorLayer::subsetString() { if ( !mValid || !mDataProvider ) { QgsDebugMsg( "invoked with invalid layer or null mDataProvider" ); return nullptr; } return mDataProvider->subsetString(); } bool QgsVectorLayer::setSubsetString( const QString& subset ) { if ( !mValid || !mDataProvider ) { QgsDebugMsg( "invoked with invalid layer or null mDataProvider" ); return false; } bool res = mDataProvider->setSubsetString( subset ); // get the updated data source string from the provider mDataSource = mDataProvider->dataSourceUri(); updateExtents(); updateFields(); if ( res ) emit repaintRequested(); return res; } bool QgsVectorLayer::simplifyDrawingCanbeApplied( const QgsRenderContext& renderContext, QgsVectorSimplifyMethod::SimplifyHint simplifyHint ) const { if ( mValid && mDataProvider && !mEditBuffer && ( hasGeometryType() && geometryType() != QGis::Point ) && ( mSimplifyMethod.simplifyHints() & simplifyHint ) && renderContext.useRenderingOptimization() ) { double maximumSimplificationScale = mSimplifyMethod.maximumScale(); // check maximum scale at which generalisation should be carried out if ( maximumSimplificationScale > 1 && renderContext.rendererScale() <= maximumSimplificationScale ) return false; return true; } return false; } QgsConditionalLayerStyles* QgsVectorLayer::conditionalStyles() const { return mConditionalStyles; } QgsFeatureIterator QgsVectorLayer::getFeatures( const QgsFeatureRequest& request ) { if ( !mValid || !mDataProvider ) return QgsFeatureIterator(); return QgsFeatureIterator( new QgsVectorLayerFeatureIterator( new QgsVectorLayerFeatureSource( this ), true, request ) ); } bool QgsVectorLayer::addFeature( QgsFeature& feature, bool alsoUpdateExtent ) { Q_UNUSED( alsoUpdateExtent ); // TODO[MD] if ( !mValid || !mEditBuffer || !mDataProvider ) return false; bool success = mEditBuffer->addFeature( feature ); if ( success ) updateExtents(); return success; } bool QgsVectorLayer::updateFeature( QgsFeature &f ) { QgsFeatureRequest req; req.setFilterFid( f.id() ); if ( !f.constGeometry() ) req.setFlags( QgsFeatureRequest::NoGeometry ); if ( f.attributes().isEmpty() ) req.setSubsetOfAttributes( QgsAttributeList() ); QgsFeature current; if ( !getFeatures( req ).nextFeature( current ) ) { QgsDebugMsg( QString( "feature %1 could not be retrieved" ).arg( f.id() ) ); return false; } if ( f.constGeometry() && current.constGeometry() && f.constGeometry() != current.constGeometry() && !f.constGeometry()->isGeosEqual( *current.constGeometry() ) ) { if ( !changeGeometry( f.id(), f.geometry() ) ) { QgsDebugMsg( QString( "geometry of feature %1 could not be changed." ).arg( f.id() ) ); return false; } } QgsAttributes fa = f.attributes(); QgsAttributes ca = current.attributes(); for ( int attr = 0; attr < fa.count(); ++attr ) { if ( fa.at( attr ) != ca.at( attr ) ) { if ( !changeAttributeValue( f.id(), attr, fa.at( attr ), ca.at( attr ) ) ) { QgsDebugMsg( QString( "attribute %1 of feature %2 could not be changed." ).arg( attr ).arg( f.id() ) ); return false; } } } return true; } bool QgsVectorLayer::insertVertex( double x, double y, QgsFeatureId atFeatureId, int beforeVertex ) { if ( !mValid || !mEditBuffer || !mDataProvider ) return false; QgsVectorLayerEditUtils utils( this ); bool result = utils.insertVertex( x, y, atFeatureId, beforeVertex ); if ( result ) updateExtents(); return result; } bool QgsVectorLayer::moveVertex( double x, double y, QgsFeatureId atFeatureId, int atVertex ) { if ( !mValid || !mEditBuffer || !mDataProvider ) return false; QgsVectorLayerEditUtils utils( this ); bool result = utils.moveVertex( x, y, atFeatureId, atVertex ); if ( result ) updateExtents(); return result; } bool QgsVectorLayer::moveVertex( const QgsPointV2& p, QgsFeatureId atFeatureId, int atVertex ) { if ( !mValid || !mEditBuffer || !mDataProvider ) return false; QgsVectorLayerEditUtils utils( this ); bool result = utils.moveVertex( p, atFeatureId, atVertex ); if ( result ) updateExtents(); return result; } bool QgsVectorLayer::deleteVertex( QgsFeatureId atFeatureId, int atVertex ) { QgsVectorLayer::EditResult res = deleteVertexV2( atFeatureId, atVertex ); bool result = ( res == QgsVectorLayer::Success || res == QgsVectorLayer::EmptyGeometry ); if ( result ) updateExtents(); return result; } QgsVectorLayer::EditResult QgsVectorLayer::deleteVertexV2( QgsFeatureId featureId, int vertex ) { if ( !mValid || !mEditBuffer || !mDataProvider ) return QgsVectorLayer::InvalidLayer; QgsVectorLayerEditUtils utils( this ); EditResult result = utils.deleteVertexV2( featureId, vertex ); if ( result == Success ) updateExtents(); return result; } bool QgsVectorLayer::deleteSelectedFeatures( int* deletedCount ) { if ( !mValid || !mDataProvider || !( mDataProvider->capabilities() & QgsVectorDataProvider::DeleteFeatures ) ) { return false; } if ( !isEditable() ) { return false; } int deleted = 0; int count = mSelectedFeatureIds.size(); // Make a copy since deleteFeature modifies mSelectedFeatureIds QgsFeatureIds selectedFeatures( mSelectedFeatureIds ); Q_FOREACH ( QgsFeatureId fid, selectedFeatures ) { deleted += deleteFeature( fid ); // removes from selection } triggerRepaint(); updateExtents(); if ( deletedCount ) { *deletedCount = deleted; } return deleted == count; } int QgsVectorLayer::addRing( const QList& ring, QgsFeatureId* featureId ) { if ( !mValid || !mEditBuffer || !mDataProvider ) return 6; QgsVectorLayerEditUtils utils( this ); int result = 5; //first try with selected features if ( !mSelectedFeatureIds.isEmpty() ) { result = utils.addRing( ring, mSelectedFeatureIds, featureId ); } if ( result != 0 ) { //try with all intersecting features result = utils.addRing( ring, QgsFeatureIds(), featureId ); } return result; } int QgsVectorLayer::addRing( QgsCurveV2* ring, QgsFeatureId* featureId ) { if ( !mValid || !mEditBuffer || !mDataProvider ) { delete ring; return 6; } if ( !ring ) { return 1; } if ( !ring->isClosed() ) { delete ring; return 2; } QgsVectorLayerEditUtils utils( this ); int result = 5; //first try with selected features if ( !mSelectedFeatureIds.isEmpty() ) { result = utils.addRing( static_cast< QgsCurveV2* >( ring->clone() ), mSelectedFeatureIds, featureId ); } if ( result != 0 ) { //try with all intersecting features result = utils.addRing( static_cast< QgsCurveV2* >( ring->clone() ), QgsFeatureIds(), featureId ); } delete ring; return result; } int QgsVectorLayer::addPart( const QList &points ) { if ( !mValid || !mEditBuffer || !mDataProvider ) return 7; //number of selected features must be 1 if ( mSelectedFeatureIds.size() < 1 ) { QgsDebugMsg( "Number of selected features <1" ); return 4; } else if ( mSelectedFeatureIds.size() > 1 ) { QgsDebugMsg( "Number of selected features >1" ); return 5; } QgsVectorLayerEditUtils utils( this ); int result = utils.addPart( points, *mSelectedFeatureIds.constBegin() ); if ( result == 0 ) updateExtents(); return result; } int QgsVectorLayer::addPart( const QgsPointSequenceV2 &points ) { if ( !mValid || !mEditBuffer || !mDataProvider ) return 7; //number of selected features must be 1 if ( mSelectedFeatureIds.size() < 1 ) { QgsDebugMsg( "Number of selected features <1" ); return 4; } else if ( mSelectedFeatureIds.size() > 1 ) { QgsDebugMsg( "Number of selected features >1" ); return 5; } QgsVectorLayerEditUtils utils( this ); int result = utils.addPart( points, *mSelectedFeatureIds.constBegin() ); if ( result == 0 ) updateExtents(); return result; } int QgsVectorLayer::addPart( QgsCurveV2* ring ) { if ( !mValid || !mEditBuffer || !mDataProvider ) return 7; //number of selected features must be 1 if ( mSelectedFeatureIds.size() < 1 ) { QgsDebugMsg( "Number of selected features <1" ); return 4; } else if ( mSelectedFeatureIds.size() > 1 ) { QgsDebugMsg( "Number of selected features >1" ); return 5; } QgsVectorLayerEditUtils utils( this ); int result = utils.addPart( ring, *mSelectedFeatureIds.constBegin() ); if ( result == 0 ) updateExtents(); return result; } int QgsVectorLayer::translateFeature( QgsFeatureId featureId, double dx, double dy ) { if ( !mValid || !mEditBuffer || !mDataProvider ) return -1; QgsVectorLayerEditUtils utils( this ); int result = utils.translateFeature( featureId, dx, dy ); if ( result == 0 ) updateExtents(); return result; } int QgsVectorLayer::splitParts( const QList& splitLine, bool topologicalEditing ) { if ( !mValid || !mEditBuffer || !mDataProvider ) return -1; QgsVectorLayerEditUtils utils( this ); return utils.splitParts( splitLine, topologicalEditing ); } int QgsVectorLayer::splitFeatures( const QList& splitLine, bool topologicalEditing ) { if ( !mValid || !mEditBuffer || !mDataProvider ) return -1; QgsVectorLayerEditUtils utils( this ); return utils.splitFeatures( splitLine, topologicalEditing ); } int QgsVectorLayer::removePolygonIntersections( QgsGeometry* geom, const QgsFeatureIds& ignoreFeatures ) { if ( !hasGeometryType() ) return 1; int returnValue = 0; //first test if geom really has type polygon or multipolygon if ( geom->type() != QGis::Polygon ) { return 1; } //get bounding box of geom QgsRectangle geomBBox = geom->boundingBox(); //get list of features that intersect this bounding box QgsFeatureIterator fit = getFeatures( QgsFeatureRequest() .setFilterRect( geomBBox ) .setFlags( QgsFeatureRequest::ExactIntersect ) .setSubsetOfAttributes( QgsAttributeList() ) ); QgsFeature f; while ( fit.nextFeature( f ) ) { if ( ignoreFeatures.contains( f.id() ) ) { continue; } //call geometry->makeDifference for each feature const QgsGeometry *currentGeom = f.constGeometry(); if ( currentGeom ) { if ( geom->makeDifference( currentGeom ) != 0 ) { returnValue = 2; } } } return returnValue; } int QgsVectorLayer::addTopologicalPoints( const QgsGeometry *geom ) { if ( !mValid || !mEditBuffer || !mDataProvider ) return -1; QgsVectorLayerEditUtils utils( this ); return utils.addTopologicalPoints( geom ); } int QgsVectorLayer::addTopologicalPoints( const QgsPoint& p ) { if ( !mValid || !mEditBuffer || !mDataProvider ) return -1; QgsVectorLayerEditUtils utils( this ); return utils.addTopologicalPoints( p ); } QgsLabel *QgsVectorLayer::label() { return mLabel; } const QgsLabel *QgsVectorLayer::label() const { return mLabel; } void QgsVectorLayer::enableLabels( bool on ) { mLabelOn = on; } bool QgsVectorLayer::hasLabelsEnabled() const { return mLabelOn; } void QgsVectorLayer::setLabeling( QgsAbstractVectorLayerLabeling* labeling ) { if ( mLabeling == labeling ) return; delete mLabeling; mLabeling = labeling; } bool QgsVectorLayer::startEditing() { if ( !mValid || !mDataProvider ) { return false; } // allow editing if provider supports any of the capabilities if ( !( mDataProvider->capabilities() & QgsVectorDataProvider::EditingCapabilities ) ) { return false; } if ( mReadOnly ) { return false; } if ( mEditBuffer ) { // editing already underway return false; } emit beforeEditingStarted(); mDataProvider->enterUpdateMode(); if ( mDataProvider->transaction() ) { mEditBuffer = new QgsVectorLayerEditPassthrough( this ); } else { mEditBuffer = new QgsVectorLayerEditBuffer( this ); } // forward signals connect( mEditBuffer, SIGNAL( layerModified() ), this, SLOT( invalidateSymbolCountedFlag() ) ); connect( mEditBuffer, SIGNAL( layerModified() ), this, SIGNAL( layerModified() ) ); // TODO[MD]: necessary? //connect( mEditBuffer, SIGNAL( layerModified() ), this, SLOT( triggerRepaint() ) ); // TODO[MD]: works well? connect( mEditBuffer, SIGNAL( featureAdded( QgsFeatureId ) ), this, SIGNAL( featureAdded( QgsFeatureId ) ) ); connect( mEditBuffer, SIGNAL( featureDeleted( QgsFeatureId ) ), this, SLOT( onFeatureDeleted( QgsFeatureId ) ) ); connect( mEditBuffer, SIGNAL( geometryChanged( QgsFeatureId, QgsGeometry& ) ), this, SIGNAL( geometryChanged( QgsFeatureId, QgsGeometry& ) ) ); connect( mEditBuffer, SIGNAL( attributeValueChanged( QgsFeatureId, int, QVariant ) ), this, SIGNAL( attributeValueChanged( QgsFeatureId, int, QVariant ) ) ); connect( mEditBuffer, SIGNAL( attributeAdded( int ) ), this, SIGNAL( attributeAdded( int ) ) ); connect( mEditBuffer, SIGNAL( attributeDeleted( int ) ), this, SIGNAL( attributeDeleted( int ) ) ); connect( mEditBuffer, SIGNAL( committedAttributesDeleted( const QString &, const QgsAttributeList & ) ), this, SIGNAL( committedAttributesDeleted( const QString &, const QgsAttributeList & ) ) ); connect( mEditBuffer, SIGNAL( committedAttributesAdded( const QString &, const QList & ) ), this, SIGNAL( committedAttributesAdded( const QString &, const QList & ) ) ); connect( mEditBuffer, SIGNAL( committedFeaturesAdded( QString, QgsFeatureList ) ), this, SIGNAL( committedFeaturesAdded( QString, QgsFeatureList ) ) ); connect( mEditBuffer, SIGNAL( committedFeaturesRemoved( QString, QgsFeatureIds ) ), this, SIGNAL( committedFeaturesRemoved( QString, QgsFeatureIds ) ) ); connect( mEditBuffer, SIGNAL( committedAttributeValuesChanges( const QString &, const QgsChangedAttributesMap & ) ), this, SIGNAL( committedAttributeValuesChanges( const QString &, const QgsChangedAttributesMap & ) ) ); connect( mEditBuffer, SIGNAL( committedGeometriesChanges( const QString &, const QgsGeometryMap & ) ), this, SIGNAL( committedGeometriesChanges( const QString &, const QgsGeometryMap & ) ) ); updateFields(); emit editingStarted(); return true; } bool QgsVectorLayer::readXml( const QDomNode& layer_node ) { QgsDebugMsg( QString( "Datasource in QgsVectorLayer::readXml: " ) + mDataSource.toLocal8Bit().data() ); //process provider key QDomNode pkeyNode = layer_node.namedItem( "provider" ); if ( pkeyNode.isNull() ) { mProviderKey = ""; } else { QDomElement pkeyElt = pkeyNode.toElement(); mProviderKey = pkeyElt.text(); } // determine type of vector layer if ( !mProviderKey.isNull() ) { // if the provider string isn't empty, then we successfully // got the stored provider } else if ( mDataSource.contains( "dbname=" ) ) { mProviderKey = "postgres"; } else { mProviderKey = "ogr"; } if ( !setDataProvider( mProviderKey ) ) { return false; } QDomElement mapLayerNode = layer_node.toElement(); if ( mapLayerNode.attribute( "readOnly", "0" ).toInt() == 1 ) mReadOnly = true; QDomElement pkeyElem = pkeyNode.toElement(); if ( !pkeyElem.isNull() ) { QString encodingString = pkeyElem.attribute( "encoding" ); if ( !encodingString.isEmpty() ) { mDataProvider->setEncoding( encodingString ); } } //load vector joins if ( !mJoinBuffer ) { mJoinBuffer = new QgsVectorLayerJoinBuffer( this ); connect( mJoinBuffer, SIGNAL( joinedFieldsChanged() ), this, SLOT( onJoinedFieldsChanged() ) ); } mJoinBuffer->readXml( layer_node ); if ( !mExpressionFieldBuffer ) mExpressionFieldBuffer = new QgsExpressionFieldBuffer(); mExpressionFieldBuffer->readXml( layer_node ); updateFields(); connect( QgsMapLayerRegistry::instance(), SIGNAL( layerWillBeRemoved( QString ) ), this, SLOT( checkJoinLayerRemove( QString ) ) ); QDomNode prevExpNode = layer_node.namedItem( "previewExpression" ); if ( prevExpNode.isNull() ) { mDisplayExpression = ""; } else { QDomElement prevExpElem = prevExpNode.toElement(); mDisplayExpression = prevExpElem.text(); } QString errorMsg; if ( !readSymbology( layer_node, errorMsg ) ) { return false; } readStyleManager( layer_node ); setLegend( QgsMapLayerLegend::defaultVectorLegend( this ) ); return mValid; // should be true if read successfully } // void QgsVectorLayer::readXml void QgsVectorLayer::setDataSource( const QString& dataSource, const QString& baseName, const QString& provider, bool loadDefaultStyleFlag ) { QGis::GeometryType oldGeomType = geometryType(); mDataSource = dataSource; mLayerName = capitaliseLayerName( baseName ); setLayerName( mLayerName ); setDataProvider( provider ); if ( !mValid ) return; // Always set crs setCoordinateSystem(); // reset style if loading default style, style is missing, or geometry type has changed if ( !rendererV2() || !legend() || oldGeomType != geometryType() || loadDefaultStyleFlag ) { // check if there is a default style / propertysheet defined // for this layer and if so apply it bool defaultLoadedFlag = false; if ( loadDefaultStyleFlag ) { loadDefaultStyle( defaultLoadedFlag ); } // if the default style failed to load or was disabled use some very basic defaults if ( !defaultLoadedFlag && hasGeometryType() ) { // add single symbol renderer setRendererV2( QgsFeatureRendererV2::defaultRenderer( geometryType() ) ); } setLegend( QgsMapLayerLegend::defaultVectorLegend( this ) ); } connect( QgsMapLayerRegistry::instance(), SIGNAL( layerWillBeRemoved( QString ) ), this, SLOT( checkJoinLayerRemove( QString ) ) ); emit repaintRequested(); } bool QgsVectorLayer::setDataProvider( QString const & provider ) { mProviderKey = provider; // XXX is this necessary? Usually already set // XXX when execution gets here. //XXX - This was a dynamic cast but that kills the Windows // version big-time with an abnormal termination error delete mDataProvider; mDataProvider = ( QgsVectorDataProvider* )( QgsProviderRegistry::instance()->provider( provider, mDataSource ) ); connect( mDataProvider, SIGNAL( raiseError( QString ) ), this, SIGNAL( raiseError( QString ) ) ); if ( !mDataProvider ) { QgsDebugMsg( " unable to get data provider" ); return false; } QgsDebugMsg( "Instantiated the data provider plugin" ); mValid = mDataProvider->isValid(); if ( !mValid ) { QgsDebugMsg( "Invalid provider plugin " + QString( mDataSource.toUtf8() ) ); return false; } // TODO: Check if the provider has the capability to send fullExtentCalculated connect( mDataProvider, SIGNAL( fullExtentCalculated() ), this, SLOT( updateExtents() ) ); // get and store the feature type mWkbType = mDataProvider->geometryType(); mJoinBuffer = new QgsVectorLayerJoinBuffer( this ); connect( mJoinBuffer, SIGNAL( joinedFieldsChanged() ), this, SLOT( onJoinedFieldsChanged() ) ); mExpressionFieldBuffer = new QgsExpressionFieldBuffer(); updateFields(); // look at the fields in the layer and set the primary // display field using some real fuzzy logic setDisplayField(); if ( mProviderKey == "postgres" ) { QgsDebugMsg( "Beautifying layer name " + name() ); // adjust the display name for postgres layers QRegExp reg( "\"[^\"]+\"\\.\"([^\"]+)\"( \\([^)]+\\))?" ); if ( reg.indexIn( name() ) >= 0 ) { QStringList stuff = reg.capturedTexts(); QString lName = stuff[1]; const QMap &layers = QgsMapLayerRegistry::instance()->mapLayers(); QMap::const_iterator it; for ( it = layers.constBegin(); it != layers.constEnd() && ( *it )->name() != lName; ++it ) ; if ( it != layers.constEnd() && stuff.size() > 2 ) { lName += '.' + stuff[2].mid( 2, stuff[2].length() - 3 ); } if ( !lName.isEmpty() ) setLayerName( lName ); } QgsDebugMsg( "Beautified layer name " + name() ); // deal with unnecessary schema qualification to make v.in.ogr happy mDataSource = mDataProvider->dataSourceUri(); } else if ( mProviderKey == "osm" ) { // make sure that the "observer" has been removed from URI to avoid crashes mDataSource = mDataProvider->dataSourceUri(); } else if ( provider == "ogr" ) { // make sure that the /vsigzip or /vsizip is added to uri, if applicable mDataSource = mDataProvider->dataSourceUri(); if ( mDataSource.right( 10 ) == "|layerid=0" ) mDataSource.chop( 10 ); } // label mLabel = new QgsLabel( mDataProvider->fields() ); mLabelOn = false; connect( mDataProvider, SIGNAL( dataChanged() ), this, SIGNAL( dataChanged() ) ); connect( mDataProvider, SIGNAL( dataChanged() ), this, SLOT( removeSelection() ) ); return true; } // QgsVectorLayer:: setDataProvider /* virtual */ bool QgsVectorLayer::writeXml( QDomNode & layer_node, QDomDocument & document ) { // first get the layer element so that we can append the type attribute QDomElement mapLayerNode = layer_node.toElement(); if ( mapLayerNode.isNull() || ( "maplayer" != mapLayerNode.nodeName() ) ) { QgsDebugMsg( "can't find " ); return false; } mapLayerNode.setAttribute( "type", "vector" ); // set the geometry type mapLayerNode.setAttribute( "geometry", QGis::vectorGeometryType( geometryType() ) ); // add provider node if ( mDataProvider ) { QDomElement provider = document.createElement( "provider" ); provider.setAttribute( "encoding", mDataProvider->encoding() ); QDomText providerText = document.createTextNode( providerType() ); provider.appendChild( providerText ); layer_node.appendChild( provider ); } // save readonly state mapLayerNode.setAttribute( "readOnly", mReadOnly ); // save preview expression QDomElement prevExpElem = document.createElement( "previewExpression" ); QDomText prevExpText = document.createTextNode( mDisplayExpression ); prevExpElem.appendChild( prevExpText ); layer_node.appendChild( prevExpElem ); //save joins mJoinBuffer->writeXml( layer_node, document ); // dependencies QDomElement dependenciesElement = document.createElement( "layerDependencies" ); Q_FOREACH ( QString layerId, layerDependencies() ) { QDomElement depElem = document.createElement( "layer" ); depElem.setAttribute( "id", layerId ); dependenciesElement.appendChild( depElem ); } layer_node.appendChild( dependenciesElement ); // save expression fields mExpressionFieldBuffer->writeXml( layer_node, document ); writeStyleManager( layer_node, document ); // renderer specific settings QString errorMsg; return writeSymbology( layer_node, document, errorMsg ); } // bool QgsVectorLayer::writeXml bool QgsVectorLayer::readSymbology( const QDomNode& node, QString& errorMessage ) { readStyle( node, errorMessage ); // process the attribute actions mActions->readXML( node ); mEditFormConfig->readXml( node ); QDomNode annotationFormNode = node.namedItem( "annotationform" ); if ( !annotationFormNode.isNull() ) { QDomElement e = annotationFormNode.toElement(); mAnnotationForm = QgsProject::instance()->readPath( e.text() ); } mAttributeAliasMap.clear(); QDomNode aliasesNode = node.namedItem( "aliases" ); if ( !aliasesNode.isNull() ) { QDomElement aliasElem; QDomNodeList aliasNodeList = aliasesNode.toElement().elementsByTagName( "alias" ); for ( int i = 0; i < aliasNodeList.size(); ++i ) { aliasElem = aliasNodeList.at( i ).toElement(); QString field; if ( aliasElem.hasAttribute( "field" ) ) { field = aliasElem.attribute( "field" ); } else { int index = aliasElem.attribute( "index" ).toInt(); if ( index >= 0 && index < fields().count() ) field = fields().at( index ).name(); } mAttributeAliasMap.insert( field, aliasElem.attribute( "name" ) ); } } //Attributes excluded from WMS and WFS mExcludeAttributesWMS.clear(); QDomNode excludeWMSNode = node.namedItem( "excludeAttributesWMS" ); if ( !excludeWMSNode.isNull() ) { QDomNodeList attributeNodeList = excludeWMSNode.toElement().elementsByTagName( "attribute" ); for ( int i = 0; i < attributeNodeList.size(); ++i ) { mExcludeAttributesWMS.insert( attributeNodeList.at( i ).toElement().text() ); } } mExcludeAttributesWFS.clear(); QDomNode excludeWFSNode = node.namedItem( "excludeAttributesWFS" ); if ( !excludeWFSNode.isNull() ) { QDomNodeList attributeNodeList = excludeWFSNode.toElement().elementsByTagName( "attribute" ); for ( int i = 0; i < attributeNodeList.size(); ++i ) { mExcludeAttributesWFS.insert( attributeNodeList.at( i ).toElement().text() ); } } mEditFormConfig->readXml( node ); mAttributeTableConfig.readXml( node ); mConditionalStyles->readXml( node ); return true; } bool QgsVectorLayer::readStyle( const QDomNode &node, QString &errorMessage ) { emit readCustomSymbology( node.toElement(), errorMessage ); if ( hasGeometryType() ) { // try renderer v2 first QDomElement rendererElement = node.firstChildElement( RENDERER_TAG_NAME ); if ( !rendererElement.isNull() ) { QgsFeatureRendererV2* r = QgsFeatureRendererV2::load( rendererElement ); if ( !r ) return false; setRendererV2( r ); } else { QgsFeatureRendererV2* r = QgsSymbologyV2Conversion::readOldRenderer( node, geometryType() ); if ( !r ) r = QgsFeatureRendererV2::defaultRenderer( geometryType() ); setRendererV2( r ); } QDomElement labelingElement = node.firstChildElement( "labeling" ); if ( !labelingElement.isNull() ) { QgsAbstractVectorLayerLabeling* l = QgsAbstractVectorLayerLabeling::create( labelingElement ); setLabeling( l ? l : new QgsVectorLayerSimpleLabeling ); } // get and set the display field if it exists. QDomNode displayFieldNode = node.namedItem( "displayfield" ); if ( !displayFieldNode.isNull() ) { QDomElement e = displayFieldNode.toElement(); setDisplayField( e.text() ); } // get and set the blend mode if it exists QDomNode blendModeNode = node.namedItem( "blendMode" ); if ( !blendModeNode.isNull() ) { QDomElement e = blendModeNode.toElement(); setBlendMode( QgsMapRenderer::getCompositionMode( static_cast< QgsMapRenderer::BlendMode >( e.text().toInt() ) ) ); } // get and set the feature blend mode if it exists QDomNode featureBlendModeNode = node.namedItem( "featureBlendMode" ); if ( !featureBlendModeNode.isNull() ) { QDomElement e = featureBlendModeNode.toElement(); setFeatureBlendMode( QgsMapRenderer::getCompositionMode( static_cast< QgsMapRenderer::BlendMode >( e.text().toInt() ) ) ); } // get and set the layer transparency if it exists QDomNode layerTransparencyNode = node.namedItem( "layerTransparency" ); if ( !layerTransparencyNode.isNull() ) { QDomElement e = layerTransparencyNode.toElement(); setLayerTransparency( e.text().toInt() ); } // use scale dependent visibility flag QDomElement e = node.toElement(); if ( mLabel ) { mLabel->setScaleBasedVisibility( e.attribute( "scaleBasedLabelVisibilityFlag", "0" ) == "1" ); mLabel->setMinScale( e.attribute( "minLabelScale", "1" ).toFloat() ); mLabel->setMaxScale( e.attribute( "maxLabelScale", "100000000" ).toFloat() ); } // get the simplification drawing settings mSimplifyMethod.setSimplifyHints( static_cast< QgsVectorSimplifyMethod::SimplifyHints >( e.attribute( "simplifyDrawingHints", "1" ).toInt() ) ); mSimplifyMethod.setSimplifyAlgorithm( static_cast< QgsVectorSimplifyMethod::SimplifyAlgorithm >( e.attribute( "simplifyAlgorithm", "0" ).toInt() ) ); mSimplifyMethod.setThreshold( e.attribute( "simplifyDrawingTol", "1" ).toFloat() ); mSimplifyMethod.setForceLocalOptimization( e.attribute( "simplifyLocal", "1" ).toInt() ); mSimplifyMethod.setMaximumScale( e.attribute( "simplifyMaxScale", "1" ).toFloat() ); //also restore custom properties (for labeling-ng) readCustomProperties( node, "labeling" ); // Test if labeling is on or off QDomNode labelnode = node.namedItem( "label" ); QDomElement element = labelnode.toElement(); int hasLabelsEnabled = element.text().toInt(); Q_NOWARN_DEPRECATED_PUSH if ( hasLabelsEnabled < 1 ) { enableLabels( false ); } else { enableLabels( true ); } Q_NOWARN_DEPRECATED_POP QDomNode labelattributesnode = node.namedItem( "labelattributes" ); if ( !labelattributesnode.isNull() && mLabel ) { QgsDebugMsg( "calling readXML" ); mLabel->readXML( labelattributesnode ); } //diagram renderer and diagram layer settings delete mDiagramRenderer; mDiagramRenderer = nullptr; QDomElement singleCatDiagramElem = node.firstChildElement( "SingleCategoryDiagramRenderer" ); if ( !singleCatDiagramElem.isNull() ) { mDiagramRenderer = new QgsSingleCategoryDiagramRenderer(); mDiagramRenderer->readXML( singleCatDiagramElem, this ); } QDomElement linearDiagramElem = node.firstChildElement( "LinearlyInterpolatedDiagramRenderer" ); if ( !linearDiagramElem.isNull() ) { mDiagramRenderer = new QgsLinearlyInterpolatedDiagramRenderer(); mDiagramRenderer->readXML( linearDiagramElem, this ); } if ( mDiagramRenderer ) { QDomElement diagramSettingsElem = node.firstChildElement( "DiagramLayerSettings" ); if ( !diagramSettingsElem.isNull() ) { delete mDiagramLayerSettings; mDiagramLayerSettings = new QgsDiagramLayerSettings(); mDiagramLayerSettings->readXML( diagramSettingsElem, this ); } } } return true; } bool QgsVectorLayer::writeSymbology( QDomNode& node, QDomDocument& doc, QString& errorMessage ) const { ( void )writeStyle( node, doc, errorMessage ); // FIXME // edittypes are written to the layerNode // by slot QgsEditorWidgetRegistry::writeMapLayer() // triggered by signal QgsProject::writeMapLayer() // still other editing settings are written here, // although they are not part of symbology either QDomElement afField = doc.createElement( "annotationform" ); QDomText afText = doc.createTextNode( QgsProject::instance()->writePath( mAnnotationForm ) ); afField.appendChild( afText ); node.appendChild( afField ); //attribute aliases if ( !mAttributeAliasMap.isEmpty() ) { QDomElement aliasElem = doc.createElement( "aliases" ); QMap::const_iterator a_it = mAttributeAliasMap.constBegin(); for ( ; a_it != mAttributeAliasMap.constEnd(); ++a_it ) { int idx = fieldNameIndex( a_it.key() ); if ( idx < 0 ) continue; QDomElement aliasEntryElem = doc.createElement( "alias" ); aliasEntryElem.setAttribute( "field", a_it.key() ); aliasEntryElem.setAttribute( "index", idx ); aliasEntryElem.setAttribute( "name", a_it.value() ); aliasElem.appendChild( aliasEntryElem ); } node.appendChild( aliasElem ); } //exclude attributes WMS QDomElement excludeWMSElem = doc.createElement( "excludeAttributesWMS" ); QSet::const_iterator attWMSIt = mExcludeAttributesWMS.constBegin(); for ( ; attWMSIt != mExcludeAttributesWMS.constEnd(); ++attWMSIt ) { QDomElement attrElem = doc.createElement( "attribute" ); QDomText attrText = doc.createTextNode( *attWMSIt ); attrElem.appendChild( attrText ); excludeWMSElem.appendChild( attrElem ); } node.appendChild( excludeWMSElem ); //exclude attributes WFS QDomElement excludeWFSElem = doc.createElement( "excludeAttributesWFS" ); QSet::const_iterator attWFSIt = mExcludeAttributesWFS.constBegin(); for ( ; attWFSIt != mExcludeAttributesWFS.constEnd(); ++attWFSIt ) { QDomElement attrElem = doc.createElement( "attribute" ); QDomText attrText = doc.createTextNode( *attWFSIt ); attrElem.appendChild( attrText ); excludeWFSElem.appendChild( attrElem ); } node.appendChild( excludeWFSElem ); // add attribute actions mActions->writeXML( node, doc ); mAttributeTableConfig.writeXml( node ); mEditFormConfig->writeXml( node ); mConditionalStyles->writeXml( node, doc ); return true; } bool QgsVectorLayer::writeStyle( QDomNode &node, QDomDocument &doc, QString &errorMessage ) const { QDomElement mapLayerNode = node.toElement(); emit writeCustomSymbology( mapLayerNode, doc, errorMessage ); if ( hasGeometryType() ) { QDomElement rendererElement = mRendererV2->save( doc ); node.appendChild( rendererElement ); if ( mLabeling ) { QDomElement labelingElement = mLabeling->save( doc ); node.appendChild( labelingElement ); } // use scale dependent visibility flag if ( mLabel ) { mapLayerNode.setAttribute( "scaleBasedLabelVisibilityFlag", mLabel->scaleBasedVisibility() ? 1 : 0 ); mapLayerNode.setAttribute( "minLabelScale", QString::number( mLabel->minScale() ) ); mapLayerNode.setAttribute( "maxLabelScale", QString::number( mLabel->maxScale() ) ); } // save the simplification drawing settings mapLayerNode.setAttribute( "simplifyDrawingHints", QString::number( mSimplifyMethod.simplifyHints() ) ); mapLayerNode.setAttribute( "simplifyAlgorithm", QString::number( mSimplifyMethod.simplifyAlgorithm() ) ); mapLayerNode.setAttribute( "simplifyDrawingTol", QString::number( mSimplifyMethod.threshold() ) ); mapLayerNode.setAttribute( "simplifyLocal", mSimplifyMethod.forceLocalOptimization() ? 1 : 0 ); mapLayerNode.setAttribute( "simplifyMaxScale", QString::number( mSimplifyMethod.maximumScale() ) ); //save customproperties (for labeling ng) writeCustomProperties( node, doc ); // add the blend mode field QDomElement blendModeElem = doc.createElement( "blendMode" ); QDomText blendModeText = doc.createTextNode( QString::number( QgsMapRenderer::getBlendModeEnum( blendMode() ) ) ); blendModeElem.appendChild( blendModeText ); node.appendChild( blendModeElem ); // add the feature blend mode field QDomElement featureBlendModeElem = doc.createElement( "featureBlendMode" ); QDomText featureBlendModeText = doc.createTextNode( QString::number( QgsMapRenderer::getBlendModeEnum( featureBlendMode() ) ) ); featureBlendModeElem.appendChild( featureBlendModeText ); node.appendChild( featureBlendModeElem ); // add the layer transparency QDomElement layerTransparencyElem = doc.createElement( "layerTransparency" ); QDomText layerTransparencyText = doc.createTextNode( QString::number( layerTransparency() ) ); layerTransparencyElem.appendChild( layerTransparencyText ); node.appendChild( layerTransparencyElem ); // add the display field QDomElement dField = doc.createElement( "displayfield" ); QDomText dFieldText = doc.createTextNode( displayField() ); dField.appendChild( dFieldText ); node.appendChild( dField ); // add label node QDomElement labelElem = doc.createElement( "label" ); QDomText labelText = doc.createTextNode( "" ); Q_NOWARN_DEPRECATED_PUSH if ( hasLabelsEnabled() ) { labelText.setData( "1" ); } else { labelText.setData( "0" ); } Q_NOWARN_DEPRECATED_POP labelElem.appendChild( labelText ); node.appendChild( labelElem ); // Now we get to do all that all over again for QgsLabel if ( mLabel ) { QString fieldname = mLabel->labelField( QgsLabel::Text ); if ( fieldname != "" ) { dField = doc.createElement( "labelfield" ); dFieldText = doc.createTextNode( fieldname ); dField.appendChild( dFieldText ); node.appendChild( dField ); } mLabel->writeXML( node, doc ); } if ( mDiagramRenderer ) { mDiagramRenderer->writeXML( mapLayerNode, doc, this ); if ( mDiagramLayerSettings ) mDiagramLayerSettings->writeXML( mapLayerNode, doc, this ); } } return true; } bool QgsVectorLayer::readSld( const QDomNode& node, QString& errorMessage ) { // get the Name element QDomElement nameElem = node.firstChildElement( "Name" ); if ( nameElem.isNull() ) { errorMessage = "Warning: Name element not found within NamedLayer while it's required."; } if ( hasGeometryType() ) { QgsFeatureRendererV2* r = QgsFeatureRendererV2::loadSld( node, geometryType(), errorMessage ); if ( !r ) return false; setRendererV2( r ); // labeling readSldLabeling( node ); } return true; } bool QgsVectorLayer::writeSld( QDomNode& node, QDomDocument& doc, QString& errorMessage ) const { Q_UNUSED( errorMessage ); // store the Name element QDomElement nameNode = doc.createElement( "se:Name" ); nameNode.appendChild( doc.createTextNode( name() ) ); node.appendChild( nameNode ); if ( hasGeometryType() ) { node.appendChild( mRendererV2->writeSld( doc, name() ) ); } return true; } bool QgsVectorLayer::changeGeometry( QgsFeatureId fid, QgsGeometry* geom ) { if ( !mEditBuffer || !mDataProvider ) { return false; } updateExtents(); bool result = mEditBuffer->changeGeometry( fid, geom ); if ( result ) updateExtents(); return result; } bool QgsVectorLayer::changeAttributeValue( QgsFeatureId fid, int field, const QVariant& value, bool emitSignal ) { Q_UNUSED( emitSignal ); return changeAttributeValue( fid, field, value ); } bool QgsVectorLayer::changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue ) { if ( !mEditBuffer || !mDataProvider ) return false; return mEditBuffer->changeAttributeValue( fid, field, newValue, oldValue ); } bool QgsVectorLayer::addAttribute( const QgsField &field ) { if ( !mEditBuffer || !mDataProvider ) return false; return mEditBuffer->addAttribute( field ); } void QgsVectorLayer::remAttributeAlias( int attIndex ) { if ( attIndex < 0 || attIndex >= fields().count() ) return; QString name = fields().at( attIndex ).name(); if ( mAttributeAliasMap.contains( name ) ) { mAttributeAliasMap.remove( name ); emit layerModified(); } } bool QgsVectorLayer::renameAttribute( int attIndex, const QString& newName ) { if ( !mEditBuffer || !mDataProvider ) return false; return mEditBuffer->renameAttribute( attIndex, newName ); } void QgsVectorLayer::addAttributeAlias( int attIndex, const QString& aliasString ) { if ( attIndex < 0 || attIndex >= fields().count() ) return; QString name = fields().at( attIndex ).name(); mAttributeAliasMap.insert( name, aliasString ); emit layerModified(); // TODO[MD]: should have a different signal? } QString QgsVectorLayer::attributeAlias( int attributeIndex ) const { if ( attributeIndex < 0 || attributeIndex >= fields().count() ) return QString(); QString name = fields().at( attributeIndex ).name(); return mAttributeAliasMap.value( name, QString() ); } QString QgsVectorLayer::attributeDisplayName( int attributeIndex ) const { QString displayName = attributeAlias( attributeIndex ); if ( displayName.isEmpty() ) { if ( attributeIndex >= 0 && attributeIndex < mUpdatedFields.count() ) { displayName = mUpdatedFields.at( attributeIndex ).name(); } } return displayName; } bool QgsVectorLayer::deleteAttribute( int index ) { if ( index < 0 || index >= fields().count() ) return false; if ( mUpdatedFields.fieldOrigin( index ) == QgsFields::OriginExpression ) { removeExpressionField( index ); return true; } if ( !mEditBuffer || !mDataProvider ) return false; return mEditBuffer->deleteAttribute( index ); } bool QgsVectorLayer::deleteAttributes( QList attrs ) { bool deleted = false; // Remove multiple occurrences of same attribute attrs = attrs.toSet().toList(); qSort( attrs.begin(), attrs.end(), qGreater() ); Q_FOREACH ( int attr, attrs ) { if ( deleteAttribute( attr ) ) { deleted = true; } } return deleted; } bool QgsVectorLayer::deleteFeature( QgsFeatureId fid ) { if ( !mEditBuffer ) return false; bool res = mEditBuffer->deleteFeature( fid ); if ( res ) { mSelectedFeatureIds.remove( fid ); // remove it from selection updateExtents(); } return res; } bool QgsVectorLayer::deleteFeatures( const QgsFeatureIds& fids ) { if ( !mEditBuffer ) return false; bool res = mEditBuffer->deleteFeatures( fids ); if ( res ) { mSelectedFeatureIds.subtract( fids ); // remove it from selection updateExtents(); } return res; } QgsAttributeList QgsVectorLayer::pkAttributeList() const { QgsAttributeList pkAttributesList; QgsAttributeList providerIndexes = mDataProvider->pkAttributeIndexes(); for ( int i = 0; i < mUpdatedFields.count(); ++i ) { if ( mUpdatedFields.fieldOrigin( i ) == QgsFields::OriginProvider && providerIndexes.contains( mUpdatedFields.fieldOriginIndex( i ) ) ) pkAttributesList << i; } return pkAttributesList; } long QgsVectorLayer::featureCount() const { return mDataProvider->featureCount() + ( mEditBuffer ? mEditBuffer->mAddedFeatures.size() - mEditBuffer->mDeletedFeatureIds.size() : 0 ); } bool QgsVectorLayer::commitChanges() { mCommitErrors.clear(); if ( !mDataProvider ) { mCommitErrors << tr( "ERROR: no provider" ); return false; } if ( !mEditBuffer ) { mCommitErrors << tr( "ERROR: layer not editable" ); return false; } emit beforeCommitChanges(); bool success = mEditBuffer->commitChanges( mCommitErrors ); if ( success ) { delete mEditBuffer; mEditBuffer = nullptr; undoStack()->clear(); emit editingStopped(); } else { QgsMessageLog::logMessage( tr( "Commit errors:\n %1" ).arg( mCommitErrors.join( "\n " ) ) ); } if ( mCache ) { mCache->deleteCachedGeometries(); } updateFields(); mDataProvider->updateExtents(); mDataProvider->leaveUpdateMode(); emit repaintRequested(); return success; } const QStringList &QgsVectorLayer::commitErrors() { return mCommitErrors; } bool QgsVectorLayer::rollBack( bool deleteBuffer ) { if ( !mEditBuffer ) { return false; } bool rollbackExtent = !mEditBuffer->mDeletedFeatureIds.isEmpty() || !mEditBuffer->mAddedFeatures.isEmpty() || !mEditBuffer->mChangedGeometries.isEmpty(); emit beforeRollBack(); mEditBuffer->rollBack(); if ( isModified() ) { // new undo stack roll back method // old method of calling every undo could cause many canvas refreshes undoStack()->setIndex( 0 ); } updateFields(); if ( deleteBuffer ) { delete mEditBuffer; mEditBuffer = nullptr; undoStack()->clear(); } emit editingStopped(); if ( mCache ) { mCache->deleteCachedGeometries(); } if ( rollbackExtent ) updateExtents(); mDataProvider->leaveUpdateMode(); emit repaintRequested(); return true; } void QgsVectorLayer::setSelectedFeatures( const QgsFeatureIds& ids ) { selectByIds( ids, SetSelection ); } int QgsVectorLayer::selectedFeatureCount() { return mSelectedFeatureIds.size(); } const QgsFeatureIds& QgsVectorLayer::selectedFeaturesIds() const { return mSelectedFeatureIds; } QgsFeatureList QgsVectorLayer::selectedFeatures() { QgsFeatureList features; QgsFeature f; if ( mSelectedFeatureIds.count() <= 8 ) { // for small amount of selected features, fetch them directly // because request with FilterFids would go iterate over the whole layer Q_FOREACH ( QgsFeatureId fid, mSelectedFeatureIds ) { getFeatures( QgsFeatureRequest( fid ) ).nextFeature( f ); features << f; } } else { QgsFeatureIterator it = selectedFeaturesIterator(); while ( it.nextFeature( f ) ) { features.push_back( f ); } } return features; } QgsFeatureIterator QgsVectorLayer::selectedFeaturesIterator( QgsFeatureRequest request ) { if ( mSelectedFeatureIds.isEmpty() ) return QgsFeatureIterator(); if ( geometryType() == QGis::NoGeometry ) request.setFlags( QgsFeatureRequest::NoGeometry ); if ( mSelectedFeatureIds.count() == 1 ) request.setFilterFid( *mSelectedFeatureIds.constBegin() ); else request.setFilterFids( mSelectedFeatureIds ); return getFeatures( request ); } bool QgsVectorLayer::addFeatures( QgsFeatureList features, bool makeSelected ) { if ( !mEditBuffer || !mDataProvider ) return false; bool res = mEditBuffer->addFeatures( features ); if ( makeSelected ) { QgsFeatureIds ids; for ( QgsFeatureList::iterator iter = features.begin(); iter != features.end(); ++iter ) ids << iter->id(); selectByIds( ids ); } updateExtents(); return res; } bool QgsVectorLayer::snapPoint( QgsPoint& point, double tolerance ) { if ( !hasGeometryType() ) return false; QMultiMap snapResults; int result = snapWithContext( point, tolerance, snapResults, QgsSnapper::SnapToVertex ); if ( result != 0 ) { return false; } if ( snapResults.size() < 1 ) { return false; } QMultiMap::const_iterator snap_it = snapResults.constBegin(); point.setX( snap_it.value().snappedVertex.x() ); point.setY( snap_it.value().snappedVertex.y() ); return true; } int QgsVectorLayer::snapWithContext( const QgsPoint& startPoint, double snappingTolerance, QMultiMap& snappingResults, QgsSnapper::SnappingType snap_to ) { if ( !hasGeometryType() ) return 1; if ( snappingTolerance <= 0 || !mDataProvider ) { return 1; } QgsRectangle searchRect( startPoint.x() - snappingTolerance, startPoint.y() - snappingTolerance, startPoint.x() + snappingTolerance, startPoint.y() + snappingTolerance ); double sqrSnappingTolerance = snappingTolerance * snappingTolerance; int n = 0; QgsFeature f; if ( mCache->cachedGeometriesRect().contains( searchRect ) ) { QgsGeometryMap& cachedGeometries = mCache->cachedGeometries(); for ( QgsGeometryMap::iterator it = cachedGeometries.begin(); it != cachedGeometries.end() ; ++it ) { QgsGeometry* g = &( it.value() ); if ( g->boundingBox().intersects( searchRect ) ) { snapToGeometry( startPoint, it.key(), g, sqrSnappingTolerance, snappingResults, snap_to ); ++n; } } } else { // snapping outside cached area QgsFeatureIterator fit = getFeatures( QgsFeatureRequest() .setFilterRect( searchRect ) .setFlags( QgsFeatureRequest::ExactIntersect ) .setSubsetOfAttributes( QgsAttributeList() ) ); while ( fit.nextFeature( f ) ) { snapToGeometry( startPoint, f.id(), f.constGeometry(), sqrSnappingTolerance, snappingResults, snap_to ); ++n; } } return n == 0 ? 2 : 0; } void QgsVectorLayer::snapToGeometry( const QgsPoint& startPoint, QgsFeatureId featureId, const QgsGeometry* geom, double sqrSnappingTolerance, QMultiMap& snappingResults, QgsSnapper::SnappingType snap_to ) const { if ( !geom ) { return; } int atVertex, beforeVertex, afterVertex; double sqrDistVertexSnap, sqrDistSegmentSnap; QgsPoint snappedPoint; QgsSnappingResult snappingResultVertex; QgsSnappingResult snappingResultSegment; if ( snap_to == QgsSnapper::SnapToVertex || snap_to == QgsSnapper::SnapToVertexAndSegment ) { snappedPoint = geom->closestVertex( startPoint, atVertex, beforeVertex, afterVertex, sqrDistVertexSnap ); if ( sqrDistVertexSnap < sqrSnappingTolerance ) { snappingResultVertex.snappedVertex = snappedPoint; snappingResultVertex.snappedVertexNr = atVertex; snappingResultVertex.beforeVertexNr = beforeVertex; if ( beforeVertex != -1 ) // make sure the vertex is valid { snappingResultVertex.beforeVertex = geom->vertexAt( beforeVertex ); } snappingResultVertex.afterVertexNr = afterVertex; if ( afterVertex != -1 ) // make sure the vertex is valid { snappingResultVertex.afterVertex = geom->vertexAt( afterVertex ); } snappingResultVertex.snappedAtGeometry = featureId; snappingResultVertex.layer = this; snappingResults.insert( sqrt( sqrDistVertexSnap ), snappingResultVertex ); return; } } if ( snap_to == QgsSnapper::SnapToSegment || snap_to == QgsSnapper::SnapToVertexAndSegment ) // snap to segment { if ( geometryType() != QGis::Point ) // cannot snap to segment for points/multipoints { sqrDistSegmentSnap = geom->closestSegmentWithContext( startPoint, snappedPoint, afterVertex, nullptr, crs().geographicFlag() ? 1e-12 : 1e-8 ); if ( sqrDistSegmentSnap < sqrSnappingTolerance ) { snappingResultSegment.snappedVertex = snappedPoint; snappingResultSegment.snappedVertexNr = -1; snappingResultSegment.beforeVertexNr = afterVertex - 1; snappingResultSegment.afterVertexNr = afterVertex; snappingResultSegment.snappedAtGeometry = featureId; snappingResultSegment.beforeVertex = geom->vertexAt( afterVertex - 1 ); snappingResultSegment.afterVertex = geom->vertexAt( afterVertex ); snappingResultSegment.layer = this; snappingResults.insert( sqrt( sqrDistSegmentSnap ), snappingResultSegment ); } } } } int QgsVectorLayer::insertSegmentVerticesForSnap( const QList& snapResults ) { QgsVectorLayerEditUtils utils( this ); return utils.insertSegmentVerticesForSnap( snapResults ); } void QgsVectorLayer::setCoordinateSystem() { QgsDebugMsg( "----- Computing Coordinate System" ); // // Get the layers project info and set up the QgsCoordinateTransform // for this layer // if ( hasGeometryType() ) { // get CRS directly from provider setCrs( mDataProvider->crs() ); } else { setCrs( QgsCoordinateReferenceSystem( GEO_EPSG_CRS_AUTHID ) ); } } const QString QgsVectorLayer::displayField() const { return mDisplayField; } void QgsVectorLayer::setDisplayExpression( const QString &displayExpression ) { mDisplayExpression = displayExpression; } const QString QgsVectorLayer::displayExpression() { return mDisplayExpression; } bool QgsVectorLayer::isEditable() const { return ( mEditBuffer && mDataProvider ); } bool QgsVectorLayer::isSpatial() const { return geometryType() != QGis::NoGeometry; } bool QgsVectorLayer::isReadOnly() const { return mReadOnly; } bool QgsVectorLayer::setReadOnly( bool readonly ) { // exit if the layer is in editing mode if ( readonly && mEditBuffer ) return false; mReadOnly = readonly; return true; } bool QgsVectorLayer::isModified() const { emit beforeModifiedCheck(); return mEditBuffer && mEditBuffer->isModified(); } QgsVectorLayer::EditType QgsVectorLayer::editType( int idx ) { if ( idx < 0 || idx >= mUpdatedFields.count() ) return Hidden; Q_NOWARN_DEPRECATED_PUSH return QgsLegacyHelpers::convertEditType( editorWidgetV2( idx ), editorWidgetV2Config( idx ), this, mUpdatedFields[ idx ].name() ); Q_NOWARN_DEPRECATED_POP } void QgsVectorLayer::setEditType( int idx, EditType type ) { if ( idx < 0 || idx >= mUpdatedFields.count() ) return; QgsEditorWidgetConfig cfg; Q_NOWARN_DEPRECATED_PUSH const QString widgetType = QgsLegacyHelpers::convertEditType( type, cfg, this, mUpdatedFields[idx].name() ); setEditorWidgetV2( idx, widgetType ); setEditorWidgetV2Config( idx, cfg ); Q_NOWARN_DEPRECATED_POP } void QgsVectorLayer::setAnnotationForm( const QString& ui ) { mAnnotationForm = ui; } QMap< QString, QVariant > QgsVectorLayer::valueMap( int idx ) { Q_NOWARN_DEPRECATED_PUSH return editorWidgetV2Config( idx ); Q_NOWARN_DEPRECATED_POP } QgsVectorLayer::RangeData QgsVectorLayer::range( int idx ) { Q_NOWARN_DEPRECATED_PUSH const QgsEditorWidgetConfig cfg = editorWidgetV2Config( idx ); return RangeData( cfg.value( "Min" ), cfg.value( "Max" ), cfg.value( "Step" ) ); Q_NOWARN_DEPRECATED_POP } QString QgsVectorLayer::dateFormat( int idx ) { Q_NOWARN_DEPRECATED_PUSH return editorWidgetV2Config( idx ).value( "DateFormat" ).toString(); Q_NOWARN_DEPRECATED_POP } QSize QgsVectorLayer::widgetSize( int idx ) { Q_NOWARN_DEPRECATED_PUSH const QgsEditorWidgetConfig cfg = editorWidgetV2Config( idx ); return QSize( cfg.value( "Width" ).toInt(), cfg.value( "Height" ).toInt() ); Q_NOWARN_DEPRECATED_POP } void QgsVectorLayer::setRendererV2( QgsFeatureRendererV2 *r ) { if ( !hasGeometryType() ) return; if ( r != mRendererV2 ) { delete mRendererV2; mRendererV2 = r; mSymbolFeatureCounted = false; mSymbolFeatureCountMap.clear(); emit rendererChanged(); } } void QgsVectorLayer::beginEditCommand( const QString& text ) { if ( !mDataProvider ) { return; } if ( !mDataProvider->transaction() ) { undoStack()->beginMacro( text ); mEditCommandActive = true; emit editCommandStarted( text ); } } void QgsVectorLayer::endEditCommand() { if ( !mDataProvider ) { return; } if ( !mDataProvider->transaction() ) { undoStack()->endMacro(); mEditCommandActive = false; if ( !mDeletedFids.isEmpty() ) { emit featuresDeleted( mDeletedFids ); mDeletedFids.clear(); } emit editCommandEnded(); } } void QgsVectorLayer::destroyEditCommand() { if ( !mDataProvider ) { return; } if ( !mDataProvider->transaction() ) { undoStack()->endMacro(); undoStack()->undo(); mEditCommandActive = false; mDeletedFids.clear(); emit editCommandDestroyed(); } } void QgsVectorLayer::setCheckedState( int idx, const QString& checked, const QString& unchecked ) { Q_NOWARN_DEPRECATED_PUSH QgsEditorWidgetConfig cfg = editorWidgetV2Config( idx ); cfg["CheckedState"] = checked; cfg["UncheckedState"] = unchecked; setEditorWidgetV2Config( idx, cfg ); Q_NOWARN_DEPRECATED_POP } int QgsVectorLayer::fieldNameIndex( const QString& fieldName ) const { return fields().fieldNameIndex( fieldName ); } bool QgsVectorLayer::addJoin( const QgsVectorJoinInfo& joinInfo ) { return mJoinBuffer && mJoinBuffer->addJoin( joinInfo ); } void QgsVectorLayer::checkJoinLayerRemove( const QString& theLayerId ) { removeJoin( theLayerId ); } bool QgsVectorLayer::removeJoin( const QString& joinLayerId ) { bool res = false; if ( mJoinBuffer ) { res = mJoinBuffer->removeJoin( joinLayerId ); } return res; } const QList< QgsVectorJoinInfo > QgsVectorLayer::vectorJoins() const { if ( mJoinBuffer ) return mJoinBuffer->vectorJoins(); else return QList< QgsVectorJoinInfo >(); } int QgsVectorLayer::addExpressionField( const QString& exp, const QgsField& fld ) { emit beforeAddingExpressionField( fld.name() ); mExpressionFieldBuffer->addExpression( exp, fld ); updateFields(); int idx = mUpdatedFields.indexFromName( fld.name() ); emit attributeAdded( idx ); return idx; } void QgsVectorLayer::removeExpressionField( int index ) { emit beforeRemovingExpressionField( index ); int oi = mUpdatedFields.fieldOriginIndex( index ); mExpressionFieldBuffer->removeExpression( oi ); updateFields(); emit attributeDeleted( index ); } QString QgsVectorLayer::expressionField( int index ) { int oi = mUpdatedFields.fieldOriginIndex( index ); return mExpressionFieldBuffer->expressions().value( oi ).expression; } void QgsVectorLayer::updateExpressionField( int index, const QString& exp ) { int oi = mUpdatedFields.fieldOriginIndex( index ); mExpressionFieldBuffer->updateExpression( oi, exp ); } void QgsVectorLayer::updateFields() { if ( !mDataProvider ) return; QgsFields oldFields = mUpdatedFields; mUpdatedFields = mDataProvider->fields(); // added / removed fields if ( mEditBuffer ) mEditBuffer->updateFields( mUpdatedFields ); // joined fields if ( mJoinBuffer && mJoinBuffer->containsJoins() ) mJoinBuffer->updateFields( mUpdatedFields ); if ( mExpressionFieldBuffer ) mExpressionFieldBuffer->updateFields( mUpdatedFields ); if ( oldFields != mUpdatedFields ) { emit updatedFields(); mEditFormConfig->setFields( mUpdatedFields ); } } void QgsVectorLayer::createJoinCaches() { if ( mJoinBuffer->containsJoins() ) { mJoinBuffer->createJoinCaches(); } } void QgsVectorLayer::uniqueValues( int index, QList &uniqueValues, int limit ) { uniqueValues.clear(); if ( !mDataProvider ) { return; } QgsFields::FieldOrigin origin = mUpdatedFields.fieldOrigin( index ); if ( origin == QgsFields::OriginUnknown ) { return; } if ( origin == QgsFields::OriginProvider ) //a provider field { mDataProvider->uniqueValues( index, uniqueValues, limit ); if ( mEditBuffer ) { QSet vals; Q_FOREACH ( const QVariant& v, uniqueValues ) { vals << v.toString(); } QMapIterator< QgsFeatureId, QgsAttributeMap > it( mEditBuffer->changedAttributeValues() ); while ( it.hasNext() && ( limit < 0 || uniqueValues.count() < limit ) ) { it.next(); QVariant v = it.value().value( index ); if ( v.isValid() ) { QString vs = v.toString(); if ( !vals.contains( vs ) ) { vals << vs; uniqueValues << v; } } } } return; } else if ( origin == QgsFields::OriginJoin ) { int sourceLayerIndex; const QgsVectorJoinInfo* join = mJoinBuffer->joinForFieldIndex( index, mUpdatedFields, sourceLayerIndex ); Q_ASSERT( join ); QgsVectorLayer *vl = dynamic_cast( QgsMapLayerRegistry::instance()->mapLayer( join->joinLayerId ) ); if ( vl ) vl->dataProvider()->uniqueValues( sourceLayerIndex, uniqueValues, limit ); return; } else if ( origin == QgsFields::OriginEdit || origin == QgsFields::OriginExpression ) { // the layer is editable, but in certain cases it can still be avoided going through all features if ( origin == QgsFields::OriginEdit && mEditBuffer->mDeletedFeatureIds.isEmpty() && mEditBuffer->mAddedFeatures.isEmpty() && !mEditBuffer->mDeletedAttributeIds.contains( index ) && mEditBuffer->mChangedAttributeValues.isEmpty() ) { mDataProvider->uniqueValues( index, uniqueValues, limit ); return; } // we need to go through each feature QgsAttributeList attList; attList << index; QgsFeatureIterator fit = getFeatures( QgsFeatureRequest() .setFlags( QgsFeatureRequest::NoGeometry ) .setSubsetOfAttributes( attList ) ); QgsFeature f; QVariant currentValue; QHash val; while ( fit.nextFeature( f ) ) { currentValue = f.attribute( index ); val.insert( currentValue.toString(), currentValue ); if ( limit >= 0 && val.size() >= limit ) { break; } } uniqueValues = val.values(); return; } Q_ASSERT_X( false, "QgsVectorLayer::uniqueValues()", "Unknown source of the field!" ); } QVariant QgsVectorLayer::minimumValue( int index ) { if ( !mDataProvider ) { return QVariant(); } QgsFields::FieldOrigin origin = mUpdatedFields.fieldOrigin( index ); if ( origin == QgsFields::OriginUnknown ) { return QVariant(); } if ( origin == QgsFields::OriginProvider ) //a provider field { return mDataProvider->minimumValue( index ); } else if ( origin == QgsFields::OriginJoin ) { int sourceLayerIndex; const QgsVectorJoinInfo* join = mJoinBuffer->joinForFieldIndex( index, mUpdatedFields, sourceLayerIndex ); Q_ASSERT( join ); QgsVectorLayer* vl = dynamic_cast( QgsMapLayerRegistry::instance()->mapLayer( join->joinLayerId ) ); Q_ASSERT( vl ); return vl->minimumValue( sourceLayerIndex ); } else if ( origin == QgsFields::OriginEdit || origin == QgsFields::OriginExpression ) { // the layer is editable, but in certain cases it can still be avoided going through all features if ( origin == QgsFields::OriginEdit && mEditBuffer->mDeletedFeatureIds.isEmpty() && mEditBuffer->mAddedFeatures.isEmpty() && ! mEditBuffer->mDeletedAttributeIds.contains( index ) && mEditBuffer->mChangedAttributeValues.isEmpty() ) { return mDataProvider->minimumValue( index ); } // we need to go through each feature QgsAttributeList attList; attList << index; QgsFeatureIterator fit = getFeatures( QgsFeatureRequest() .setFlags( QgsFeatureRequest::NoGeometry ) .setSubsetOfAttributes( attList ) ); QgsFeature f; double minimumValue = std::numeric_limits::max(); double currentValue = 0; while ( fit.nextFeature( f ) ) { currentValue = f.attribute( index ).toDouble(); if ( currentValue < minimumValue ) { minimumValue = currentValue; } } return QVariant( minimumValue ); } Q_ASSERT_X( false, "QgsVectorLayer::minimumValue()", "Unknown source of the field!" ); return QVariant(); } QVariant QgsVectorLayer::maximumValue( int index ) { if ( !mDataProvider ) { return QVariant(); } QgsFields::FieldOrigin origin = mUpdatedFields.fieldOrigin( index ); if ( origin == QgsFields::OriginUnknown ) { return QVariant(); } if ( origin == QgsFields::OriginProvider ) //a provider field { return mDataProvider->maximumValue( index ); } else if ( origin == QgsFields::OriginJoin ) { int sourceLayerIndex; const QgsVectorJoinInfo* join = mJoinBuffer->joinForFieldIndex( index, mUpdatedFields, sourceLayerIndex ); Q_ASSERT( join ); QgsVectorLayer* vl = dynamic_cast( QgsMapLayerRegistry::instance()->mapLayer( join->joinLayerId ) ); Q_ASSERT( vl ); return vl->maximumValue( sourceLayerIndex ); } else if ( origin == QgsFields::OriginEdit || origin == QgsFields::OriginExpression ) { // the layer is editable, but in certain cases it can still be avoided going through all features if ( origin == QgsFields::OriginEdit && mEditBuffer->mDeletedFeatureIds.isEmpty() && mEditBuffer->mAddedFeatures.isEmpty() && !mEditBuffer->mDeletedAttributeIds.contains( index ) && mEditBuffer->mChangedAttributeValues.isEmpty() ) { return mDataProvider->maximumValue( index ); } // we need to go through each feature QgsAttributeList attList; attList << index; QgsFeatureIterator fit = getFeatures( QgsFeatureRequest() .setFlags( QgsFeatureRequest::NoGeometry ) .setSubsetOfAttributes( attList ) ); QgsFeature f; double maximumValue = -std::numeric_limits::max(); double currentValue = 0; while ( fit.nextFeature( f ) ) { currentValue = f.attribute( index ).toDouble(); if ( currentValue > maximumValue ) { maximumValue = currentValue; } } return QVariant( maximumValue ); } Q_ASSERT_X( false, "QgsVectorLayer::maximumValue()", "Unknown source of the field!" ); return QVariant(); } QVariant QgsVectorLayer::aggregate( QgsAggregateCalculator::Aggregate aggregate, const QString& fieldOrExpression, const QgsAggregateCalculator::AggregateParameters& parameters, QgsExpressionContext* context, bool* ok ) { if ( ok ) *ok = false; if ( !mDataProvider ) { return QVariant(); } // test if we are calculating based on a field int attrIndex = mUpdatedFields.fieldNameIndex( fieldOrExpression ); if ( attrIndex >= 0 ) { // aggregate is based on a field - if it's a provider field, we could possibly hand over the calculation // to the provider itself QgsFields::FieldOrigin origin = mUpdatedFields.fieldOrigin( attrIndex ); if ( origin == QgsFields::OriginProvider ) { bool providerOk = false; QVariant val = mDataProvider->aggregate( aggregate, attrIndex, parameters, context, providerOk ); if ( providerOk ) { // provider handled calculation if ( ok ) *ok = true; return val; } } } // fallback to using aggregate calculator to determine aggregate QgsAggregateCalculator c( this ); c.setParameters( parameters ); return c.calculate( aggregate, fieldOrExpression, context, ok ); } QList QgsVectorLayer::getValues( const QString &fieldOrExpression, bool& ok, bool selectedOnly ) { QList values; QScopedPointer expression; QgsExpressionContext context; int attrNum = fieldNameIndex( fieldOrExpression ); if ( attrNum == -1 ) { // try to use expression expression.reset( new QgsExpression( fieldOrExpression ) ); context << QgsExpressionContextUtils::globalScope() << QgsExpressionContextUtils::projectScope() << QgsExpressionContextUtils::layerScope( this ); if ( expression->hasParserError() || !expression->prepare( &context ) ) { ok = false; return values; } } QgsFeature f; QStringList lst; if ( expression.isNull() ) lst.append( fieldOrExpression ); else lst = expression->referencedColumns(); QgsFeatureRequest request = QgsFeatureRequest() .setFlags(( expression && expression->needsGeometry() ) ? QgsFeatureRequest::NoFlags : QgsFeatureRequest::NoGeometry ) .setSubsetOfAttributes( lst, fields() ); QgsFeatureIterator fit; if ( !selectedOnly ) { fit = getFeatures( request ); } else { fit = selectedFeaturesIterator( request ); } // create list of non-null attribute values while ( fit.nextFeature( f ) ) { if ( expression ) { context.setFeature( f ); QVariant v = expression->evaluate( &context ); values << v; } else { values << f.attribute( attrNum ); } } ok = true; return values; } QList QgsVectorLayer::getDoubleValues( const QString &fieldOrExpression, bool& ok, bool selectedOnly, int* nullCount ) { QList values; if ( nullCount ) *nullCount = 0; QList variantValues = getValues( fieldOrExpression, ok, selectedOnly ); if ( !ok ) return values; bool convertOk; Q_FOREACH ( const QVariant& value, variantValues ) { double val = value.toDouble( &convertOk ); if ( convertOk ) values << val; else if ( value.isNull() ) { if ( nullCount ) *nullCount += 1; } } ok = true; return values; } /** Write blend mode for features */ void QgsVectorLayer::setFeatureBlendMode( QPainter::CompositionMode featureBlendMode ) { mFeatureBlendMode = featureBlendMode; emit featureBlendModeChanged( featureBlendMode ); } /** Read blend mode for layer */ QPainter::CompositionMode QgsVectorLayer::featureBlendMode() const { return mFeatureBlendMode; } /** Write transparency for layer */ void QgsVectorLayer::setLayerTransparency( int layerTransparency ) { mLayerTransparency = layerTransparency; emit layerTransparencyChanged( layerTransparency ); } /** Read transparency for layer */ int QgsVectorLayer::layerTransparency() const { return mLayerTransparency; } void QgsVectorLayer::readSldLabeling( const QDomNode& node ) { QDomElement element = node.toElement(); if ( element.isNull() ) return; QDomElement userStyleElem = element.firstChildElement( "UserStyle" ); if ( userStyleElem.isNull() ) { QgsDebugMsg( "Info: UserStyle element not found." ); return; } QDomElement featureTypeStyleElem = userStyleElem.firstChildElement( "FeatureTypeStyle" ); if ( featureTypeStyleElem.isNull() ) { QgsDebugMsg( "Info: FeatureTypeStyle element not found." ); return; } // use last rule QDomElement ruleElem = featureTypeStyleElem.lastChildElement( "Rule" ); if ( ruleElem.isNull() ) { QgsDebugMsg( "Info: Rule element not found." ); return; } // use last text symbolizer QDomElement textSymbolizerElem = ruleElem.lastChildElement( "TextSymbolizer" ); if ( textSymbolizerElem.isNull() ) { QgsDebugMsg( "Info: TextSymbolizer element not found." ); return; } // Label setCustomProperty( "labeling/enabled", false ); QDomElement labelElem = textSymbolizerElem.firstChildElement( "Label" ); if ( !labelElem.isNull() ) { QDomElement propertyNameElem = labelElem.firstChildElement( "PropertyName" ); if ( !propertyNameElem.isNull() ) { // enable labeling setCustomProperty( "labeling", "pal" ); setCustomProperty( "labeling/enabled", true ); // set labeling defaults setCustomProperty( "labeling/fontFamily", "Sans-Serif" ); setCustomProperty( "labeling/fontItalic", false ); setCustomProperty( "labeling/fontSize", 10 ); setCustomProperty( "labeling/fontSizeInMapUnits", false ); setCustomProperty( "labeling/fontBold", false ); setCustomProperty( "labeling/fontUnderline", false ); setCustomProperty( "labeling/textColorR", 0 ); setCustomProperty( "labeling/textColorG", 0 ); setCustomProperty( "labeling/textColorB", 0 ); setCustomProperty( "labeling/textTransp", 0 ); setCustomProperty( "labeling/bufferDraw", false ); setCustomProperty( "labeling/bufferSize", 1 ); setCustomProperty( "labeling/bufferSizeInMapUnits", false ); setCustomProperty( "labeling/bufferColorR", 255 ); setCustomProperty( "labeling/bufferColorG", 255 ); setCustomProperty( "labeling/bufferColorB", 255 ); setCustomProperty( "labeling/bufferTransp", 0 ); setCustomProperty( "labeling/placement", QgsPalLayerSettings::AroundPoint ); setCustomProperty( "labeling/xOffset", 0 ); setCustomProperty( "labeling/yOffset", 0 ); setCustomProperty( "labeling/labelOffsetInMapUnits", false ); setCustomProperty( "labeling/angleOffset", 0 ); // label attribute QString labelAttribute = propertyNameElem.text(); setCustomProperty( "labeling/fieldName", labelAttribute ); setCustomProperty( "labeling/isExpression", false ); int fieldIndex = fieldNameIndex( labelAttribute ); if ( fieldIndex == -1 ) { // label attribute is not in columns, check if it is an expression QgsExpression exp( labelAttribute ); if ( !exp.hasEvalError() ) { setCustomProperty( "labeling/isExpression", true ); } else { QgsDebugMsg( "SLD label attribute error: " + exp.evalErrorString() ); } } } else { QgsDebugMsg( "Info: PropertyName element not found." ); return; } } else { QgsDebugMsg( "Info: Label element not found." ); return; } // Font QDomElement fontElem = textSymbolizerElem.firstChildElement( "Font" ); if ( !fontElem.isNull() ) { QString cssName; QString elemText; QDomElement cssElem = fontElem.firstChildElement( "CssParameter" ); while ( !cssElem.isNull() ) { cssName = cssElem.attribute( "name", "not_found" ); if ( cssName != "not_found" ) { elemText = cssElem.text(); if ( cssName == "font-family" ) { setCustomProperty( "labeling/fontFamily", elemText ); } else if ( cssName == "font-style" ) { setCustomProperty( "labeling/fontItalic", ( elemText == "italic" ) || ( elemText == "Italic" ) ); } else if ( cssName == "font-size" ) { bool ok; int fontSize = elemText.toInt( &ok ); if ( ok ) { setCustomProperty( "labeling/fontSize", fontSize ); } } else if ( cssName == "font-weight" ) { setCustomProperty( "labeling/fontBold", ( elemText == "bold" ) || ( elemText == "Bold" ) ); } else if ( cssName == "font-underline" ) { setCustomProperty( "labeling/fontUnderline", ( elemText == "underline" ) || ( elemText == "Underline" ) ); } } cssElem = cssElem.nextSiblingElement( "CssParameter" ); } } // Fill QColor textColor = QgsOgcUtils::colorFromOgcFill( textSymbolizerElem.firstChildElement( "Fill" ) ); if ( textColor.isValid() ) { setCustomProperty( "labeling/textColorR", textColor.red() ); setCustomProperty( "labeling/textColorG", textColor.green() ); setCustomProperty( "labeling/textColorB", textColor.blue() ); setCustomProperty( "labeling/textTransp", 100 - static_cast< int >( 100 * textColor.alphaF() ) ); } // Halo QDomElement haloElem = textSymbolizerElem.firstChildElement( "Halo" ); if ( !haloElem.isNull() ) { setCustomProperty( "labeling/bufferDraw", true ); setCustomProperty( "labeling/bufferSize", 1 ); QDomElement radiusElem = haloElem.firstChildElement( "Radius" ); if ( !radiusElem.isNull() ) { bool ok; double bufferSize = radiusElem.text().toDouble( &ok ); if ( ok ) { setCustomProperty( "labeling/bufferSize", bufferSize ); } } QColor bufferColor = QgsOgcUtils::colorFromOgcFill( haloElem.firstChildElement( "Fill" ) ); if ( bufferColor.isValid() ) { setCustomProperty( "labeling/bufferColorR", bufferColor.red() ); setCustomProperty( "labeling/bufferColorG", bufferColor.green() ); setCustomProperty( "labeling/bufferColorB", bufferColor.blue() ); setCustomProperty( "labeling/bufferTransp", 100 - static_cast< int >( 100 * bufferColor.alphaF() ) ); } } // LabelPlacement QDomElement labelPlacementElem = textSymbolizerElem.firstChildElement( "LabelPlacement" ); if ( !labelPlacementElem.isNull() ) { // PointPlacement QDomElement pointPlacementElem = labelPlacementElem.firstChildElement( "PointPlacement" ); if ( !pointPlacementElem.isNull() ) { setCustomProperty( "labeling/placement", QgsPalLayerSettings::OverPoint ); QDomElement displacementElem = pointPlacementElem.firstChildElement( "Displacement" ); if ( !displacementElem.isNull() ) { QDomElement displacementXElem = displacementElem.firstChildElement( "DisplacementX" ); if ( !displacementXElem.isNull() ) { bool ok; double xOffset = displacementXElem.text().toDouble( &ok ); if ( ok ) { setCustomProperty( "labeling/xOffset", xOffset ); } } QDomElement displacementYElem = displacementElem.firstChildElement( "DisplacementY" ); if ( !displacementYElem.isNull() ) { bool ok; double yOffset = displacementYElem.text().toDouble( &ok ); if ( ok ) { setCustomProperty( "labeling/yOffset", yOffset ); } } } QDomElement rotationElem = pointPlacementElem.firstChildElement( "Rotation" ); if ( !rotationElem.isNull() ) { bool ok; double rotation = rotationElem.text().toDouble( &ok ); if ( ok ) { setCustomProperty( "labeling/angleOffset", rotation ); } } } } } QgsAttributeTableConfig QgsVectorLayer::attributeTableConfig() const { return mAttributeTableConfig; } void QgsVectorLayer::setAttributeTableConfig( const QgsAttributeTableConfig& attributeTableConfig ) { mAttributeTableConfig = attributeTableConfig; } void QgsVectorLayer::setDiagramLayerSettings( const QgsDiagramLayerSettings& s ) { if ( !mDiagramLayerSettings ) mDiagramLayerSettings = new QgsDiagramLayerSettings(); *mDiagramLayerSettings = s; } QString QgsVectorLayer::metadata() { QString myMetadata = ""; //------------- myMetadata += "

"; myMetadata += tr( "General" ); myMetadata += "

\n"; // data comment if ( !( dataComment().isEmpty() ) ) { myMetadata += "

" + tr( "Layer comment" ) + "

\n"; myMetadata += "

"; myMetadata += dataComment(); myMetadata += "

\n"; } //storage type myMetadata += "

" + tr( "Storage type of this layer" ) + "

\n"; myMetadata += "

"; myMetadata += storageType(); myMetadata += "

\n"; if ( dataProvider() ) { //provider description myMetadata += "

" + tr( "Description of this provider" ) + "

\n"; myMetadata += "

"; myMetadata += dataProvider()->description().replace( '\n', "
" ); myMetadata += "

\n"; } // data source myMetadata += "

" + tr( "Source for this layer" ) + "

\n"; myMetadata += "

"; myMetadata += publicSource(); myMetadata += "

\n"; //geom type QGis::GeometryType type = geometryType(); if ( type < 0 || type > QGis::NoGeometry ) { QgsDebugMsg( "Invalid vector type" ); } else { QString typeString( QGis::vectorGeometryType( geometryType() ) ); myMetadata += "

" + tr( "Geometry type of the features in this layer" ) + "

\n"; myMetadata += "

"; myMetadata += typeString; myMetadata += "

\n"; } QgsAttributeList pkAttrList = pkAttributeList(); if ( !pkAttrList.isEmpty() ) { myMetadata += "

" + tr( "Primary key attributes" ) + "

\n"; myMetadata += "

"; Q_FOREACH ( int idx, pkAttrList ) { myMetadata += fields().at( idx ).name() + ' '; } myMetadata += "

\n"; } //feature count myMetadata += "

" + tr( "The number of features in this layer" ) + "

\n"; myMetadata += "

"; myMetadata += QString::number( featureCount() ); myMetadata += "

\n"; //capabilities myMetadata += "

" + tr( "Capabilities of this layer" ) + "

\n"; myMetadata += "

"; myMetadata += capabilitiesString(); myMetadata += "

\n"; //------------- QgsRectangle myExtent = extent(); myMetadata += "

"; myMetadata += tr( "Extents" ); myMetadata += "

\n"; //extents in layer cs TODO...maybe make a little nested table to improve layout... myMetadata += "

" + tr( "In layer spatial reference system units" ) + "

\n"; myMetadata += "

"; // Try to be a bit clever over what number format we use for the // extents. Some people don't like it using scientific notation when the // numbers get large, but for small numbers this is the more practical // option (so we can't force the format to 'f' for all values). // The scheme: // - for all numbers with more than 5 digits, force non-scientific notation // and 2 digits after the decimal point. // - for all smaller numbers let the OS decide which format to use (it will // generally use non-scientific unless the number gets much less than 1). if ( !myExtent.isEmpty() ) { QString xMin, yMin, xMax, yMax; double changeoverValue = 99999; // The 'largest' 5 digit number if ( qAbs( myExtent.xMinimum() ) > changeoverValue ) { xMin = QString( "%1" ).arg( myExtent.xMinimum(), 0, 'f', 2 ); } else { xMin = QString( "%1" ).arg( myExtent.xMinimum() ); } if ( qAbs( myExtent.yMinimum() ) > changeoverValue ) { yMin = QString( "%1" ).arg( myExtent.yMinimum(), 0, 'f', 2 ); } else { yMin = QString( "%1" ).arg( myExtent.yMinimum() ); } if ( qAbs( myExtent.xMaximum() ) > changeoverValue ) { xMax = QString( "%1" ).arg( myExtent.xMaximum(), 0, 'f', 2 ); } else { xMax = QString( "%1" ).arg( myExtent.xMaximum() ); } if ( qAbs( myExtent.yMaximum() ) > changeoverValue ) { yMax = QString( "%1" ).arg( myExtent.yMaximum(), 0, 'f', 2 ); } else { yMax = QString( "%1" ).arg( myExtent.yMaximum() ); } myMetadata += tr( "xMin,yMin %1,%2 : xMax,yMax %3,%4" ) .arg( xMin, yMin, xMax, yMax ); } else { myMetadata += tr( "unknown extent" ); } myMetadata += "

\n"; //extents in project cs try { #if 0 // TODO: currently disabled, will revisit later [MD] QgsRectangle myProjectedExtent = coordinateTransform->transformBoundingBox( extent() ); myMetadata += "

" + tr( "In project spatial reference system units" ) + "

\n"; myMetadata += "

"; myMetadata += tr( "xMin,yMin %1,%2 : xMax,yMax %3,%4" ) .arg( myProjectedExtent.xMinimum() ) .arg( myProjectedExtent.yMinimum() ) .arg( myProjectedExtent.xMaximum() ) .arg( myProjectedExtent.yMaximum() ); myMetadata += "

\n"; #endif // // Display layer spatial ref system // myMetadata += "

" + tr( "Layer Spatial Reference System" ) + "

\n"; myMetadata += "

"; myMetadata += crs().toProj4().replace( '"', " \"" ); myMetadata += "

\n"; // // Display project (output) spatial ref system // #if 0 // TODO: disabled for now, will revisit later [MD] //myMetadata += ""; myMetadata += "

" + tr( "Project (Output) Spatial Reference System" ) + "

\n"; myMetadata += "

"; myMetadata += coordinateTransform->destCRS().toProj4().replace( '"', " \"" ); myMetadata += "

\n"; #endif } catch ( QgsCsException &cse ) { Q_UNUSED( cse ); QgsDebugMsg( cse.what() ); myMetadata += "

" + tr( "In project spatial reference system units" ) + "

\n"; myMetadata += "

"; myMetadata += tr( "(Invalid transformation of layer extents)" ); myMetadata += "

\n"; } #if 0 // // Add the info about each field in the attribute table // myMetadata += "

" + tr( "Attribute field info" ) + "

\n"; myMetadata += "

"; // Start a nested table in this trow myMetadata += ""; myMetadata += ""; myMetadata += ""; myMetadata += ""; myMetadata += ""; myMetadata += ""; //get info for each field by looping through them const QgsFields& myFields = pendingFields(); for ( int i = 0, n = myFields.size(); i < n; ++i ) { const QgsField& myField = fields[i]; myMetadata += ""; myMetadata += ""; myMetadata += ""; myMetadata += ""; myMetadata += ""; } //close field list myMetadata += "
"; myMetadata += tr( "Field" ); myMetadata += ""; myMetadata += tr( "Type" ); myMetadata += ""; myMetadata += tr( "Length" ); myMetadata += ""; myMetadata += tr( "Precision" ); myMetadata += ""; myMetadata += tr( "Comment" ); myMetadata += "
"; myMetadata += myField.name(); myMetadata += ""; myMetadata += myField.typeName(); myMetadata += ""; myMetadata += QString( "%1" ).arg( myField.length() ); myMetadata += ""; myMetadata += QString( "%1" ).arg( myField.precision() ); myMetadata += ""; myMetadata += QString( "%1" ).arg( myField.comment() ); myMetadata += "
"; //end of nested table #endif myMetadata += ""; return myMetadata; } void QgsVectorLayer::invalidateSymbolCountedFlag() { mSymbolFeatureCounted = false; } void QgsVectorLayer::onJoinedFieldsChanged() { // some of the fields of joined layers have changed -> we need to update this layer's fields too updateFields(); } void QgsVectorLayer::onFeatureDeleted( QgsFeatureId fid ) { if ( mEditCommandActive ) mDeletedFids << fid; else emit featuresDeleted( QgsFeatureIds() << fid ); emit featureDeleted( fid ); } QgsVectorLayer::ValueRelationData QgsVectorLayer::valueRelation( int idx ) { if ( mEditFormConfig->widgetType( idx ) == "ValueRelation" ) { QgsEditorWidgetConfig cfg = mEditFormConfig->widgetConfig( idx ); return ValueRelationData( cfg.value( "Layer" ).toString(), cfg.value( "Key" ).toString(), cfg.value( "Value" ).toString(), cfg.value( "AllowNull" ).toBool(), cfg.value( "OrderByValue" ).toBool(), cfg.value( "AllowMulti" ).toBool(), cfg.value( "FilterExpression" ).toString() ); } else { return ValueRelationData(); } } QList QgsVectorLayer::referencingRelations( int idx ) { return QgsProject::instance()->relationManager()->referencingRelations( this, idx ); } QDomElement QgsAttributeEditorContainer::toDomElement( QDomDocument& doc ) const { QDomElement elem = doc.createElement( "attributeEditorContainer" ); elem.setAttribute( "name", mName ); elem.setAttribute( "columnCount", mColumnCount ); Q_FOREACH ( QgsAttributeEditorElement* child, mChildren ) { elem.appendChild( child->toDomElement( doc ) ); } return elem; } void QgsAttributeEditorContainer::addChildElement( QgsAttributeEditorElement *widget ) { mChildren.append( widget ); } void QgsAttributeEditorContainer::setName( const QString& name ) { mName = name; } QList QgsAttributeEditorContainer::findElements( QgsAttributeEditorElement::AttributeEditorType type ) const { QList results; Q_FOREACH ( QgsAttributeEditorElement* elem, mChildren ) { if ( elem->type() == type ) { results.append( elem ); } if ( elem->type() == AeTypeContainer ) { QgsAttributeEditorContainer* cont = dynamic_cast( elem ); if ( cont ) results += cont->findElements( type ); } } return results; } QDomElement QgsAttributeEditorField::toDomElement( QDomDocument& doc ) const { QDomElement elem = doc.createElement( "attributeEditorField" ); elem.setAttribute( "name", mName ); elem.setAttribute( "index", mIdx ); return elem; } int QgsVectorLayer::listStylesInDatabase( QStringList &ids, QStringList &names, QStringList &descriptions, QString &msgError ) { QgsProviderRegistry * pReg = QgsProviderRegistry::instance(); QLibrary *myLib = pReg->providerLibrary( mProviderKey ); if ( !myLib ) { msgError = QObject::tr( "Unable to load %1 provider" ).arg( mProviderKey ); return -1; } listStyles_t* listStylesExternalMethod = reinterpret_cast< listStyles_t * >( cast_to_fptr( myLib->resolve( "listStyles" ) ) ); if ( !listStylesExternalMethod ) { delete myLib; msgError = QObject::tr( "Provider %1 has no %2 method" ).arg( mProviderKey, "listStyles" ); return -1; } return listStylesExternalMethod( mDataSource, ids, names, descriptions, msgError ); } QString QgsVectorLayer::getStyleFromDatabase( const QString& styleId, QString &msgError ) { QgsProviderRegistry * pReg = QgsProviderRegistry::instance(); QLibrary *myLib = pReg->providerLibrary( mProviderKey ); if ( !myLib ) { msgError = QObject::tr( "Unable to load %1 provider" ).arg( mProviderKey ); return QObject::tr( "" ); } getStyleById_t* getStyleByIdMethod = reinterpret_cast< getStyleById_t * >( cast_to_fptr( myLib->resolve( "getStyleById" ) ) ); if ( !getStyleByIdMethod ) { delete myLib; msgError = QObject::tr( "Provider %1 has no %2 method" ).arg( mProviderKey, "getStyleById" ); return QObject::tr( "" ); } return getStyleByIdMethod( mDataSource, styleId, msgError ); } void QgsVectorLayer::saveStyleToDatabase( const QString& name, const QString& description, bool useAsDefault, const QString& uiFileContent, QString &msgError ) { QString sldStyle, qmlStyle; QgsProviderRegistry * pReg = QgsProviderRegistry::instance(); QLibrary *myLib = pReg->providerLibrary( mProviderKey ); if ( !myLib ) { msgError = QObject::tr( "Unable to load %1 provider" ).arg( mProviderKey ); return; } saveStyle_t* saveStyleExternalMethod = reinterpret_cast< saveStyle_t * >( cast_to_fptr( myLib->resolve( "saveStyle" ) ) ); if ( !saveStyleExternalMethod ) { delete myLib; msgError = QObject::tr( "Provider %1 has no %2 method" ).arg( mProviderKey, "saveStyle" ); return; } QDomDocument qmlDocument, sldDocument; this->exportNamedStyle( qmlDocument, msgError ); if ( !msgError.isNull() ) { return; } qmlStyle = qmlDocument.toString(); this->exportSldStyle( sldDocument, msgError ); if ( !msgError.isNull() ) { return; } sldStyle = sldDocument.toString(); saveStyleExternalMethod( mDataSource, qmlStyle, sldStyle, name, description, uiFileContent, useAsDefault, msgError ); } QString QgsVectorLayer::loadNamedStyle( const QString &theURI, bool &theResultFlag ) { return loadNamedStyle( theURI, theResultFlag, false ); } QString QgsVectorLayer::loadNamedStyle( const QString &theURI, bool &theResultFlag, bool loadFromLocalDB ) { QgsDataSourceURI dsUri( theURI ); if ( !loadFromLocalDB && !dsUri.database().isEmpty() ) { QgsProviderRegistry * pReg = QgsProviderRegistry::instance(); QLibrary *myLib = pReg->providerLibrary( mProviderKey ); if ( myLib ) { loadStyle_t* loadStyleExternalMethod = reinterpret_cast< loadStyle_t * >( cast_to_fptr( myLib->resolve( "loadStyle" ) ) ); if ( loadStyleExternalMethod ) { QString qml, errorMsg; qml = loadStyleExternalMethod( mDataSource, errorMsg ); if ( !qml.isEmpty() ) { Q_NOWARN_DEPRECATED_PUSH theResultFlag = applyNamedStyle( qml, errorMsg ); Q_NOWARN_DEPRECATED_POP return QObject::tr( "Loaded from Provider" ); } } } } return QgsMapLayer::loadNamedStyle( theURI, theResultFlag ); } bool QgsVectorLayer::applyNamedStyle( const QString& namedStyle, QString& errorMsg ) { QDomDocument myDocument( "qgis" ); myDocument.setContent( namedStyle ); return importNamedStyle( myDocument, errorMsg ); } QDomElement QgsAttributeEditorRelation::toDomElement( QDomDocument& doc ) const { QDomElement elem = doc.createElement( "attributeEditorRelation" ); elem.setAttribute( "name", mName ); elem.setAttribute( "relation", mRelation.id() ); return elem; } bool QgsAttributeEditorRelation::init( QgsRelationManager* relationManager ) { mRelation = relationManager->relation( mRelationId ); return mRelation.isValid(); } QSet QgsVectorLayer::layerDependencies() const { if ( mDataProvider ) { return mDataProvider->layerDependencies(); } return QSet(); }