/*************************************************************************** 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 "qgssettings.h" #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 "qgsexception.h" #include "qgscurve.h" #include "qgsdatasourceuri.h" #include "qgsexpressionfieldbuffer.h" #include "qgsexpressionnodeimpl.h" #include "qgsfeature.h" #include "qgsfeaturerequest.h" #include "qgsfields.h" #include "qgsgeometry.h" #include "qgslayermetadataformatter.h" #include "qgslogger.h" #include "qgsmaplayerlegend.h" #include "qgsmaptopixel.h" #include "qgsmessagelog.h" #include "qgsogcutils.h" #include "qgspainting.h" #include "qgspointxy.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 "qgsvectorlayerfeaturecounter.h" #include "qgspoint.h" #include "qgsrenderer.h" #include "qgssymbollayer.h" #include "qgssinglesymbolrenderer.h" #include "qgsdiagramrenderer.h" #include "qgsstyle.h" #include "qgspallabeling.h" #include "qgssimplifymethod.h" #include "qgsexpressioncontext.h" #include "qgsfeedback.h" #include "qgsxmlutils.h" #include "qgsunittypes.h" #include "qgstaskmanager.h" #include "qgstransaction.h" #include "qgsauxiliarystorage.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 ); typedef bool deleteStyleById_t( const QString &uri, QString styleID, QString &errCause ); QgsVectorLayer::QgsVectorLayer( const QString &vectorLayerPath, const QString &baseName, const QString &providerKey, const LayerOptions &options ) : QgsMapLayer( VectorLayer, baseName, vectorLayerPath ) , mProviderKey( providerKey ) , mAuxiliaryLayer( nullptr ) , mAuxiliaryLayerKey( QString() ) , mReadExtentFromXml( options.readExtentFromXml ) { mActions = new QgsActionManager( this ); mConditionalStyles = new QgsConditionalLayerStyles(); mJoinBuffer = new QgsVectorLayerJoinBuffer( this ); mJoinBuffer->setParent( this ); connect( mJoinBuffer, &QgsVectorLayerJoinBuffer::joinedFieldsChanged, this, &QgsVectorLayer::onJoinedFieldsChanged ); mExpressionFieldBuffer = new QgsExpressionFieldBuffer(); // if we're given a provider type, try to create and bind one to this layer if ( !vectorLayerPath.isEmpty() && !mProviderKey.isEmpty() ) { setDataSource( vectorLayerPath, baseName, providerKey, options.loadDefaultStyle ); } connect( this, &QgsVectorLayer::selectionChanged, this, [ = ] { emit repaintRequested(); } ); connect( QgsProject::instance()->relationManager(), &QgsRelationManager::relationsLoaded, this, &QgsVectorLayer::onRelationsLoaded ); // Default simplify drawing settings QgsSettings settings; mSimplifyMethod.setSimplifyHints( settings.enumValue( QStringLiteral( "qgis/simplifyDrawingHints" ), mSimplifyMethod.simplifyHints(), QgsSettings::NoSection, true ) ); mSimplifyMethod.setSimplifyAlgorithm( settings.enumValue( QStringLiteral( "qgis/simplifyAlgorithm" ), mSimplifyMethod.simplifyAlgorithm() ) ); mSimplifyMethod.setThreshold( settings.value( QStringLiteral( "qgis/simplifyDrawingTol" ), mSimplifyMethod.threshold() ).toFloat() ); mSimplifyMethod.setForceLocalOptimization( settings.value( QStringLiteral( "qgis/simplifyLocal" ), mSimplifyMethod.forceLocalOptimization() ).toBool() ); mSimplifyMethod.setMaximumScale( settings.value( QStringLiteral( "qgis/simplifyMaxScale" ), mSimplifyMethod.maximumScale() ).toFloat() ); } // QgsVectorLayer ctor QgsVectorLayer::~QgsVectorLayer() { emit willBeDeleted(); mValid = false; delete mDataProvider; delete mEditBuffer; delete mJoinBuffer; delete mExpressionFieldBuffer; delete mLabeling; delete mDiagramLayerSettings; delete mDiagramRenderer; delete mActions; delete mRenderer; delete mConditionalStyles; if ( mFeatureCounter ) mFeatureCounter->cancel(); } QgsVectorLayer *QgsVectorLayer::clone() const { QgsVectorLayer *layer = new QgsVectorLayer( source(), name(), mProviderKey ); QgsMapLayer::clone( layer ); QList joins = vectorJoins(); Q_FOREACH ( const QgsVectorLayerJoinInfo &join, joins ) { // do not copy join information for auxiliary layer if ( !auxiliaryLayer() || ( auxiliaryLayer() && auxiliaryLayer()->id() != join.joinLayerId() ) ) layer->addJoin( join ); } layer->setProviderEncoding( dataProvider()->encoding() ); layer->setDisplayExpression( displayExpression() ); layer->setMapTipTemplate( mapTipTemplate() ); layer->setReadOnly( isReadOnly() ); layer->selectByIds( selectedFeatureIds() ); layer->setExcludeAttributesWms( excludeAttributesWms() ); layer->setExcludeAttributesWfs( excludeAttributesWfs() ); layer->setAttributeTableConfig( attributeTableConfig() ); layer->setFeatureBlendMode( featureBlendMode() ); layer->setOpacity( opacity() ); layer->setReadExtentFromXml( readExtentFromXml() ); Q_FOREACH ( const QgsAction &action, actions()->actions() ) { layer->actions()->addAction( action ); } if ( renderer() ) { layer->setRenderer( renderer()->clone() ); } if ( labeling() ) { layer->setLabeling( labeling()->clone() ); } layer->setLabelsEnabled( labelsEnabled() ); layer->setSimplifyMethod( simplifyMethod() ); if ( diagramRenderer() ) { layer->setDiagramRenderer( diagramRenderer()->clone() ); } if ( diagramLayerSettings() ) { layer->setDiagramLayerSettings( *diagramLayerSettings() ); } for ( int i = 0; i < fields().count(); i++ ) { layer->setFieldAlias( i, attributeAlias( i ) ); layer->setEditorWidgetSetup( i, editorWidgetSetup( i ) ); layer->setConstraintExpression( i, constraintExpression( i ), constraintDescription( i ) ); layer->setDefaultValueDefinition( i, defaultValueDefinition( i ) ); QMap< QgsFieldConstraints::Constraint, QgsFieldConstraints::ConstraintStrength> constraints = fieldConstraintsAndStrength( i ); auto constraintIt = constraints.constBegin(); for ( ; constraintIt != constraints.constEnd(); ++ constraintIt ) { layer->setFieldConstraint( i, constraintIt.key(), constraintIt.value() ); } if ( fields().fieldOrigin( i ) == QgsFields::OriginExpression ) { layer->addExpressionField( expressionField( i ), fields().at( i ) ); } } layer->setEditFormConfig( editFormConfig() ); if ( auxiliaryLayer() ) layer->setAuxiliaryLayer( auxiliaryLayer()->clone( layer ) ); return layer; } QString QgsVectorLayer::storageType() const { if ( mDataProvider ) { return mDataProvider->storageType(); } return QString(); } QString QgsVectorLayer::capabilitiesString() const { if ( mDataProvider ) { return mDataProvider->capabilitiesString(); } return QString(); } QString QgsVectorLayer::dataComment() const { if ( mDataProvider ) { return mDataProvider->dataComment(); } return QString(); } QString QgsVectorLayer::providerType() const { return mProviderKey; } QgsCoordinateReferenceSystem QgsVectorLayer::sourceCrs() const { return crs(); } QString QgsVectorLayer::sourceName() const { return name(); } void QgsVectorLayer::reload() { if ( mDataProvider ) { mDataProvider->reloadData(); updateFields(); } } QgsMapLayerRenderer *QgsVectorLayer::createMapRenderer( QgsRenderContext &rendererContext ) { return new QgsVectorLayerRenderer( this, rendererContext ); } 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::selectByRect( QgsRectangle &rect, QgsVectorLayer::SelectBehavior behavior ) { // 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, behavior ); } void QgsVectorLayer::selectByExpression( const QString &expression, QgsVectorLayer::SelectBehavior behavior ) { QgsFeatureIds newSelection; QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( this ) ); if ( behavior == SetSelection || behavior == AddToSelection ) { QgsFeatureRequest request = QgsFeatureRequest().setFilterExpression( expression ) .setExpressionContext( context ) .setFlags( QgsFeatureRequest::NoGeometry ) .setSubsetOfAttributes( QgsAttributeList() ); QgsFeatureIterator features = getFeatures( request ); if ( behavior == AddToSelection ) { newSelection = selectedFeatureIds(); } QgsFeature feat; while ( features.nextFeature( feat ) ) { newSelection << feat.id(); } features.close(); } else if ( behavior == IntersectSelection || behavior == RemoveFromSelection ) { QgsExpression exp( expression ); exp.prepare( &context ); QgsFeatureIds oldSelection = selectedFeatureIds(); 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 && behavior == IntersectSelection ) { newSelection << feat.id(); } else if ( !matches && behavior == RemoveFromSelection ) { newSelection << feat.id(); } } } selectByIds( newSelection ); } void QgsVectorLayer::selectByIds( const QgsFeatureIds &ids, QgsVectorLayer::SelectBehavior behavior ) { QgsFeatureIds newSelection; switch ( behavior ) { 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( const QgsFeatureIds &selectIds, const QgsFeatureIds &deselectIds ) { QgsFeatureIds intersectingIds = selectIds & deselectIds; if ( !intersectingIds.isEmpty() ) { QgsDebugMsgLevel( QStringLiteral( "Trying to select and deselect the same item at the same time. Unsure what to do. Selecting dubious items." ), 3 ); } 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() ); } 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( QgsDiagramRenderer *r ) { delete mDiagramRenderer; mDiagramRenderer = r; emit rendererChanged(); emit styleChanged(); } QgsWkbTypes::GeometryType QgsVectorLayer::geometryType() const { if ( mValid && mDataProvider ) { return QgsWkbTypes::geometryType( mDataProvider->wkbType() ); } else { QgsDebugMsgLevel( QStringLiteral( "invalid layer or pointer to mDataProvider is null" ), 3 ); } // 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. // AP: it looks like we almost always get here, since 2.x ... either we remove this // warning of take care of the problems that may occur QgsDebugMsg( QStringLiteral( "WARNING: This code should never be reached. Problems may occur..." ) ); return QgsWkbTypes::UnknownGeometry; } QgsWkbTypes::Type QgsVectorLayer::wkbType() const { return mWkbType; } QgsRectangle QgsVectorLayer::boundingBoxOfSelected() const { if ( !mValid || !isSpatial() || 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.hasGeometry() ) continue; r = fet.geometry().boundingBox(); retval.combineExtentWith( r ); } } else { QgsFeatureIterator fit = getFeatures( QgsFeatureRequest() .setSubsetOfAttributes( QgsAttributeList() ) ); while ( fit.nextFeature( fet ) ) { if ( mSelectedFeatureIds.contains( fet.id() ) ) { if ( fet.hasGeometry() ) { r = fet.geometry().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 { return mLabelsEnabled && static_cast< bool >( mLabeling ); } void QgsVectorLayer::setLabelsEnabled( bool enabled ) { mLabelsEnabled = enabled; } 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( const QString &legendKey ) const { if ( !mSymbolFeatureCounted ) return -1; return mSymbolFeatureCountMap.value( legendKey ); } QgsVectorLayerFeatureCounter *QgsVectorLayer::countSymbolFeatures() { if ( mSymbolFeatureCounted || mFeatureCounter ) return mFeatureCounter; mSymbolFeatureCountMap.clear(); if ( !mValid ) { QgsDebugMsgLevel( QStringLiteral( "invoked with invalid layer" ), 3 ); return mFeatureCounter; } if ( !mDataProvider ) { QgsDebugMsgLevel( QStringLiteral( "invoked with null mDataProvider" ), 3 ); return mFeatureCounter; } if ( !mRenderer ) { QgsDebugMsgLevel( QStringLiteral( "invoked with null mRenderer" ), 3 ); return mFeatureCounter; } if ( !mFeatureCounter ) { mFeatureCounter = new QgsVectorLayerFeatureCounter( this ); connect( mFeatureCounter, &QgsTask::taskCompleted, this, &QgsVectorLayer::onFeatureCounterCompleted ); connect( mFeatureCounter, &QgsTask::taskTerminated, this, &QgsVectorLayer::onFeatureCounterTerminated ); QgsApplication::taskManager()->addTask( mFeatureCounter ); } return mFeatureCounter; } void QgsVectorLayer::updateExtents( bool force ) { // do not update extent by default when trust project option is activated if ( force || !mReadExtentFromXml || ( mReadExtentFromXml && mXmlExtent.isNull() ) ) mValidExtent = false; } void QgsVectorLayer::setExtent( const QgsRectangle &r ) { QgsMapLayer::setExtent( r ); mValidExtent = true; } void QgsVectorLayer::updateDefaultValues( QgsFeatureId fid, QgsFeature feature ) { if ( !mDefaultValueOnUpdateFields.isEmpty() ) { if ( !feature.isValid() ) feature = getFeature( fid ); int size = mFields.size(); for ( int idx : qgis::as_const( mDefaultValueOnUpdateFields ) ) { if ( idx < 0 || idx >= size ) continue; feature.setAttribute( idx, defaultValue( idx, feature ) ); updateFeature( feature, true ); } } } QgsRectangle QgsVectorLayer::extent() const { QgsRectangle rect; rect.setMinimal(); if ( !isSpatial() ) return rect; if ( !mValidExtent && mLazyExtent && mDataProvider && !mDataProvider->hasMetadata() && mReadExtentFromXml && !mXmlExtent.isNull() ) { mExtent = mXmlExtent; mValidExtent = true; mLazyExtent = false; } if ( !mValidExtent && mLazyExtent && mDataProvider ) { // get the extent QgsRectangle mbr = mDataProvider->extent(); // show the extent QgsDebugMsgLevel( QStringLiteral( "Extent of layer: %1" ).arg( mbr.toString() ), 3 ); // store the extent mValidExtent = true; mExtent = mbr; mLazyExtent = false; } if ( mValidExtent ) return QgsMapLayer::extent(); if ( !mValid || !mDataProvider ) { QgsDebugMsgLevel( QStringLiteral( "invoked with invalid layer or null mDataProvider" ), 3 ); 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->hasGeometry() ) { QgsRectangle r = it->geometry().boundingBox(); rect.combineExtentWith( r ); } } } } else { QgsFeatureIterator fit = getFeatures( QgsFeatureRequest() .setSubsetOfAttributes( QgsAttributeList() ) ); QgsFeature fet; while ( fit.nextFeature( fet ) ) { if ( fet.hasGeometry() && fet.geometry().type() != QgsWkbTypes::UnknownGeometry ) { QgsRectangle bb = fet.geometry().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 } mValidExtent = true; mExtent = rect; // Send this (hopefully) up the chain to the map canvas emit recalculateExtents(); return rect; } QgsRectangle QgsVectorLayer::sourceExtent() const { return extent(); } QString QgsVectorLayer::subsetString() const { if ( !mValid || !mDataProvider ) { QgsDebugMsgLevel( QStringLiteral( "invoked with invalid layer or null mDataProvider" ), 3 ); return QString(); } return mDataProvider->subsetString(); } bool QgsVectorLayer::setSubsetString( const QString &subset ) { if ( !mValid || !mDataProvider ) { QgsDebugMsgLevel( QStringLiteral( "invoked with invalid layer or null mDataProvider" ), 3 ); 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 && ( isSpatial() && geometryType() != QgsWkbTypes::PointGeometry ) && ( mSimplifyMethod.simplifyHints() & simplifyHint ) && renderContext.useRenderingOptimization() ) { double maximumSimplificationScale = mSimplifyMethod.maximumScale(); // check maximum scale at which generalisation should be carried out return !( maximumSimplificationScale > 1 && renderContext.rendererScale() <= maximumSimplificationScale ); } return false; } QgsConditionalLayerStyles *QgsVectorLayer::conditionalStyles() const { return mConditionalStyles; } QgsFeatureIterator QgsVectorLayer::getFeatures( const QgsFeatureRequest &request ) const { if ( !mValid || !mDataProvider ) return QgsFeatureIterator(); return QgsFeatureIterator( new QgsVectorLayerFeatureIterator( new QgsVectorLayerFeatureSource( this ), true, request ) ); } bool QgsVectorLayer::addFeature( QgsFeature &feature, Flags ) { if ( !mValid || !mEditBuffer || !mDataProvider ) return false; bool success = mEditBuffer->addFeature( feature ); if ( success ) { updateExtents(); if ( mJoinBuffer->containsJoins() ) success = mJoinBuffer->addFeature( feature ); } return success; } bool QgsVectorLayer::updateFeature( const QgsFeature &updatedFeature, bool skipDefaultValues ) { if ( !mEditBuffer || !mDataProvider ) { return false; } QgsFeature currentFeature = getFeature( updatedFeature.id() ); if ( currentFeature.isValid() ) { bool hasChanged = false; bool hasError = false; if ( ( updatedFeature.hasGeometry() || currentFeature.hasGeometry() ) && !updatedFeature.geometry().equals( currentFeature.geometry() ) ) { if ( changeGeometry( updatedFeature.id(), updatedFeature.geometry(), true ) ) { hasChanged = true; } else { QgsDebugMsgLevel( QStringLiteral( "geometry of feature %1 could not be changed." ).arg( updatedFeature.id() ), 3 ); } } QgsAttributes fa = updatedFeature.attributes(); QgsAttributes ca = currentFeature.attributes(); for ( int attr = 0; attr < fa.count(); ++attr ) { if ( fa.at( attr ) != ca.at( attr ) ) { if ( changeAttributeValue( updatedFeature.id(), attr, fa.at( attr ), ca.at( attr ), true ) ) { hasChanged = true; } else { QgsDebugMsgLevel( QStringLiteral( "attribute %1 of feature %2 could not be changed." ).arg( attr ).arg( updatedFeature.id() ), 3 ); hasError = true; } } } if ( hasChanged && !mDefaultValueOnUpdateFields.isEmpty() && !skipDefaultValues ) updateDefaultValues( updatedFeature.id(), updatedFeature ); return !hasError; } else { QgsDebugMsgLevel( QStringLiteral( "feature %1 could not be retrieved" ).arg( updatedFeature.id() ), 3 ); return false; } } 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::insertVertex( const QgsPoint &point, QgsFeatureId atFeatureId, int beforeVertex ) { if ( !mValid || !mEditBuffer || !mDataProvider ) return false; QgsVectorLayerEditUtils utils( this ); bool result = utils.insertVertex( point, 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 QgsPoint &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; } QgsVectorLayer::EditResult QgsVectorLayer::deleteVertex( QgsFeatureId featureId, int vertex ) { if ( !mValid || !mEditBuffer || !mDataProvider ) return QgsVectorLayer::InvalidLayer; QgsVectorLayerEditUtils utils( this ); EditResult result = utils.deleteVertex( 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; } QgsGeometry::OperationResult QgsVectorLayer::addRing( const QVector &ring, QgsFeatureId *featureId ) { if ( !mValid || !mEditBuffer || !mDataProvider ) return QgsGeometry::OperationResult::LayerNotEditable; QgsVectorLayerEditUtils utils( this ); QgsGeometry::OperationResult result = QgsGeometry::OperationResult::AddRingNotInExistingFeature; //first try with selected features if ( !mSelectedFeatureIds.isEmpty() ) { result = utils.addRing( ring, mSelectedFeatureIds, featureId ); } if ( result != QgsGeometry::OperationResult::Success ) { //try with all intersecting features result = utils.addRing( ring, QgsFeatureIds(), featureId ); } return result; } QgsGeometry::OperationResult QgsVectorLayer::addRing( QgsCurve *ring, QgsFeatureId *featureId ) { if ( !mValid || !mEditBuffer || !mDataProvider ) { delete ring; return QgsGeometry::OperationResult::LayerNotEditable; } if ( !ring ) { return QgsGeometry::OperationResult::InvalidInputGeometryType; } if ( !ring->isClosed() ) { delete ring; return QgsGeometry::OperationResult::AddRingNotClosed; } QgsVectorLayerEditUtils utils( this ); QgsGeometry::OperationResult result = QgsGeometry::OperationResult::AddRingNotInExistingFeature; //first try with selected features if ( !mSelectedFeatureIds.isEmpty() ) { result = utils.addRing( static_cast< QgsCurve * >( ring->clone() ), mSelectedFeatureIds, featureId ); } if ( result != QgsGeometry::OperationResult::Success ) { //try with all intersecting features result = utils.addRing( static_cast< QgsCurve * >( ring->clone() ), QgsFeatureIds(), featureId ); } delete ring; return result; } QgsGeometry::OperationResult QgsVectorLayer::addPart( const QList &points ) { if ( !mValid || !mEditBuffer || !mDataProvider ) return QgsGeometry::OperationResult::LayerNotEditable; //number of selected features must be 1 if ( mSelectedFeatureIds.empty() ) { QgsDebugMsgLevel( "Number of selected features < 1", 3 ); return QgsGeometry::OperationResult::SelectionIsEmpty; } else if ( mSelectedFeatureIds.size() > 1 ) { QgsDebugMsgLevel( "Number of selected features > 1", 3 ); return QgsGeometry::OperationResult::SelectionIsGreaterThanOne; } QgsVectorLayerEditUtils utils( this ); QgsGeometry::OperationResult result = utils.addPart( points, *mSelectedFeatureIds.constBegin() ); if ( result == QgsGeometry::OperationResult::Success ) updateExtents(); return result; } QgsGeometry::OperationResult QgsVectorLayer::addPart( const QgsPointSequence &points ) { if ( !mValid || !mEditBuffer || !mDataProvider ) return QgsGeometry::OperationResult::LayerNotEditable; //number of selected features must be 1 if ( mSelectedFeatureIds.empty() ) { QgsDebugMsgLevel( "Number of selected features <1", 3 ); return QgsGeometry::OperationResult::SelectionIsEmpty; } else if ( mSelectedFeatureIds.size() > 1 ) { QgsDebugMsgLevel( "Number of selected features >1", 3 ); return QgsGeometry::OperationResult::SelectionIsGreaterThanOne; } QgsVectorLayerEditUtils utils( this ); QgsGeometry::OperationResult result = utils.addPart( points, *mSelectedFeatureIds.constBegin() ); if ( result == QgsGeometry::OperationResult::Success ) updateExtents(); return result; } QgsGeometry::OperationResult QgsVectorLayer::addPart( QgsCurve *ring ) { if ( !mValid || !mEditBuffer || !mDataProvider ) return QgsGeometry::OperationResult::LayerNotEditable; //number of selected features must be 1 if ( mSelectedFeatureIds.empty() ) { QgsDebugMsgLevel( "Number of selected features <1", 3 ); return QgsGeometry::OperationResult::SelectionIsEmpty; } else if ( mSelectedFeatureIds.size() > 1 ) { QgsDebugMsgLevel( "Number of selected features >1", 3 ); return QgsGeometry::OperationResult::SelectionIsGreaterThanOne; } QgsVectorLayerEditUtils utils( this ); QgsGeometry::OperationResult result = utils.addPart( ring, *mSelectedFeatureIds.constBegin() ); if ( result == QgsGeometry::OperationResult::Success ) updateExtents(); return result; } int QgsVectorLayer::translateFeature( QgsFeatureId featureId, double dx, double dy ) { if ( !mValid || !mEditBuffer || !mDataProvider ) return QgsGeometry::OperationResult::LayerNotEditable; QgsVectorLayerEditUtils utils( this ); int result = utils.translateFeature( featureId, dx, dy ); if ( result == QgsGeometry::OperationResult::Success ) updateExtents(); return result; } QgsGeometry::OperationResult QgsVectorLayer::splitParts( const QVector &splitLine, bool topologicalEditing ) { if ( !mValid || !mEditBuffer || !mDataProvider ) return QgsGeometry::OperationResult::LayerNotEditable; QgsVectorLayerEditUtils utils( this ); return utils.splitParts( splitLine, topologicalEditing ); } QgsGeometry::OperationResult QgsVectorLayer::splitFeatures( const QVector &splitLine, bool topologicalEditing ) { if ( !mValid || !mEditBuffer || !mDataProvider ) return QgsGeometry::OperationResult::LayerNotEditable; QgsVectorLayerEditUtils utils( this ); return utils.splitFeatures( splitLine, topologicalEditing ); } int QgsVectorLayer::addTopologicalPoints( const QgsGeometry &geom ) { if ( !mValid || !mEditBuffer || !mDataProvider ) return -1; QgsVectorLayerEditUtils utils( this ); return utils.addTopologicalPoints( geom ); } int QgsVectorLayer::addTopologicalPoints( const QgsPointXY &p ) { if ( !mValid || !mEditBuffer || !mDataProvider ) return -1; QgsVectorLayerEditUtils utils( this ); return utils.addTopologicalPoints( p ); } 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 ); connect( mDataProvider->transaction(), &QgsTransaction::dirtied, this, &QgsVectorLayer::onDirtyTransaction, Qt::UniqueConnection ); } else { mEditBuffer = new QgsVectorLayerEditBuffer( this ); } // forward signals connect( mEditBuffer, &QgsVectorLayerEditBuffer::layerModified, this, &QgsVectorLayer::invalidateSymbolCountedFlag ); connect( mEditBuffer, &QgsVectorLayerEditBuffer::layerModified, this, &QgsVectorLayer::layerModified ); // TODO[MD]: necessary? //connect( mEditBuffer, SIGNAL( layerModified() ), this, SLOT( triggerRepaint() ) ); // TODO[MD]: works well? connect( mEditBuffer, &QgsVectorLayerEditBuffer::featureAdded, this, &QgsVectorLayer::featureAdded ); connect( mEditBuffer, &QgsVectorLayerEditBuffer::featureDeleted, this, &QgsVectorLayer::onFeatureDeleted ); connect( mEditBuffer, &QgsVectorLayerEditBuffer::geometryChanged, this, &QgsVectorLayer::geometryChanged ); connect( mEditBuffer, &QgsVectorLayerEditBuffer::attributeValueChanged, this, &QgsVectorLayer::attributeValueChanged ); connect( mEditBuffer, &QgsVectorLayerEditBuffer::attributeAdded, this, &QgsVectorLayer::attributeAdded ); connect( mEditBuffer, &QgsVectorLayerEditBuffer::attributeDeleted, this, &QgsVectorLayer::attributeDeleted ); connect( mEditBuffer, &QgsVectorLayerEditBuffer::committedAttributesDeleted, this, &QgsVectorLayer::committedAttributesDeleted ); connect( mEditBuffer, &QgsVectorLayerEditBuffer::committedAttributesAdded, this, &QgsVectorLayer::committedAttributesAdded ); connect( mEditBuffer, &QgsVectorLayerEditBuffer::committedFeaturesAdded, this, &QgsVectorLayer::committedFeaturesAdded ); connect( mEditBuffer, &QgsVectorLayerEditBuffer::committedFeaturesRemoved, this, &QgsVectorLayer::committedFeaturesRemoved ); connect( mEditBuffer, &QgsVectorLayerEditBuffer::committedAttributeValuesChanges, this, &QgsVectorLayer::committedAttributeValuesChanges ); connect( mEditBuffer, &QgsVectorLayerEditBuffer::committedGeometriesChanges, this, &QgsVectorLayer::committedGeometriesChanges ); updateFields(); emit editingStarted(); return true; } bool QgsVectorLayer::readXml( const QDomNode &layer_node, QgsReadWriteContext &context ) { QgsDebugMsgLevel( QStringLiteral( "Datasource in QgsVectorLayer::readXml: %1" ).arg( mDataSource.toLocal8Bit().data() ), 3 ); //process provider key QDomNode pkeyNode = layer_node.namedItem( QStringLiteral( "provider" ) ); if ( pkeyNode.isNull() ) { mProviderKey.clear(); } 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( QLatin1String( "dbname=" ) ) ) { mProviderKey = QStringLiteral( "postgres" ); } else { mProviderKey = QStringLiteral( "ogr" ); } if ( !setDataProvider( mProviderKey ) ) { return false; } QDomElement pkeyElem = pkeyNode.toElement(); if ( !pkeyElem.isNull() ) { QString encodingString = pkeyElem.attribute( QStringLiteral( "encoding" ) ); if ( !encodingString.isEmpty() ) { mDataProvider->setEncoding( encodingString ); } } // load vector joins - does not resolve references to layers yet mJoinBuffer->readXml( layer_node ); updateFields(); QString errorMsg; if ( !readSymbology( layer_node, errorMsg, context ) ) { return false; } readStyleManager( layer_node ); QDomNode depsNode = layer_node.namedItem( QStringLiteral( "dataDependencies" ) ); QDomNodeList depsNodes = depsNode.childNodes(); QSet sources; for ( int i = 0; i < depsNodes.count(); i++ ) { QString source = depsNodes.at( i ).toElement().attribute( QStringLiteral( "id" ) ); sources << QgsMapLayerDependency( source ); } setDependencies( sources ); setLegend( QgsMapLayerLegend::defaultVectorLegend( this ) ); // read extent if ( mReadExtentFromXml ) { QDomNode extentNode = layer_node.namedItem( QStringLiteral( "extent" ) ); if ( !extentNode.isNull() ) { mXmlExtent = QgsXmlUtils::readRectangle( extentNode.toElement() ); } } // auxiliary layer const QDomNode asNode = layer_node.namedItem( QStringLiteral( "auxiliaryLayer" ) ); const QDomElement asElem = asNode.toElement(); if ( !asElem.isNull() ) { mAuxiliaryLayerKey = asElem.attribute( QStringLiteral( "key" ) ); } 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 ) { QgsWkbTypes::GeometryType geomType = mValid && mDataProvider ? geometryType() : QgsWkbTypes::UnknownGeometry; mDataSource = dataSource; setName( baseName ); setDataProvider( provider ); if ( !mValid ) return; // Always set crs setCoordinateSystem(); // reset style if loading default style, style is missing, or geometry type has changed if ( !renderer() || !legend() || geomType != geometryType() || loadDefaultStyleFlag ) { bool defaultLoadedFlag = false; if ( loadDefaultStyleFlag && isSpatial() && mDataProvider->capabilities() & QgsVectorDataProvider::CreateRenderer ) { // first try to create a renderer directly from the data provider std::unique_ptr< QgsFeatureRenderer > defaultRenderer( mDataProvider->createRenderer() ); if ( defaultRenderer ) { defaultLoadedFlag = true; setRenderer( defaultRenderer.release() ); } } // else check if there is a default style / propertysheet defined // for this layer and if so apply it if ( !defaultLoadedFlag && loadDefaultStyleFlag ) { loadDefaultStyle( defaultLoadedFlag ); } // if the default style failed to load or was disabled use some very basic defaults if ( !defaultLoadedFlag && isSpatial() ) { // add single symbol renderer setRenderer( QgsFeatureRenderer::defaultRenderer( geometryType() ) ); } setLegend( QgsMapLayerLegend::defaultVectorLegend( this ) ); } emit repaintRequested(); } QString QgsVectorLayer::loadDefaultStyle( bool &resultFlag ) { if ( isSpatial() && mDataProvider->capabilities() & QgsVectorDataProvider::CreateRenderer ) { // first try to create a renderer directly from the data provider std::unique_ptr< QgsFeatureRenderer > defaultRenderer( mDataProvider->createRenderer() ); if ( defaultRenderer ) { resultFlag = true; setRenderer( defaultRenderer.release() ); return QString(); } } return QgsMapLayer::loadDefaultStyle( resultFlag ); } bool QgsVectorLayer::setDataProvider( QString const &provider ) { mProviderKey = provider; // XXX is this necessary? Usually already set // primary key unicity is tested at construction time, so it has to be set // before initializing postgres provider QString checkUnicityKey = QStringLiteral( "checkPrimaryKeyUnicity" ); QString dataSource = mDataSource; if ( provider.compare( QLatin1String( "postgres" ) ) == 0 ) { QgsDataSourceUri uri( dataSource ); if ( uri.hasParam( checkUnicityKey ) ) uri.removeParam( checkUnicityKey ); uri.setParam( checkUnicityKey, mReadExtentFromXml ? "0" : "1" ); dataSource = uri.uri( false ); } delete mDataProvider; mDataProvider = qobject_cast( QgsProviderRegistry::instance()->createProvider( provider, dataSource ) ); if ( !mDataProvider ) { QgsDebugMsgLevel( QStringLiteral( "Unable to get data provider" ), 2 ); return false; } mDataProvider->setParent( this ); connect( mDataProvider, &QgsVectorDataProvider::raiseError, this, &QgsVectorLayer::raiseError ); QgsDebugMsgLevel( QStringLiteral( "Instantiated the data provider plugin" ), 2 ); mValid = mDataProvider->isValid(); if ( !mValid ) { QgsDebugMsgLevel( QStringLiteral( "Invalid provider plugin %1" ).arg( QString( mDataSource.toUtf8() ) ), 2 ); return false; } if ( mDataProvider->capabilities() & QgsVectorDataProvider::ReadLayerMetadata ) { setMetadata( mDataProvider->layerMetadata() ); QgsDebugMsgLevel( QString( "Set Data provider QgsLayerMetadata identifier[%1]" ).arg( metadata().identifier() ), 4 ); } // TODO: Check if the provider has the capability to send fullExtentCalculated connect( mDataProvider, &QgsVectorDataProvider::fullExtentCalculated, this, [ = ] { updateExtents(); } ); // get and store the feature type mWkbType = mDataProvider->wkbType(); updateFields(); if ( mProviderKey == QLatin1String( "postgres" ) ) { QgsDebugMsgLevel( QStringLiteral( "Beautifying layer name %1" ).arg( name() ), 3 ); // adjust the display name for postgres layers QRegExp reg( R"lit("[^"]+"\."([^"] + )"( \([^)]+\))?)lit" ); if ( reg.indexIn( name() ) >= 0 ) { QStringList stuff = reg.capturedTexts(); QString lName = stuff[1]; const QMap &layers = QgsProject::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() ) setName( lName ); } QgsDebugMsgLevel( QStringLiteral( "Beautified layer name %1" ).arg( name() ), 3 ); // deal with unnecessary schema qualification to make v.in.ogr happy // and remove unnecessary key QgsDataSourceUri dataProviderUri( mDataProvider->dataSourceUri() ); if ( dataProviderUri.hasParam( checkUnicityKey ) ) dataProviderUri.removeParam( checkUnicityKey ); mDataSource = dataProviderUri.uri( false ); } else if ( mProviderKey == QLatin1String( "osm" ) ) { // make sure that the "observer" has been removed from URI to avoid crashes mDataSource = mDataProvider->dataSourceUri(); } else if ( provider == QLatin1String( "ogr" ) ) { // make sure that the /vsigzip or /vsizip is added to uri, if applicable mDataSource = mDataProvider->dataSourceUri(); if ( mDataSource.right( 10 ) == QLatin1String( "|layerid=0" ) ) mDataSource.chop( 10 ); } else if ( provider == QStringLiteral( "memory" ) ) { // required so that source differs between memory layers mDataSource = mDataSource + QStringLiteral( "&uid=%1" ).arg( QUuid::createUuid().toString() ); } connect( mDataProvider, &QgsVectorDataProvider::dataChanged, this, &QgsVectorLayer::dataChanged ); connect( mDataProvider, &QgsVectorDataProvider::dataChanged, this, &QgsVectorLayer::removeSelection ); return true; } // QgsVectorLayer:: setDataProvider /* virtual */ bool QgsVectorLayer::writeXml( QDomNode &layer_node, QDomDocument &document, const QgsReadWriteContext &context ) const { // first get the layer element so that we can append the type attribute QDomElement mapLayerNode = layer_node.toElement(); if ( mapLayerNode.isNull() || ( "maplayer" != mapLayerNode.nodeName() ) ) { QgsDebugMsgLevel( QStringLiteral( "can't find " ), 2 ); return false; } mapLayerNode.setAttribute( QStringLiteral( "type" ), QStringLiteral( "vector" ) ); // set the geometry type mapLayerNode.setAttribute( QStringLiteral( "geometry" ), QgsWkbTypes::geometryDisplayString( geometryType() ) ); // add provider node if ( mDataProvider ) { QDomElement provider = document.createElement( QStringLiteral( "provider" ) ); provider.setAttribute( QStringLiteral( "encoding" ), mDataProvider->encoding() ); QDomText providerText = document.createTextNode( providerType() ); provider.appendChild( providerText ); layer_node.appendChild( provider ); } //save joins mJoinBuffer->writeXml( layer_node, document ); // dependencies QDomElement dependenciesElement = document.createElement( QStringLiteral( "layerDependencies" ) ); Q_FOREACH ( const QgsMapLayerDependency &dep, dependencies() ) { if ( dep.type() != QgsMapLayerDependency::PresenceDependency ) continue; QDomElement depElem = document.createElement( QStringLiteral( "layer" ) ); depElem.setAttribute( QStringLiteral( "id" ), dep.layerId() ); dependenciesElement.appendChild( depElem ); } layer_node.appendChild( dependenciesElement ); // change dependencies QDomElement dataDependenciesElement = document.createElement( QStringLiteral( "dataDependencies" ) ); Q_FOREACH ( const QgsMapLayerDependency &dep, dependencies() ) { if ( dep.type() != QgsMapLayerDependency::DataDependency ) continue; QDomElement depElem = document.createElement( QStringLiteral( "layer" ) ); depElem.setAttribute( QStringLiteral( "id" ), dep.layerId() ); dataDependenciesElement.appendChild( depElem ); } layer_node.appendChild( dataDependenciesElement ); // save expression fields mExpressionFieldBuffer->writeXml( layer_node, document ); writeStyleManager( layer_node, document ); // auxiliary layer QDomElement asElem = document.createElement( QStringLiteral( "auxiliaryLayer" ) ); if ( mAuxiliaryLayer ) { const QString pkField = mAuxiliaryLayer->joinInfo().targetFieldName(); asElem.setAttribute( QStringLiteral( "key" ), pkField ); } layer_node.appendChild( asElem ); // renderer specific settings QString errorMsg; return writeSymbology( layer_node, document, errorMsg, context ); } // bool QgsVectorLayer::writeXml void QgsVectorLayer::resolveReferences( QgsProject *project ) { QgsMapLayer::resolveReferences( project ); mJoinBuffer->resolveReferences( project ); } bool QgsVectorLayer::readSymbology( const QDomNode &layerNode, QString &errorMessage, QgsReadWriteContext &context ) { QgsReadWriteContextCategoryPopper p = context.enterCategory( tr( "Symbology" ) ); if ( !mExpressionFieldBuffer ) mExpressionFieldBuffer = new QgsExpressionFieldBuffer(); mExpressionFieldBuffer->readXml( layerNode ); updateFields(); QDomElement layerElement = layerNode.toElement(); readCommonStyle( layerElement, context ); readStyle( layerNode, errorMessage, context ); mDisplayExpression = layerNode.namedItem( QStringLiteral( "previewExpression" ) ).toElement().text(); mMapTipTemplate = layerNode.namedItem( QStringLiteral( "mapTip" ) ).toElement().text(); QString displayField = layerNode.namedItem( QStringLiteral( "displayfield" ) ).toElement().text(); // Try to migrate pre QGIS 3.0 display field property if ( mFields.lookupField( displayField ) < 0 ) { // if it's not a field, it's a maptip if ( mMapTipTemplate.isEmpty() ) mMapTipTemplate = displayField; } else { if ( mDisplayExpression.isEmpty() ) mDisplayExpression = QgsExpression::quotedColumnRef( displayField ); } // process the attribute actions mActions->readXml( layerNode ); mAttributeAliasMap.clear(); QDomNode aliasesNode = layerNode.namedItem( QStringLiteral( "aliases" ) ); if ( !aliasesNode.isNull() ) { QDomElement aliasElem; QDomNodeList aliasNodeList = aliasesNode.toElement().elementsByTagName( QStringLiteral( "alias" ) ); for ( int i = 0; i < aliasNodeList.size(); ++i ) { aliasElem = aliasNodeList.at( i ).toElement(); QString field; if ( aliasElem.hasAttribute( QStringLiteral( "field" ) ) ) { field = aliasElem.attribute( QStringLiteral( "field" ) ); } else { int index = aliasElem.attribute( QStringLiteral( "index" ) ).toInt(); if ( index >= 0 && index < fields().count() ) field = fields().at( index ).name(); } mAttributeAliasMap.insert( field, aliasElem.attribute( QStringLiteral( "name" ) ) ); } } // default expressions mDefaultExpressionMap.clear(); QDomNode defaultsNode = layerNode.namedItem( QStringLiteral( "defaults" ) ); if ( !defaultsNode.isNull() ) { QDomNodeList defaultNodeList = defaultsNode.toElement().elementsByTagName( QStringLiteral( "default" ) ); for ( int i = 0; i < defaultNodeList.size(); ++i ) { QDomElement defaultElem = defaultNodeList.at( i ).toElement(); QString field = defaultElem.attribute( QStringLiteral( "field" ), QString() ); QString expression = defaultElem.attribute( QStringLiteral( "expression" ), QString() ); bool applyOnUpdate = defaultElem.attribute( QStringLiteral( "applyOnUpdate" ), QStringLiteral( "0" ) ) == QLatin1String( "1" ); if ( field.isEmpty() || expression.isEmpty() ) continue; mDefaultExpressionMap.insert( field, QgsDefaultValue( expression, applyOnUpdate ) ); } } // constraints mFieldConstraints.clear(); mFieldConstraintStrength.clear(); QDomNode constraintsNode = layerNode.namedItem( QStringLiteral( "constraints" ) ); if ( !constraintsNode.isNull() ) { QDomNodeList constraintNodeList = constraintsNode.toElement().elementsByTagName( QStringLiteral( "constraint" ) ); for ( int i = 0; i < constraintNodeList.size(); ++i ) { QDomElement constraintElem = constraintNodeList.at( i ).toElement(); QString field = constraintElem.attribute( QStringLiteral( "field" ), QString() ); int constraints = constraintElem.attribute( QStringLiteral( "constraints" ), QStringLiteral( "0" ) ).toInt(); if ( field.isEmpty() || constraints == 0 ) continue; mFieldConstraints.insert( field, static_cast< QgsFieldConstraints::Constraints >( constraints ) ); int uniqueStrength = constraintElem.attribute( QStringLiteral( "unique_strength" ), QStringLiteral( "1" ) ).toInt(); int notNullStrength = constraintElem.attribute( QStringLiteral( "notnull_strength" ), QStringLiteral( "1" ) ).toInt(); int expStrength = constraintElem.attribute( QStringLiteral( "exp_strength" ), QStringLiteral( "1" ) ).toInt(); mFieldConstraintStrength.insert( qMakePair( field, QgsFieldConstraints::ConstraintUnique ), static_cast< QgsFieldConstraints::ConstraintStrength >( uniqueStrength ) ); mFieldConstraintStrength.insert( qMakePair( field, QgsFieldConstraints::ConstraintNotNull ), static_cast< QgsFieldConstraints::ConstraintStrength >( notNullStrength ) ); mFieldConstraintStrength.insert( qMakePair( field, QgsFieldConstraints::ConstraintExpression ), static_cast< QgsFieldConstraints::ConstraintStrength >( expStrength ) ); } } mFieldConstraintExpressions.clear(); QDomNode constraintExpressionsNode = layerNode.namedItem( QStringLiteral( "constraintExpressions" ) ); if ( !constraintExpressionsNode.isNull() ) { QDomNodeList constraintNodeList = constraintExpressionsNode.toElement().elementsByTagName( QStringLiteral( "constraint" ) ); for ( int i = 0; i < constraintNodeList.size(); ++i ) { QDomElement constraintElem = constraintNodeList.at( i ).toElement(); QString field = constraintElem.attribute( QStringLiteral( "field" ), QString() ); QString exp = constraintElem.attribute( QStringLiteral( "exp" ), QString() ); QString desc = constraintElem.attribute( QStringLiteral( "desc" ), QString() ); if ( field.isEmpty() || exp.isEmpty() ) continue; mFieldConstraintExpressions.insert( field, qMakePair( exp, desc ) ); } } updateFields(); //Attributes excluded from WMS and WFS mExcludeAttributesWMS.clear(); QDomNode excludeWMSNode = layerNode.namedItem( QStringLiteral( "excludeAttributesWMS" ) ); if ( !excludeWMSNode.isNull() ) { QDomNodeList attributeNodeList = excludeWMSNode.toElement().elementsByTagName( QStringLiteral( "attribute" ) ); for ( int i = 0; i < attributeNodeList.size(); ++i ) { mExcludeAttributesWMS.insert( attributeNodeList.at( i ).toElement().text() ); } } mExcludeAttributesWFS.clear(); QDomNode excludeWFSNode = layerNode.namedItem( QStringLiteral( "excludeAttributesWFS" ) ); if ( !excludeWFSNode.isNull() ) { QDomNodeList attributeNodeList = excludeWFSNode.toElement().elementsByTagName( QStringLiteral( "attribute" ) ); for ( int i = 0; i < attributeNodeList.size(); ++i ) { mExcludeAttributesWFS.insert( attributeNodeList.at( i ).toElement().text() ); } } // Load editor widget configuration QDomElement widgetsElem = layerNode.namedItem( QStringLiteral( "fieldConfiguration" ) ).toElement(); QDomNodeList fieldConfigurationElementList = widgetsElem.elementsByTagName( QStringLiteral( "field" ) ); for ( int i = 0; i < fieldConfigurationElementList.size(); ++i ) { const QDomElement fieldConfigElement = fieldConfigurationElementList.at( i ).toElement(); const QDomElement fieldWidgetElement = fieldConfigElement.elementsByTagName( QStringLiteral( "editWidget" ) ).at( 0 ).toElement(); QString fieldName = fieldConfigElement.attribute( QStringLiteral( "name" ) ); const QString widgetType = fieldWidgetElement.attribute( QStringLiteral( "type" ) ); const QDomElement cfgElem = fieldConfigElement.elementsByTagName( QStringLiteral( "config" ) ).at( 0 ).toElement(); const QDomElement optionsElem = cfgElem.childNodes().at( 0 ).toElement(); QVariantMap optionsMap = QgsXmlUtils::readVariant( optionsElem ).toMap(); QgsEditorWidgetSetup setup = QgsEditorWidgetSetup( widgetType, optionsMap ); mFieldWidgetSetups[fieldName] = setup; } mEditFormConfig.readXml( layerNode, context ); mAttributeTableConfig.readXml( layerNode ); mConditionalStyles->readXml( layerNode, context ); readCustomProperties( layerNode, QStringLiteral( "variable" ) ); QDomElement mapLayerNode = layerNode.toElement(); if ( mapLayerNode.attribute( QStringLiteral( "readOnly" ), QStringLiteral( "0" ) ).toInt() == 1 ) mReadOnly = true; updateFields(); return true; } bool QgsVectorLayer::readStyle( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context ) { bool result = true; emit readCustomSymbology( node.toElement(), errorMessage ); if ( isSpatial() ) { // try renderer v2 first QDomElement rendererElement = node.firstChildElement( RENDERER_TAG_NAME ); if ( !rendererElement.isNull() ) { QgsFeatureRenderer *r = QgsFeatureRenderer::load( rendererElement, context ); if ( r ) { setRenderer( r ); } else { result = false; } } // make sure layer has a renderer - if none exists, fallback to a default renderer if ( !renderer() ) { setRenderer( QgsFeatureRenderer::defaultRenderer( geometryType() ) ); } // read labeling definition QDomElement labelingElement = node.firstChildElement( QStringLiteral( "labeling" ) ); QgsAbstractVectorLayerLabeling *labeling = nullptr; if ( labelingElement.isNull() || ( labelingElement.attribute( QStringLiteral( "type" ) ) == QLatin1String( "simple" ) && labelingElement.firstChildElement( QStringLiteral( "settings" ) ).isNull() ) ) { // make sure we have custom properties for labeling for 2.x projects // (custom properties should be already loaded when reading the whole layer from XML, // but when reading style, custom properties are not read) readCustomProperties( node, QStringLiteral( "labeling" ) ); // support for pre-QGIS 3 labeling configurations written in custom properties labeling = readLabelingFromCustomProperties(); } else { labeling = QgsAbstractVectorLayerLabeling::create( labelingElement, context ); } setLabeling( labeling ); if ( node.toElement().hasAttribute( QStringLiteral( "labelsEnabled" ) ) ) mLabelsEnabled = node.toElement().attribute( QStringLiteral( "labelsEnabled" ) ).toInt(); else mLabelsEnabled = true; // get and set the blend mode if it exists QDomNode blendModeNode = node.namedItem( QStringLiteral( "blendMode" ) ); if ( !blendModeNode.isNull() ) { QDomElement e = blendModeNode.toElement(); setBlendMode( QgsPainting::getCompositionMode( static_cast< QgsPainting::BlendMode >( e.text().toInt() ) ) ); } // get and set the feature blend mode if it exists QDomNode featureBlendModeNode = node.namedItem( QStringLiteral( "featureBlendMode" ) ); if ( !featureBlendModeNode.isNull() ) { QDomElement e = featureBlendModeNode.toElement(); setFeatureBlendMode( QgsPainting::getCompositionMode( static_cast< QgsPainting::BlendMode >( e.text().toInt() ) ) ); } // get and set the layer transparency if it exists QDomNode layerTransparencyNode = node.namedItem( QStringLiteral( "layerTransparency" ) ); if ( !layerTransparencyNode.isNull() ) { QDomElement e = layerTransparencyNode.toElement(); setOpacity( 1.0 - e.text().toInt() / 100.0 ); } QDomNode layerOpacityNode = node.namedItem( QStringLiteral( "layerOpacity" ) ); if ( !layerOpacityNode.isNull() ) { QDomElement e = layerOpacityNode.toElement(); setOpacity( e.text().toDouble() ); } QDomElement e = node.toElement(); // get the simplification drawing settings mSimplifyMethod.setSimplifyHints( static_cast< QgsVectorSimplifyMethod::SimplifyHints >( e.attribute( QStringLiteral( "simplifyDrawingHints" ), QStringLiteral( "1" ) ).toInt() ) ); mSimplifyMethod.setSimplifyAlgorithm( static_cast< QgsVectorSimplifyMethod::SimplifyAlgorithm >( e.attribute( QStringLiteral( "simplifyAlgorithm" ), QStringLiteral( "0" ) ).toInt() ) ); mSimplifyMethod.setThreshold( e.attribute( QStringLiteral( "simplifyDrawingTol" ), QStringLiteral( "1" ) ).toFloat() ); mSimplifyMethod.setForceLocalOptimization( e.attribute( QStringLiteral( "simplifyLocal" ), QStringLiteral( "1" ) ).toInt() ); mSimplifyMethod.setMaximumScale( e.attribute( QStringLiteral( "simplifyMaxScale" ), QStringLiteral( "1" ) ).toFloat() ); //diagram renderer and diagram layer settings delete mDiagramRenderer; mDiagramRenderer = nullptr; QDomElement singleCatDiagramElem = node.firstChildElement( QStringLiteral( "SingleCategoryDiagramRenderer" ) ); if ( !singleCatDiagramElem.isNull() ) { mDiagramRenderer = new QgsSingleCategoryDiagramRenderer(); mDiagramRenderer->readXml( singleCatDiagramElem, context ); } QDomElement linearDiagramElem = node.firstChildElement( QStringLiteral( "LinearlyInterpolatedDiagramRenderer" ) ); if ( !linearDiagramElem.isNull() ) { if ( linearDiagramElem.hasAttribute( QStringLiteral( "classificationAttribute" ) ) ) { // fix project from before QGIS 3.0 int idx = linearDiagramElem.attribute( QStringLiteral( "classificationAttribute" ) ).toInt(); if ( idx >= 0 && idx < mFields.count() ) linearDiagramElem.setAttribute( QStringLiteral( "classificationField" ), mFields.at( idx ).name() ); } mDiagramRenderer = new QgsLinearlyInterpolatedDiagramRenderer(); mDiagramRenderer->readXml( linearDiagramElem, context ); } if ( mDiagramRenderer ) { QDomElement diagramSettingsElem = node.firstChildElement( QStringLiteral( "DiagramLayerSettings" ) ); if ( !diagramSettingsElem.isNull() ) { bool oldXPos = diagramSettingsElem.hasAttribute( QStringLiteral( "xPosColumn" ) ); bool oldYPos = diagramSettingsElem.hasAttribute( QStringLiteral( "yPosColumn" ) ); bool oldShow = diagramSettingsElem.hasAttribute( QStringLiteral( "showColumn" ) ); if ( oldXPos || oldYPos || oldShow ) { // fix project from before QGIS 3.0 QgsPropertyCollection ddp; if ( oldXPos ) { int xPosColumn = diagramSettingsElem.attribute( QStringLiteral( "xPosColumn" ) ).toInt(); if ( xPosColumn >= 0 && xPosColumn < mFields.count() ) ddp.setProperty( QgsDiagramLayerSettings::PositionX, QgsProperty::fromField( mFields.at( xPosColumn ).name(), true ) ); } if ( oldYPos ) { int yPosColumn = diagramSettingsElem.attribute( QStringLiteral( "yPosColumn" ) ).toInt(); if ( yPosColumn >= 0 && yPosColumn < mFields.count() ) ddp.setProperty( QgsDiagramLayerSettings::PositionY, QgsProperty::fromField( mFields.at( yPosColumn ).name(), true ) ); } if ( oldShow ) { int showColumn = diagramSettingsElem.attribute( QStringLiteral( "showColumn" ) ).toInt(); if ( showColumn >= 0 && showColumn < mFields.count() ) ddp.setProperty( QgsDiagramLayerSettings::Show, QgsProperty::fromField( mFields.at( showColumn ).name(), true ) ); } QDomElement propertiesElem = diagramSettingsElem.ownerDocument().createElement( QStringLiteral( "properties" ) ); QgsPropertiesDefinition defs = QgsPropertiesDefinition { { QgsDiagramLayerSettings::PositionX, QgsPropertyDefinition( "positionX", QObject::tr( "Position (X)" ), QgsPropertyDefinition::Double ) }, { QgsDiagramLayerSettings::PositionY, QgsPropertyDefinition( "positionY", QObject::tr( "Position (Y)" ), QgsPropertyDefinition::Double ) }, { QgsDiagramLayerSettings::Show, QgsPropertyDefinition( "show", QObject::tr( "Show diagram" ), QgsPropertyDefinition::Boolean ) }, }; ddp.writeXml( propertiesElem, defs ); diagramSettingsElem.appendChild( propertiesElem ); } delete mDiagramLayerSettings; mDiagramLayerSettings = new QgsDiagramLayerSettings(); mDiagramLayerSettings->readXml( diagramSettingsElem ); } } } return result; } bool QgsVectorLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context ) const { QDomElement layerElement = node.toElement(); writeCommonStyle( layerElement, doc, context ); ( void )writeStyle( node, doc, errorMessage, context ); QDomElement fieldConfigurationElement = doc.createElement( QStringLiteral( "fieldConfiguration" ) ); node.appendChild( fieldConfigurationElement ); int index = 0; Q_FOREACH ( const QgsField &field, mFields ) { QDomElement fieldElement = doc.createElement( QStringLiteral( "field" ) ); fieldElement.setAttribute( QStringLiteral( "name" ), field.name() ); fieldConfigurationElement.appendChild( fieldElement ); QgsEditorWidgetSetup widgetSetup = field.editorWidgetSetup(); // TODO : wrap this part in an if to only save if it was user-modified QDomElement editWidgetElement = doc.createElement( QStringLiteral( "editWidget" ) ); fieldElement.appendChild( editWidgetElement ); editWidgetElement.setAttribute( QStringLiteral( "type" ), field.editorWidgetSetup().type() ); QDomElement editWidgetConfigElement = doc.createElement( QStringLiteral( "config" ) ); editWidgetConfigElement.appendChild( QgsXmlUtils::writeVariant( widgetSetup.config(), doc ) ); editWidgetElement.appendChild( editWidgetConfigElement ); // END TODO : wrap this part in an if to only save if it was user-modified ++index; } //attribute aliases QDomElement aliasElem = doc.createElement( QStringLiteral( "aliases" ) ); Q_FOREACH ( const QgsField &field, mFields ) { QDomElement aliasEntryElem = doc.createElement( QStringLiteral( "alias" ) ); aliasEntryElem.setAttribute( QStringLiteral( "field" ), field.name() ); aliasEntryElem.setAttribute( QStringLiteral( "index" ), mFields.indexFromName( field.name() ) ); aliasEntryElem.setAttribute( QStringLiteral( "name" ), field.alias() ); aliasElem.appendChild( aliasEntryElem ); } node.appendChild( aliasElem ); //exclude attributes WMS QDomElement excludeWMSElem = doc.createElement( QStringLiteral( "excludeAttributesWMS" ) ); QSet::const_iterator attWMSIt = mExcludeAttributesWMS.constBegin(); for ( ; attWMSIt != mExcludeAttributesWMS.constEnd(); ++attWMSIt ) { QDomElement attrElem = doc.createElement( QStringLiteral( "attribute" ) ); QDomText attrText = doc.createTextNode( *attWMSIt ); attrElem.appendChild( attrText ); excludeWMSElem.appendChild( attrElem ); } node.appendChild( excludeWMSElem ); //exclude attributes WFS QDomElement excludeWFSElem = doc.createElement( QStringLiteral( "excludeAttributesWFS" ) ); QSet::const_iterator attWFSIt = mExcludeAttributesWFS.constBegin(); for ( ; attWFSIt != mExcludeAttributesWFS.constEnd(); ++attWFSIt ) { QDomElement attrElem = doc.createElement( QStringLiteral( "attribute" ) ); QDomText attrText = doc.createTextNode( *attWFSIt ); attrElem.appendChild( attrText ); excludeWFSElem.appendChild( attrElem ); } node.appendChild( excludeWFSElem ); //default expressions QDomElement defaultsElem = doc.createElement( QStringLiteral( "defaults" ) ); Q_FOREACH ( const QgsField &field, mFields ) { QDomElement defaultElem = doc.createElement( QStringLiteral( "default" ) ); defaultElem.setAttribute( QStringLiteral( "field" ), field.name() ); defaultElem.setAttribute( QStringLiteral( "expression" ), field.defaultValueDefinition().expression() ); defaultElem.setAttribute( QStringLiteral( "applyOnUpdate" ), field.defaultValueDefinition().applyOnUpdate() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ); defaultsElem.appendChild( defaultElem ); } node.appendChild( defaultsElem ); // constraints QDomElement constraintsElem = doc.createElement( QStringLiteral( "constraints" ) ); Q_FOREACH ( const QgsField &field, mFields ) { QDomElement constraintElem = doc.createElement( QStringLiteral( "constraint" ) ); constraintElem.setAttribute( QStringLiteral( "field" ), field.name() ); constraintElem.setAttribute( QStringLiteral( "constraints" ), field.constraints().constraints() ); constraintElem.setAttribute( QStringLiteral( "unique_strength" ), field.constraints().constraintStrength( QgsFieldConstraints::ConstraintUnique ) ); constraintElem.setAttribute( QStringLiteral( "notnull_strength" ), field.constraints().constraintStrength( QgsFieldConstraints::ConstraintNotNull ) ); constraintElem.setAttribute( QStringLiteral( "exp_strength" ), field.constraints().constraintStrength( QgsFieldConstraints::ConstraintExpression ) ); constraintsElem.appendChild( constraintElem ); } node.appendChild( constraintsElem ); // constraint expressions QDomElement constraintExpressionsElem = doc.createElement( QStringLiteral( "constraintExpressions" ) ); Q_FOREACH ( const QgsField &field, mFields ) { QDomElement constraintExpressionElem = doc.createElement( QStringLiteral( "constraint" ) ); constraintExpressionElem.setAttribute( QStringLiteral( "field" ), field.name() ); constraintExpressionElem.setAttribute( QStringLiteral( "exp" ), field.constraints().constraintExpression() ); constraintExpressionElem.setAttribute( QStringLiteral( "desc" ), field.constraints().constraintDescription() ); constraintExpressionsElem.appendChild( constraintExpressionElem ); } node.appendChild( constraintExpressionsElem ); // add attribute actions mActions->writeXml( node ); mAttributeTableConfig.writeXml( node ); mEditFormConfig.writeXml( node, context ); mConditionalStyles->writeXml( node, doc, context ); // save expression fields if ( !mExpressionFieldBuffer ) { // can happen when saving style on a invalid layer QgsExpressionFieldBuffer dummy; dummy.writeXml( node, doc ); } else mExpressionFieldBuffer->writeXml( node, doc ); // save readonly state node.toElement().setAttribute( QStringLiteral( "readOnly" ), mReadOnly ); // save preview expression QDomElement prevExpElem = doc.createElement( QStringLiteral( "previewExpression" ) ); QDomText prevExpText = doc.createTextNode( mDisplayExpression ); prevExpElem.appendChild( prevExpText ); node.appendChild( prevExpElem ); // save map tip QDomElement mapTipElem = doc.createElement( QStringLiteral( "mapTip" ) ); QDomText mapTipText = doc.createTextNode( mMapTipTemplate ); mapTipElem.appendChild( mapTipText ); node.toElement().appendChild( mapTipElem ); return true; } bool QgsVectorLayer::writeStyle( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context ) const { QDomElement mapLayerNode = node.toElement(); emit writeCustomSymbology( mapLayerNode, doc, errorMessage ); if ( isSpatial() ) { if ( mRenderer ) { QDomElement rendererElement = mRenderer->save( doc, context ); node.appendChild( rendererElement ); } if ( mLabeling ) { QDomElement labelingElement = mLabeling->save( doc, context ); node.appendChild( labelingElement ); } mapLayerNode.setAttribute( QStringLiteral( "labelsEnabled" ), mLabelsEnabled ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ); // save the simplification drawing settings mapLayerNode.setAttribute( QStringLiteral( "simplifyDrawingHints" ), QString::number( mSimplifyMethod.simplifyHints() ) ); mapLayerNode.setAttribute( QStringLiteral( "simplifyAlgorithm" ), QString::number( mSimplifyMethod.simplifyAlgorithm() ) ); mapLayerNode.setAttribute( QStringLiteral( "simplifyDrawingTol" ), QString::number( mSimplifyMethod.threshold() ) ); mapLayerNode.setAttribute( QStringLiteral( "simplifyLocal" ), mSimplifyMethod.forceLocalOptimization() ? 1 : 0 ); mapLayerNode.setAttribute( QStringLiteral( "simplifyMaxScale" ), QString::number( mSimplifyMethod.maximumScale() ) ); //save customproperties writeCustomProperties( node, doc ); // add the blend mode field QDomElement blendModeElem = doc.createElement( QStringLiteral( "blendMode" ) ); QDomText blendModeText = doc.createTextNode( QString::number( QgsPainting::getBlendModeEnum( blendMode() ) ) ); blendModeElem.appendChild( blendModeText ); node.appendChild( blendModeElem ); // add the feature blend mode field QDomElement featureBlendModeElem = doc.createElement( QStringLiteral( "featureBlendMode" ) ); QDomText featureBlendModeText = doc.createTextNode( QString::number( QgsPainting::getBlendModeEnum( featureBlendMode() ) ) ); featureBlendModeElem.appendChild( featureBlendModeText ); node.appendChild( featureBlendModeElem ); // add the layer opacity QDomElement layerOpacityElem = doc.createElement( QStringLiteral( "layerOpacity" ) ); QDomText layerOpacityText = doc.createTextNode( QString::number( opacity() ) ); layerOpacityElem.appendChild( layerOpacityText ); node.appendChild( layerOpacityElem ); if ( mDiagramRenderer ) { mDiagramRenderer->writeXml( mapLayerNode, doc, context ); if ( mDiagramLayerSettings ) mDiagramLayerSettings->writeXml( mapLayerNode, doc ); } } return true; } bool QgsVectorLayer::readSld( const QDomNode &node, QString &errorMessage ) { // get the Name element QDomElement nameElem = node.firstChildElement( QStringLiteral( "Name" ) ); if ( nameElem.isNull() ) { errorMessage = QStringLiteral( "Warning: Name element not found within NamedLayer while it's required." ); } if ( isSpatial() ) { QgsFeatureRenderer *r = QgsFeatureRenderer::loadSld( node, geometryType(), errorMessage ); if ( !r ) return false; setRenderer( r ); // labeling readSldLabeling( node ); } return true; } bool QgsVectorLayer::writeSld( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsStringMap &props ) const { Q_UNUSED( errorMessage ); QgsStringMap localProps = QgsStringMap( props ); if ( hasScaleBasedVisibility() ) { QgsSymbolLayerUtils::mergeScaleDependencies( maximumScale(), minimumScale(), localProps ); } if ( isSpatial() ) { // store the Name element QDomElement nameNode = doc.createElement( QStringLiteral( "se:Name" ) ); nameNode.appendChild( doc.createTextNode( name() ) ); node.appendChild( nameNode ); QDomElement userStyleElem = doc.createElement( QStringLiteral( "UserStyle" ) ); node.appendChild( userStyleElem ); QDomElement nameElem = doc.createElement( QStringLiteral( "se:Name" ) ); nameElem.appendChild( doc.createTextNode( name() ) ); userStyleElem.appendChild( nameElem ); QDomElement featureTypeStyleElem = doc.createElement( QStringLiteral( "se:FeatureTypeStyle" ) ); userStyleElem.appendChild( featureTypeStyleElem ); mRenderer->toSld( doc, featureTypeStyleElem, localProps ); if ( labelsEnabled() ) { mLabeling->toSld( featureTypeStyleElem, localProps ); } } return true; } bool QgsVectorLayer::changeGeometry( QgsFeatureId fid, const QgsGeometry &geom, bool skipDefaultValue ) { if ( !mEditBuffer || !mDataProvider ) { return false; } updateExtents(); bool result = mEditBuffer->changeGeometry( fid, geom ); if ( result ) { updateExtents(); if ( !skipDefaultValue && !mDefaultValueOnUpdateFields.isEmpty() ) updateDefaultValues( fid ); } return result; } bool QgsVectorLayer::changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue, bool skipDefaultValues ) { bool result = false; switch ( fields().fieldOrigin( field ) ) { case QgsFields::OriginJoin: result = mJoinBuffer->changeAttributeValue( fid, field, newValue, oldValue ); break; case QgsFields::OriginProvider: case QgsFields::OriginEdit: case QgsFields::OriginExpression: { if ( mEditBuffer && mDataProvider ) result = mEditBuffer->changeAttributeValue( fid, field, newValue, oldValue ); break; } case QgsFields::OriginUnknown: break; } if ( result && !skipDefaultValues && !mDefaultValueOnUpdateFields.isEmpty() ) updateDefaultValues( fid ); return result; } bool QgsVectorLayer::changeAttributeValues( QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues, bool skipDefaultValues ) { bool result = true; QgsAttributeMap newValuesJoin; QgsAttributeMap oldValuesJoin; QgsAttributeMap newValuesNotJoin; QgsAttributeMap oldValuesNotJoin; for ( auto it = newValues.constBegin(); it != newValues.constEnd(); ++it ) { const int field = it.key(); const QVariant newValue = it.value(); QVariant oldValue; if ( oldValues.contains( field ) ) oldValue = oldValues[field]; switch ( fields().fieldOrigin( field ) ) { case QgsFields::OriginJoin: newValuesJoin[field] = newValue; oldValuesJoin[field] = oldValue; break; case QgsFields::OriginProvider: case QgsFields::OriginEdit: case QgsFields::OriginExpression: { newValuesNotJoin[field] = newValue; oldValuesNotJoin[field] = oldValue; break; } case QgsFields::OriginUnknown: break; } } if ( ! newValuesJoin.isEmpty() && mJoinBuffer ) { result = mJoinBuffer->changeAttributeValues( fid, newValuesJoin, oldValuesJoin ); } if ( ! newValuesNotJoin.isEmpty() && mEditBuffer && mDataProvider ) { result &= mEditBuffer->changeAttributeValues( fid, newValues, oldValues ); } if ( result && !skipDefaultValues && !mDefaultValueOnUpdateFields.isEmpty() ) { updateDefaultValues( fid ); } return result; } bool QgsVectorLayer::addAttribute( const QgsField &field ) { if ( !mEditBuffer || !mDataProvider ) return false; return mEditBuffer->addAttribute( field ); } void QgsVectorLayer::removeFieldAlias( int attIndex ) { if ( attIndex < 0 || attIndex >= fields().count() ) return; QString name = fields().at( attIndex ).name(); mFields[ attIndex ].setAlias( QString() ); if ( mAttributeAliasMap.contains( name ) ) { mAttributeAliasMap.remove( name ); updateFields(); mEditFormConfig.setFields( mFields ); emit layerModified(); } } bool QgsVectorLayer::renameAttribute( int index, const QString &newName ) { if ( index < 0 || index >= fields().count() ) return false; switch ( mFields.fieldOrigin( index ) ) { case QgsFields::OriginExpression: { if ( mExpressionFieldBuffer ) { int oi = mFields.fieldOriginIndex( index ); mExpressionFieldBuffer->renameExpression( oi, newName ); updateFields(); return true; } else { return false; } } case QgsFields::OriginProvider: case QgsFields::OriginEdit: if ( !mEditBuffer || !mDataProvider ) return false; return mEditBuffer->renameAttribute( index, newName ); case QgsFields::OriginJoin: case QgsFields::OriginUnknown: return false; } return false; // avoid warning } void QgsVectorLayer::setFieldAlias( int attIndex, const QString &aliasString ) { if ( attIndex < 0 || attIndex >= fields().count() ) return; QString name = fields().at( attIndex ).name(); mAttributeAliasMap.insert( name, aliasString ); mFields[ attIndex ].setAlias( aliasString ); mEditFormConfig.setFields( mFields ); emit layerModified(); // TODO[MD]: should have a different signal? } QString QgsVectorLayer::attributeAlias( int index ) const { if ( index < 0 || index >= fields().count() ) return QString(); return fields().at( index ).alias(); } QString QgsVectorLayer::attributeDisplayName( int index ) const { if ( index >= 0 && index < mFields.count() ) return mFields.at( index ).displayName(); else return QString(); } QgsStringMap QgsVectorLayer::attributeAliases() const { QgsStringMap map; Q_FOREACH ( const QgsField &field, fields() ) { if ( !field.alias().isEmpty() ) map.insert( field.name(), field.alias() ); } return map; } bool QgsVectorLayer::deleteAttribute( int index ) { if ( index < 0 || index >= fields().count() ) return false; if ( mFields.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(); std::sort( attrs.begin(), attrs.end(), std::greater() ); Q_FOREACH ( int attr, attrs ) { if ( deleteAttribute( attr ) ) { deleted = true; } } return deleted; } bool QgsVectorLayer::deleteFeature( QgsFeatureId fid ) { if ( !mEditBuffer ) return false; if ( mJoinBuffer->containsJoins() ) mJoinBuffer->deleteFeature( fid ); 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 ) { QgsDebugMsgLevel( QStringLiteral( "Cannot delete features (mEditBuffer==NULL)" ), 1 ); return false; } if ( mJoinBuffer->containsJoins() ) mJoinBuffer->deleteFeatures( fids ); bool res = mEditBuffer->deleteFeatures( fids ); if ( res ) { mSelectedFeatureIds.subtract( fids ); // remove it from selection updateExtents(); } return res; } QgsAttributeList QgsVectorLayer::primaryKeyAttributes() const { QgsAttributeList pkAttributesList; QgsAttributeList providerIndexes = mDataProvider->pkAttributeIndexes(); for ( int i = 0; i < mFields.count(); ++i ) { if ( mFields.fieldOrigin( i ) == QgsFields::OriginProvider && providerIndexes.contains( mFields.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( QStringLiteral( "\n " ) ) ) ); } updateFields(); mDataProvider->updateExtents(); mDataProvider->leaveUpdateMode(); emit repaintRequested(); return success; } QStringList QgsVectorLayer::commitErrors() const { 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 ( rollbackExtent ) updateExtents(); mDataProvider->leaveUpdateMode(); emit repaintRequested(); return true; } int QgsVectorLayer::selectedFeatureCount() const { return mSelectedFeatureIds.size(); } const QgsFeatureIds &QgsVectorLayer::selectedFeatureIds() const { return mSelectedFeatureIds; } QgsFeatureList QgsVectorLayer::selectedFeatures() const { 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 = getSelectedFeatures(); while ( it.nextFeature( f ) ) { features.push_back( f ); } } return features; } QgsFeatureIterator QgsVectorLayer::getSelectedFeatures( QgsFeatureRequest request ) const { if ( mSelectedFeatureIds.isEmpty() ) return QgsFeatureIterator(); if ( geometryType() == QgsWkbTypes::NullGeometry ) request.setFlags( QgsFeatureRequest::NoGeometry ); if ( mSelectedFeatureIds.count() == 1 ) request.setFilterFid( *mSelectedFeatureIds.constBegin() ); else request.setFilterFids( mSelectedFeatureIds ); return getFeatures( request ); } bool QgsVectorLayer::addFeatures( QgsFeatureList &features, Flags ) { if ( !mEditBuffer || !mDataProvider ) return false; bool res = mEditBuffer->addFeatures( features ); updateExtents(); if ( res && mJoinBuffer->containsJoins() ) res = mJoinBuffer->addFeatures( features ); return res; } void QgsVectorLayer::setCoordinateSystem() { QgsDebugMsgLevel( QStringLiteral( "Computing Coordinate System" ), 4 ); if ( isSpatial() ) { // get CRS directly from provider setCrs( mDataProvider->crs() ); } else { setCrs( QgsCoordinateReferenceSystem( GEO_EPSG_CRS_AUTHID ) ); } } QString QgsVectorLayer::displayField() const { QgsExpression exp( mDisplayExpression ); if ( exp.isField() ) { return static_cast( exp.rootNode() )->name(); } return QString(); } void QgsVectorLayer::setDisplayExpression( const QString &displayExpression ) { if ( mDisplayExpression == displayExpression ) return; mDisplayExpression = displayExpression; emit displayExpressionChanged(); } QString QgsVectorLayer::displayExpression() const { if ( !mDisplayExpression.isEmpty() || mFields.isEmpty() ) { return mDisplayExpression; } else { QString idxName; Q_FOREACH ( const QgsField &field, mFields ) { QString fldName = field.name(); // 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( QLatin1String( "name" ), 0, Qt::CaseInsensitive ) > -1 ) { idxName = fldName; break; } if ( fldName.indexOf( QLatin1String( "descrip" ), 0, Qt::CaseInsensitive ) > -1 ) { idxName = fldName; break; } if ( fldName.indexOf( QLatin1String( "id" ), 0, Qt::CaseInsensitive ) > -1 ) { idxName = fldName; break; } } if ( !idxName.isNull() ) { return QgsExpression::quotedColumnRef( idxName ); } else { return QgsExpression::quotedColumnRef( mFields.at( 0 ).name() ); } } } bool QgsVectorLayer::isEditable() const { return ( mEditBuffer && mDataProvider ); } bool QgsVectorLayer::isSpatial() const { QgsWkbTypes::GeometryType t = geometryType(); return t != QgsWkbTypes::NullGeometry && t != QgsWkbTypes::UnknownGeometry; } 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; emit readOnlyChanged(); return true; } bool QgsVectorLayer::isModified() const { emit beforeModifiedCheck(); return mEditBuffer && mEditBuffer->isModified(); } bool QgsVectorLayer::isAuxiliaryField( int index, int &srcIndex ) const { bool auxiliaryField = false; srcIndex = -1; if ( !auxiliaryLayer() ) return auxiliaryField; if ( index >= 0 && fields().fieldOrigin( index ) == QgsFields::OriginJoin ) { const QgsVectorLayerJoinInfo *info = mJoinBuffer->joinForFieldIndex( index, fields(), srcIndex ); if ( info && info->joinLayerId() == auxiliaryLayer()->id() ) auxiliaryField = true; } return auxiliaryField; } void QgsVectorLayer::setRenderer( QgsFeatureRenderer *r ) { if ( !isSpatial() ) return; if ( r != mRenderer ) { delete mRenderer; mRenderer = r; mSymbolFeatureCounted = false; mSymbolFeatureCountMap.clear(); emit rendererChanged(); emit styleChanged(); } } void QgsVectorLayer::beginEditCommand( const QString &text ) { if ( !mDataProvider ) { return; } if ( mDataProvider->transaction() ) { QString ignoredError; mDataProvider->transaction()->createSavepoint( ignoredError ); } undoStack()->beginMacro( text ); mEditCommandActive = true; emit editCommandStarted( text ); } void QgsVectorLayer::endEditCommand() { if ( !mDataProvider ) { return; } undoStack()->endMacro(); mEditCommandActive = false; if ( !mDeletedFids.isEmpty() ) { emit featuresDeleted( mDeletedFids ); mDeletedFids.clear(); } emit editCommandEnded(); } void QgsVectorLayer::destroyEditCommand() { if ( !mDataProvider ) { return; } undoStack()->endMacro(); undoStack()->undo(); mEditCommandActive = false; mDeletedFids.clear(); emit editCommandDestroyed(); } bool QgsVectorLayer::addJoin( const QgsVectorLayerJoinInfo &joinInfo ) { return mJoinBuffer->addJoin( joinInfo ); } bool QgsVectorLayer::removeJoin( const QString &joinLayerId ) { return mJoinBuffer->removeJoin( joinLayerId ); } const QList< QgsVectorLayerJoinInfo > QgsVectorLayer::vectorJoins() const { return mJoinBuffer->vectorJoins(); } int QgsVectorLayer::addExpressionField( const QString &exp, const QgsField &fld ) { emit beforeAddingExpressionField( fld.name() ); mExpressionFieldBuffer->addExpression( exp, fld ); updateFields(); int idx = mFields.indexFromName( fld.name() ); emit attributeAdded( idx ); return idx; } void QgsVectorLayer::removeExpressionField( int index ) { emit beforeRemovingExpressionField( index ); int oi = mFields.fieldOriginIndex( index ); mExpressionFieldBuffer->removeExpression( oi ); updateFields(); emit attributeDeleted( index ); } QString QgsVectorLayer::expressionField( int index ) const { int oi = mFields.fieldOriginIndex( index ); if ( oi < 0 || oi >= mExpressionFieldBuffer->expressions().size() ) return QString(); return mExpressionFieldBuffer->expressions().at( oi ).cachedExpression.expression(); } void QgsVectorLayer::updateExpressionField( int index, const QString &exp ) { int oi = mFields.fieldOriginIndex( index ); mExpressionFieldBuffer->updateExpression( oi, exp ); } void QgsVectorLayer::updateFields() { if ( !mDataProvider ) return; QgsFields oldFields = mFields; mFields = mDataProvider->fields(); // added / removed fields if ( mEditBuffer ) mEditBuffer->updateFields( mFields ); // joined fields if ( mJoinBuffer->containsJoins() ) mJoinBuffer->updateFields( mFields ); if ( mExpressionFieldBuffer ) mExpressionFieldBuffer->updateFields( mFields ); // set aliases and default values QMap< QString, QString >::const_iterator aliasIt = mAttributeAliasMap.constBegin(); for ( ; aliasIt != mAttributeAliasMap.constEnd(); ++aliasIt ) { int index = mFields.lookupField( aliasIt.key() ); if ( index < 0 ) continue; mFields[ index ].setAlias( aliasIt.value() ); } // Update default values mDefaultValueOnUpdateFields.clear(); QMap< QString, QgsDefaultValue >::const_iterator defaultIt = mDefaultExpressionMap.constBegin(); for ( ; defaultIt != mDefaultExpressionMap.constEnd(); ++defaultIt ) { int index = mFields.lookupField( defaultIt.key() ); if ( index < 0 ) continue; mFields[ index ].setDefaultValueDefinition( defaultIt.value() ); if ( defaultIt.value().applyOnUpdate() ) mDefaultValueOnUpdateFields.insert( index ); } QMap< QString, QgsFieldConstraints::Constraints >::const_iterator constraintIt = mFieldConstraints.constBegin(); for ( ; constraintIt != mFieldConstraints.constEnd(); ++constraintIt ) { int index = mFields.lookupField( constraintIt.key() ); if ( index < 0 ) continue; QgsFieldConstraints constraints = mFields.at( index ).constraints(); // always keep provider constraints intact if ( !( constraints.constraints() & QgsFieldConstraints::ConstraintNotNull ) && ( constraintIt.value() & QgsFieldConstraints::ConstraintNotNull ) ) constraints.setConstraint( QgsFieldConstraints::ConstraintNotNull, QgsFieldConstraints::ConstraintOriginLayer ); if ( !( constraints.constraints() & QgsFieldConstraints::ConstraintUnique ) && ( constraintIt.value() & QgsFieldConstraints::ConstraintUnique ) ) constraints.setConstraint( QgsFieldConstraints::ConstraintUnique, QgsFieldConstraints::ConstraintOriginLayer ); if ( !( constraints.constraints() & QgsFieldConstraints::ConstraintExpression ) && ( constraintIt.value() & QgsFieldConstraints::ConstraintExpression ) ) constraints.setConstraint( QgsFieldConstraints::ConstraintExpression, QgsFieldConstraints::ConstraintOriginLayer ); mFields[ index ].setConstraints( constraints ); } QMap< QString, QPair< QString, QString > >::const_iterator constraintExpIt = mFieldConstraintExpressions.constBegin(); for ( ; constraintExpIt != mFieldConstraintExpressions.constEnd(); ++constraintExpIt ) { int index = mFields.lookupField( constraintExpIt.key() ); if ( index < 0 ) continue; QgsFieldConstraints constraints = mFields.at( index ).constraints(); // always keep provider constraints intact if ( constraints.constraintOrigin( QgsFieldConstraints::ConstraintExpression ) == QgsFieldConstraints::ConstraintOriginProvider ) continue; constraints.setConstraintExpression( constraintExpIt.value().first, constraintExpIt.value().second ); mFields[ index ].setConstraints( constraints ); } QMap< QPair< QString, QgsFieldConstraints::Constraint >, QgsFieldConstraints::ConstraintStrength >::const_iterator constraintStrengthIt = mFieldConstraintStrength.constBegin(); for ( ; constraintStrengthIt != mFieldConstraintStrength.constEnd(); ++constraintStrengthIt ) { int index = mFields.lookupField( constraintStrengthIt.key().first ); if ( index < 0 ) continue; QgsFieldConstraints constraints = mFields.at( index ).constraints(); // always keep provider constraints intact if ( constraints.constraintOrigin( QgsFieldConstraints::ConstraintExpression ) == QgsFieldConstraints::ConstraintOriginProvider ) continue; constraints.setConstraintStrength( constraintStrengthIt.key().second, constraintStrengthIt.value() ); mFields[ index ].setConstraints( constraints ); } auto fieldWidgetIterator = mFieldWidgetSetups.constBegin(); for ( ; fieldWidgetIterator != mFieldWidgetSetups.constEnd(); ++ fieldWidgetIterator ) { int index = mFields.indexOf( fieldWidgetIterator.key() ); if ( index < 0 ) continue; mFields[index].setEditorWidgetSetup( fieldWidgetIterator.value() ); } if ( oldFields != mFields ) { emit updatedFields(); mEditFormConfig.setFields( mFields ); } } QVariant QgsVectorLayer::defaultValue( int index, const QgsFeature &feature, QgsExpressionContext *context ) const { if ( index < 0 || index >= mFields.count() ) return QVariant(); QString expression = mFields.at( index ).defaultValueDefinition().expression(); if ( expression.isEmpty() ) return mDataProvider->defaultValue( index ); QgsExpressionContext *evalContext = context; std::unique_ptr< QgsExpressionContext > tempContext; if ( !evalContext ) { // no context passed, so we create a default one tempContext.reset( new QgsExpressionContext( QgsExpressionContextUtils::globalProjectLayerScopes( this ) ) ); evalContext = tempContext.get(); } if ( feature.isValid() ) { QgsExpressionContextScope *featScope = new QgsExpressionContextScope(); featScope->setFeature( feature ); featScope->setFields( feature.fields() ); evalContext->appendScope( featScope ); } QVariant val; QgsExpression exp( expression ); exp.prepare( evalContext ); if ( exp.hasEvalError() ) { QgsLogger::warning( "Error evaluating default value: " + exp.evalErrorString() ); } else { val = exp.evaluate( evalContext ); } if ( feature.isValid() ) { delete evalContext->popScope(); } return val; } void QgsVectorLayer::setDefaultValueDefinition( int index, const QgsDefaultValue &definition ) { if ( index < 0 || index >= mFields.count() ) return; if ( definition.isValid() ) { mDefaultExpressionMap.insert( mFields.at( index ).name(), definition ); } else { mDefaultExpressionMap.remove( mFields.at( index ).name() ); } updateFields(); } QgsDefaultValue QgsVectorLayer::defaultValueDefinition( int index ) const { if ( index < 0 || index >= mFields.count() ) return QgsDefaultValue(); else return mFields.at( index ).defaultValueDefinition(); } QSet QgsVectorLayer::uniqueValues( int index, int limit ) const { QSet uniqueValues; if ( !mDataProvider ) { return uniqueValues; } QgsFields::FieldOrigin origin = mFields.fieldOrigin( index ); switch ( origin ) { case QgsFields::OriginUnknown: return uniqueValues; case QgsFields::OriginProvider: //a provider field { uniqueValues = mDataProvider->uniqueValues( index, limit ); if ( mEditBuffer ) { QSet vals; Q_FOREACH ( const QVariant &v, uniqueValues ) { vals << v.toString(); } QgsFeatureMap added = mEditBuffer->addedFeatures(); QMapIterator< QgsFeatureId, QgsFeature > addedIt( added ); while ( addedIt.hasNext() && ( limit < 0 || uniqueValues.count() < limit ) ) { addedIt.next(); QVariant v = addedIt.value().attribute( index ); if ( v.isValid() ) { QString vs = v.toString(); if ( !vals.contains( vs ) ) { vals << vs; uniqueValues << v; } } } 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 uniqueValues; } case QgsFields::OriginEdit: // the layer is editable, but in certain cases it can still be avoided going through all features if ( mEditBuffer->mDeletedFeatureIds.isEmpty() && mEditBuffer->mAddedFeatures.isEmpty() && !mEditBuffer->mDeletedAttributeIds.contains( index ) && mEditBuffer->mChangedAttributeValues.isEmpty() ) { uniqueValues = mDataProvider->uniqueValues( index, limit ); return uniqueValues; } FALLTHROUGH; //we need to go through each feature case QgsFields::OriginJoin: case QgsFields::OriginExpression: { 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; } } return val.values().toSet(); } } Q_ASSERT_X( false, "QgsVectorLayer::uniqueValues()", "Unknown source of the field!" ); return uniqueValues; } QStringList QgsVectorLayer::uniqueStringsMatching( int index, const QString &substring, int limit, QgsFeedback *feedback ) const { QStringList results; if ( !mDataProvider ) { return results; } QgsFields::FieldOrigin origin = mFields.fieldOrigin( index ); switch ( origin ) { case QgsFields::OriginUnknown: return results; case QgsFields::OriginProvider: //a provider field { results = mDataProvider->uniqueStringsMatching( index, substring, limit, feedback ); if ( mEditBuffer ) { QgsFeatureMap added = mEditBuffer->addedFeatures(); QMapIterator< QgsFeatureId, QgsFeature > addedIt( added ); while ( addedIt.hasNext() && ( limit < 0 || results.count() < limit ) && ( !feedback || !feedback->isCanceled() ) ) { addedIt.next(); QVariant v = addedIt.value().attribute( index ); if ( v.isValid() ) { QString vs = v.toString(); if ( vs.contains( substring, Qt::CaseInsensitive ) && !results.contains( vs ) ) { results << vs; } } } QMapIterator< QgsFeatureId, QgsAttributeMap > it( mEditBuffer->changedAttributeValues() ); while ( it.hasNext() && ( limit < 0 || results.count() < limit ) && ( !feedback || !feedback->isCanceled() ) ) { it.next(); QVariant v = it.value().value( index ); if ( v.isValid() ) { QString vs = v.toString(); if ( vs.contains( substring, Qt::CaseInsensitive ) && !results.contains( vs ) ) { results << vs; } } } } return results; } case QgsFields::OriginEdit: // the layer is editable, but in certain cases it can still be avoided going through all features if ( mEditBuffer->mDeletedFeatureIds.isEmpty() && mEditBuffer->mAddedFeatures.isEmpty() && !mEditBuffer->mDeletedAttributeIds.contains( index ) && mEditBuffer->mChangedAttributeValues.isEmpty() ) { return mDataProvider->uniqueStringsMatching( index, substring, limit, feedback ); } FALLTHROUGH; //we need to go through each feature case QgsFields::OriginJoin: case QgsFields::OriginExpression: { QgsAttributeList attList; attList << index; QgsFeatureRequest request; request.setSubsetOfAttributes( attList ); request.setFlags( QgsFeatureRequest::NoGeometry ); QString fieldName = mFields.at( index ).name(); request.setFilterExpression( QStringLiteral( "\"%1\" ILIKE '%%2%'" ).arg( fieldName, substring ) ); QgsFeatureIterator fit = getFeatures( request ); QgsFeature f; QString currentValue; while ( fit.nextFeature( f ) ) { currentValue = f.attribute( index ).toString(); if ( !results.contains( currentValue ) ) results << currentValue; if ( ( limit >= 0 && results.size() >= limit ) || ( feedback && feedback->isCanceled() ) ) { break; } } return results; } } Q_ASSERT_X( false, "QgsVectorLayer::uniqueStringsMatching()", "Unknown source of the field!" ); return results; } QVariant QgsVectorLayer::minimumValue( int index ) const { if ( !mDataProvider ) { return QVariant(); } QgsFields::FieldOrigin origin = mFields.fieldOrigin( index ); switch ( origin ) { case QgsFields::OriginUnknown: return QVariant(); case QgsFields::OriginProvider: //a provider field { QVariant min = mDataProvider->minimumValue( index ); if ( mEditBuffer ) { QgsFeatureMap added = mEditBuffer->addedFeatures(); QMapIterator< QgsFeatureId, QgsFeature > addedIt( added ); while ( addedIt.hasNext() ) { addedIt.next(); QVariant v = addedIt.value().attribute( index ); if ( v.isValid() && qgsVariantLessThan( v, min ) ) { min = v; } } QMapIterator< QgsFeatureId, QgsAttributeMap > it( mEditBuffer->changedAttributeValues() ); while ( it.hasNext() ) { it.next(); QVariant v = it.value().value( index ); if ( v.isValid() && qgsVariantLessThan( v, min ) ) { min = v; } } } return min; } case QgsFields::OriginEdit: { // the layer is editable, but in certain cases it can still be avoided going through all features if ( mEditBuffer->mDeletedFeatureIds.isEmpty() && mEditBuffer->mAddedFeatures.isEmpty() && ! mEditBuffer->mDeletedAttributeIds.contains( index ) && mEditBuffer->mChangedAttributeValues.isEmpty() ) { return mDataProvider->minimumValue( index ); } } FALLTHROUGH; // no choice but to go through all features case QgsFields::OriginExpression: case QgsFields::OriginJoin: { // 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 ) const { if ( !mDataProvider ) { return QVariant(); } QgsFields::FieldOrigin origin = mFields.fieldOrigin( index ); switch ( origin ) { case QgsFields::OriginUnknown: return QVariant(); case QgsFields::OriginProvider: //a provider field { QVariant min = mDataProvider->maximumValue( index ); if ( mEditBuffer ) { QgsFeatureMap added = mEditBuffer->addedFeatures(); QMapIterator< QgsFeatureId, QgsFeature > addedIt( added ); while ( addedIt.hasNext() ) { addedIt.next(); QVariant v = addedIt.value().attribute( index ); if ( v.isValid() && qgsVariantGreaterThan( v, min ) ) { min = v; } } QMapIterator< QgsFeatureId, QgsAttributeMap > it( mEditBuffer->changedAttributeValues() ); while ( it.hasNext() ) { it.next(); QVariant v = it.value().value( index ); if ( v.isValid() && qgsVariantGreaterThan( v, min ) ) { min = v; } } } return min; } case QgsFields::OriginEdit: // the layer is editable, but in certain cases it can still be avoided going through all features if ( mEditBuffer->mDeletedFeatureIds.isEmpty() && mEditBuffer->mAddedFeatures.isEmpty() && !mEditBuffer->mDeletedAttributeIds.contains( index ) && mEditBuffer->mChangedAttributeValues.isEmpty() ) { return mDataProvider->maximumValue( index ); } FALLTHROUGH; //no choice but to go through each feature case QgsFields::OriginJoin: case QgsFields::OriginExpression: { 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 ¶meters, QgsExpressionContext *context, bool *ok ) const { if ( ok ) *ok = false; if ( !mDataProvider ) { return QVariant(); } // test if we are calculating based on a field int attrIndex = mFields.lookupField( 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 = mFields.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 ); } void QgsVectorLayer::setFeatureBlendMode( QPainter::CompositionMode featureBlendMode ) { mFeatureBlendMode = featureBlendMode; emit featureBlendModeChanged( featureBlendMode ); emit styleChanged(); } QPainter::CompositionMode QgsVectorLayer::featureBlendMode() const { return mFeatureBlendMode; } void QgsVectorLayer::setOpacity( double opacity ) { mLayerOpacity = opacity; emit opacityChanged( opacity ); emit styleChanged(); } double QgsVectorLayer::opacity() const { return mLayerOpacity; } void QgsVectorLayer::readSldLabeling( const QDomNode &node ) { setLabeling( nullptr ); // start with no labeling setLabelsEnabled( false ); QDomElement element = node.toElement(); if ( element.isNull() ) return; QDomElement userStyleElem = element.firstChildElement( QStringLiteral( "UserStyle" ) ); if ( userStyleElem.isNull() ) { QgsDebugMsgLevel( QStringLiteral( "Info: UserStyle element not found." ), 4 ); return; } QDomElement featureTypeStyleElem = userStyleElem.firstChildElement( QStringLiteral( "FeatureTypeStyle" ) ); if ( featureTypeStyleElem.isNull() ) { QgsDebugMsgLevel( QStringLiteral( "Info: FeatureTypeStyle element not found." ), 4 ); return; } // use last rule QDomElement ruleElem = featureTypeStyleElem.lastChildElement( QStringLiteral( "Rule" ) ); if ( ruleElem.isNull() ) { QgsDebugMsgLevel( QStringLiteral( "Info: Rule element not found." ), 4 ); return; } // use last text symbolizer QDomElement textSymbolizerElem = ruleElem.lastChildElement( QStringLiteral( "TextSymbolizer" ) ); if ( textSymbolizerElem.isNull() ) { QgsDebugMsgLevel( QStringLiteral( "Info: TextSymbolizer element not found." ), 4 ); return; } QgsPalLayerSettings settings; // Label QDomElement labelElem = textSymbolizerElem.firstChildElement( QStringLiteral( "Label" ) ); if ( !labelElem.isNull() ) { QDomElement propertyNameElem = labelElem.firstChildElement( QStringLiteral( "PropertyName" ) ); if ( !propertyNameElem.isNull() ) { // set labeling defaults // label attribute QString labelAttribute = propertyNameElem.text(); settings.fieldName = labelAttribute; settings.isExpression = false; int fieldIndex = mFields.lookupField( labelAttribute ); if ( fieldIndex == -1 ) { // label attribute is not in columns, check if it is an expression QgsExpression exp( labelAttribute ); if ( !exp.hasEvalError() ) { settings.isExpression = true; } else { QgsDebugMsgLevel( QStringLiteral( "SLD label attribute error: %1" ).arg( exp.evalErrorString() ), 3 ); } } } else { QgsDebugMsgLevel( QStringLiteral( "Info: PropertyName element not found." ), 4 ); return; } } else { QgsDebugMsgLevel( QStringLiteral( "Info: Label element not found." ), 4 ); return; } QString fontFamily = QStringLiteral( "Sans-Serif" ); int fontPointSize = 10; int fontWeight = -1; bool fontItalic = false; bool fontUnderline = false; // Font QDomElement fontElem = textSymbolizerElem.firstChildElement( QStringLiteral( "Font" ) ); if ( !fontElem.isNull() ) { QString cssName; QString elemText; QDomElement cssElem = fontElem.firstChildElement( QStringLiteral( "CssParameter" ) ); while ( !cssElem.isNull() ) { cssName = cssElem.attribute( QStringLiteral( "name" ), QStringLiteral( "not_found" ) ); if ( cssName != QLatin1String( "not_found" ) ) { elemText = cssElem.text(); if ( cssName == QLatin1String( "font-family" ) ) { fontFamily = elemText; } else if ( cssName == QLatin1String( "font-style" ) ) { fontItalic = ( elemText == QLatin1String( "italic" ) ) || ( elemText == QLatin1String( "Italic" ) ); } else if ( cssName == QLatin1String( "font-size" ) ) { bool ok; int fontSize = elemText.toInt( &ok ); if ( ok ) { fontPointSize = fontSize; } } else if ( cssName == QLatin1String( "font-weight" ) ) { if ( ( elemText == QLatin1String( "bold" ) ) || ( elemText == QLatin1String( "Bold" ) ) ) fontWeight = QFont::Bold; } else if ( cssName == QLatin1String( "font-underline" ) ) { fontUnderline = ( elemText == QLatin1String( "underline" ) ) || ( elemText == QLatin1String( "Underline" ) ); } } cssElem = cssElem.nextSiblingElement( QStringLiteral( "CssParameter" ) ); } } QgsTextFormat format; QFont font( fontFamily, fontPointSize, fontWeight, fontItalic ); font.setUnderline( fontUnderline ); format.setFont( font ); format.setSize( fontPointSize ); // Fill QColor textColor = QgsOgcUtils::colorFromOgcFill( textSymbolizerElem.firstChildElement( QStringLiteral( "Fill" ) ) ); if ( textColor.isValid() ) { format.setColor( textColor ); } QgsTextBufferSettings bufferSettings; // Halo QDomElement haloElem = textSymbolizerElem.firstChildElement( QStringLiteral( "Halo" ) ); if ( !haloElem.isNull() ) { bufferSettings.setEnabled( true ); bufferSettings.setSize( 1 ); QDomElement radiusElem = haloElem.firstChildElement( QStringLiteral( "Radius" ) ); if ( !radiusElem.isNull() ) { bool ok; double bufferSize = radiusElem.text().toDouble( &ok ); if ( ok ) { bufferSettings.setSize( bufferSize ); } } QColor bufferColor = QgsOgcUtils::colorFromOgcFill( haloElem.firstChildElement( QStringLiteral( "Fill" ) ) ); if ( bufferColor.isValid() ) { bufferSettings.setColor( bufferColor ); } } // LabelPlacement QDomElement labelPlacementElem = textSymbolizerElem.firstChildElement( QStringLiteral( "LabelPlacement" ) ); if ( !labelPlacementElem.isNull() ) { // PointPlacement QDomElement pointPlacementElem = labelPlacementElem.firstChildElement( QStringLiteral( "PointPlacement" ) ); if ( !pointPlacementElem.isNull() ) { settings.placement = QgsPalLayerSettings::OverPoint; QDomElement displacementElem = pointPlacementElem.firstChildElement( QStringLiteral( "Displacement" ) ); if ( !displacementElem.isNull() ) { QDomElement displacementXElem = displacementElem.firstChildElement( QStringLiteral( "DisplacementX" ) ); if ( !displacementXElem.isNull() ) { bool ok; double xOffset = displacementXElem.text().toDouble( &ok ); if ( ok ) { settings.xOffset = xOffset; } } QDomElement displacementYElem = displacementElem.firstChildElement( QStringLiteral( "DisplacementY" ) ); if ( !displacementYElem.isNull() ) { bool ok; double yOffset = displacementYElem.text().toDouble( &ok ); if ( ok ) { settings.yOffset = yOffset; } } } QDomElement rotationElem = pointPlacementElem.firstChildElement( QStringLiteral( "Rotation" ) ); if ( !rotationElem.isNull() ) { bool ok; double rotation = rotationElem.text().toDouble( &ok ); if ( ok ) { settings.angleOffset = 360 - rotation; } } } } format.setBuffer( bufferSettings ); settings.setFormat( format ); setLabeling( new QgsVectorLayerSimpleLabeling( settings ) ); setLabelsEnabled( true ); } QgsEditFormConfig QgsVectorLayer::editFormConfig() const { return mEditFormConfig; } void QgsVectorLayer::setEditFormConfig( const QgsEditFormConfig &editFormConfig ) { if ( mEditFormConfig == editFormConfig ) return; mEditFormConfig = editFormConfig; mEditFormConfig.onRelationsLoaded(); emit editFormConfigChanged(); } QString QgsVectorLayer::mapTipTemplate() const { return mMapTipTemplate; } void QgsVectorLayer::setMapTipTemplate( const QString &mapTip ) { if ( mMapTipTemplate == mapTip ) return; mMapTipTemplate = mapTip; emit mapTipTemplateChanged(); } QgsAttributeTableConfig QgsVectorLayer::attributeTableConfig() const { QgsAttributeTableConfig config = mAttributeTableConfig; if ( config.isEmpty() ) config.update( fields() ); return config; } void QgsVectorLayer::setAttributeTableConfig( const QgsAttributeTableConfig &attributeTableConfig ) { if ( mAttributeTableConfig != attributeTableConfig ) { mAttributeTableConfig = attributeTableConfig; emit configChanged(); } } QgsExpressionContext QgsVectorLayer::createExpressionContext() const { return QgsExpressionContext( QgsExpressionContextUtils::globalProjectLayerScopes( this ) ); } QgsExpressionContextScope *QgsVectorLayer::createExpressionContextScope() const { return QgsExpressionContextUtils::layerScope( this ); } void QgsVectorLayer::setDiagramLayerSettings( const QgsDiagramLayerSettings &s ) { if ( !mDiagramLayerSettings ) mDiagramLayerSettings = new QgsDiagramLayerSettings(); *mDiagramLayerSettings = s; } QString QgsVectorLayer::htmlMetadata() const { QgsLayerMetadataFormatter htmlFormatter( metadata() ); QString myMetadata = QStringLiteral( "\n\n" ); // Begin Provider section myMetadata += QStringLiteral( "

" ) + tr( "Information from provider" ) + QStringLiteral( "

\n
\n" ); myMetadata += QLatin1String( "\n" ); // original name myMetadata += QStringLiteral( "\n" ); // name myMetadata += QStringLiteral( "\n" ); // data source myMetadata += QStringLiteral( "\n" ); // storage type myMetadata += QStringLiteral( "\n" ); // comment myMetadata += QStringLiteral( "\n" ); // encoding myMetadata += QStringLiteral( "\n" ); // geom type QgsWkbTypes::GeometryType type = geometryType(); if ( type < 0 || type > QgsWkbTypes::NullGeometry ) { QgsDebugMsgLevel( QStringLiteral( "Invalid vector type" ), 2 ); } else { QString typeString( QStringLiteral( "%1 (%2)" ).arg( QgsWkbTypes::geometryDisplayString( geometryType() ), QgsWkbTypes::displayString( wkbType() ) ) ); myMetadata += QStringLiteral( "\n" ); } // EPSG myMetadata += QStringLiteral( "\n" ); // Extent myMetadata += QStringLiteral( "\n" ); // unit myMetadata += QStringLiteral( "\n" ); // feature count myMetadata += QStringLiteral( "\n" ); // End Provider section myMetadata += QLatin1String( "
" ) + tr( "Original" ) + QStringLiteral( "" ) + name() + QStringLiteral( "
" ) + tr( "Name" ) + QStringLiteral( "" ) + name() + QStringLiteral( "
" ) + tr( "Source" ) + QStringLiteral( "" ) + publicSource() + QStringLiteral( "
" ) + tr( "Storage" ) + QStringLiteral( "" ) + storageType() + QStringLiteral( "
" ) + tr( "Comment" ) + QStringLiteral( "" ) + dataComment() + QStringLiteral( "
" ) + tr( "Encoding" ) + QStringLiteral( "" ) + dataProvider()->encoding() + QStringLiteral( "
" ) + tr( "Geometry" ) + QStringLiteral( "" ) + typeString + QStringLiteral( "
" ) + tr( "CRS" ) + QStringLiteral( "" ); if ( crs().isValid() ) { myMetadata += crs().authid() + QStringLiteral( " - " ); myMetadata += crs().description() + QStringLiteral( " - " ); if ( crs().isGeographic() ) myMetadata += tr( "Geographic" ); else myMetadata += tr( "Projected" ); } myMetadata += QLatin1String( "
" ) + tr( "Extent" ) + QStringLiteral( "" ) + extent().toString() + QStringLiteral( "
" ) + tr( "Unit" ) + QStringLiteral( "" ) + QgsUnitTypes::toString( crs().mapUnits() ) + QStringLiteral( "
" ) + tr( "Feature count" ) + QStringLiteral( "" ) + QString::number( featureCount() ) + QStringLiteral( "
\n

" ); // identification section myMetadata += QStringLiteral( "

" ) + tr( "Identification" ) + QStringLiteral( "

\n
\n" ); myMetadata += htmlFormatter.identificationSectionHtml( ); myMetadata += QLatin1String( "

\n" ); // extent section myMetadata += QStringLiteral( "

" ) + tr( "Extent" ) + QStringLiteral( "

\n
\n" ); myMetadata += htmlFormatter.extentSectionHtml( ); myMetadata += QLatin1String( "

\n" ); // Start the Access section myMetadata += QStringLiteral( "

" ) + tr( "Access" ) + QStringLiteral( "

\n
\n" ); myMetadata += htmlFormatter.accessSectionHtml( ); myMetadata += QLatin1String( "

\n" ); // Fields section myMetadata += QStringLiteral( "

" ) + tr( "Fields" ) + QStringLiteral( "

\n
\n\n" ); // primary key QgsAttributeList pkAttrList = primaryKeyAttributes(); if ( !pkAttrList.isEmpty() ) { myMetadata += QStringLiteral( "\n" ); } const QgsFields myFields = fields(); // count fields myMetadata += QStringLiteral( "\n" ); myMetadata += QLatin1String( "
" ) + tr( "Primary key attributes" ) + QStringLiteral( "" ); Q_FOREACH ( int idx, pkAttrList ) { myMetadata += fields().at( idx ).name() + ' '; } myMetadata += QLatin1String( "
" ) + tr( "Count" ) + QStringLiteral( "" ) + QString::number( myFields.size() ) + QStringLiteral( "
\n
\n" ); myMetadata += "\n"; for ( int i = 0; i < myFields.size(); ++i ) { QgsField myField = myFields.at( i ); QString rowClass; if ( i % 2 ) rowClass = QStringLiteral( "class=\"odd-row\"" ); myMetadata += "\n"; } //close field list myMetadata += QLatin1String( "
" + tr( "Field" ) + "" + tr( "Type" ) + "" + tr( "Length" ) + "" + tr( "Precision" ) + "" + tr( "Comment" ) + "
" + myField.name() + "" + myField.typeName() + "" + QString::number( myField.length() ) + "" + QString::number( myField.precision() ) + "" + myField.comment() + "
\n

" ); // Start the contacts section myMetadata += QStringLiteral( "

" ) + tr( "Contacts" ) + QStringLiteral( "

\n
\n" ); myMetadata += htmlFormatter.contactsSectionHtml( ); myMetadata += QLatin1String( "

\n" ); // Start the links section myMetadata += QStringLiteral( "

" ) + tr( "Links" ) + QStringLiteral( "

\n
\n" ); myMetadata += htmlFormatter.linksSectionHtml( ); myMetadata += QLatin1String( "

\n" ); // Start the history section myMetadata += QStringLiteral( "

" ) + tr( "History" ) + QStringLiteral( "

\n
\n" ); myMetadata += htmlFormatter.historySectionHtml( ); myMetadata += QLatin1String( "

\n" ); myMetadata += QStringLiteral( "\n\n\n" ); return myMetadata; } void QgsVectorLayer::invalidateSymbolCountedFlag() { mSymbolFeatureCounted = false; } void QgsVectorLayer::onFeatureCounterCompleted() { onSymbolsCounted(); mFeatureCounter = nullptr; } void QgsVectorLayer::onFeatureCounterTerminated() { mFeatureCounter = nullptr; } 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 ); } void QgsVectorLayer::onRelationsLoaded() { mEditFormConfig.onRelationsLoaded(); } void QgsVectorLayer::onSymbolsCounted() { if ( mFeatureCounter ) { mSymbolFeatureCountMap = mFeatureCounter->symbolFeatureCountMap(); mSymbolFeatureCounted = true; emit symbolFeatureCountMapChanged(); } } QList QgsVectorLayer::referencingRelations( int idx ) const { return QgsProject::instance()->relationManager()->referencingRelations( this, idx ); } int QgsVectorLayer::listStylesInDatabase( QStringList &ids, QStringList &names, QStringList &descriptions, QString &msgError ) { std::unique_ptr myLib( QgsProviderRegistry::instance()->createProviderLibrary( 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 ) { msgError = QObject::tr( "Provider %1 has no %2 method" ).arg( mProviderKey, QStringLiteral( "listStyles" ) ); return -1; } return listStylesExternalMethod( mDataSource, ids, names, descriptions, msgError ); } QString QgsVectorLayer::getStyleFromDatabase( const QString &styleId, QString &msgError ) { std::unique_ptr myLib( QgsProviderRegistry::instance()->createProviderLibrary( mProviderKey ) ); if ( !myLib ) { msgError = QObject::tr( "Unable to load %1 provider" ).arg( mProviderKey ); return QString(); } getStyleById_t *getStyleByIdMethod = reinterpret_cast< getStyleById_t * >( cast_to_fptr( myLib->resolve( "getStyleById" ) ) ); if ( !getStyleByIdMethod ) { msgError = QObject::tr( "Provider %1 has no %2 method" ).arg( mProviderKey, QStringLiteral( "getStyleById" ) ); return QString(); } return getStyleByIdMethod( mDataSource, styleId, msgError ); } bool QgsVectorLayer::deleteStyleFromDatabase( const QString &styleId, QString &msgError ) { std::unique_ptr myLib( QgsProviderRegistry::instance()->createProviderLibrary( mProviderKey ) ); if ( !myLib ) { msgError = QObject::tr( "Unable to load %1 provider" ).arg( mProviderKey ); return false; } deleteStyleById_t *deleteStyleByIdMethod = reinterpret_cast< deleteStyleById_t * >( cast_to_fptr( myLib->resolve( "deleteStyleById" ) ) ); if ( !deleteStyleByIdMethod ) { msgError = QObject::tr( "Provider %1 has no %2 method" ).arg( mProviderKey, QStringLiteral( "deleteStyleById" ) ); return false; } return deleteStyleByIdMethod( mDataSource, styleId, msgError ); } void QgsVectorLayer::saveStyleToDatabase( const QString &name, const QString &description, bool useAsDefault, const QString &uiFileContent, QString &msgError ) { QString sldStyle, qmlStyle; std::unique_ptr myLib( QgsProviderRegistry::instance()->createProviderLibrary( 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 ) { msgError = QObject::tr( "Provider %1 has no %2 method" ).arg( mProviderKey, QStringLiteral( "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 &resultFlag ) { return loadNamedStyle( theURI, resultFlag, false ); } bool QgsVectorLayer::loadAuxiliaryLayer( const QgsAuxiliaryStorage &storage, const QString &key ) { bool rc = false; QString joinKey = mAuxiliaryLayerKey; if ( !key.isEmpty() ) joinKey = key; if ( storage.isValid() && !joinKey.isEmpty() ) { QgsAuxiliaryLayer *alayer = nullptr; int idx = fields().lookupField( joinKey ); if ( idx >= 0 ) { alayer = storage.createAuxiliaryLayer( fields().field( idx ), this ); if ( alayer ) { setAuxiliaryLayer( alayer ); rc = true; } } } return rc; } void QgsVectorLayer::setAuxiliaryLayer( QgsAuxiliaryLayer *alayer ) { mAuxiliaryLayerKey.clear(); if ( mAuxiliaryLayer ) removeJoin( mAuxiliaryLayer->id() ); if ( alayer ) { addJoin( alayer->joinInfo() ); if ( !alayer->isEditable() ) alayer->startEditing(); mAuxiliaryLayerKey = alayer->joinInfo().targetFieldName(); } mAuxiliaryLayer.reset( alayer ); if ( mAuxiliaryLayer ) mAuxiliaryLayer->setParent( this ); updateFields(); } const QgsAuxiliaryLayer *QgsVectorLayer::auxiliaryLayer() const { return mAuxiliaryLayer.get(); } QgsAuxiliaryLayer *QgsVectorLayer::auxiliaryLayer() { return mAuxiliaryLayer.get(); } QString QgsVectorLayer::loadNamedStyle( const QString &theURI, bool &resultFlag, bool loadFromLocalDB ) { QgsDataSourceUri dsUri( theURI ); if ( !loadFromLocalDB && mDataProvider && mDataProvider->isSaveAndLoadStyleToDatabaseSupported() ) { std::unique_ptr myLib( QgsProviderRegistry::instance()->createProviderLibrary( 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() ) { QDomDocument myDocument( QStringLiteral( "qgis" ) ); myDocument.setContent( qml ); resultFlag = importNamedStyle( myDocument, errorMsg ); return QObject::tr( "Loaded from Provider" ); } } } } return QgsMapLayer::loadNamedStyle( theURI, resultFlag ); } QSet QgsVectorLayer::dependencies() const { if ( mDataProvider ) return mDataProvider->dependencies() + mDependencies; return mDependencies; } bool QgsVectorLayer::setDependencies( const QSet &oDeps ) { QSet deps; Q_FOREACH ( const QgsMapLayerDependency &dep, oDeps ) { if ( dep.origin() == QgsMapLayerDependency::FromUser ) deps << dep; } if ( hasDependencyCycle( deps ) ) return false; QSet toAdd = deps - dependencies(); // disconnect layers that are not present in the list of dependencies anymore Q_FOREACH ( const QgsMapLayerDependency &dep, mDependencies ) { QgsVectorLayer *lyr = static_cast( QgsProject::instance()->mapLayer( dep.layerId() ) ); if ( !lyr ) continue; disconnect( lyr, &QgsVectorLayer::featureAdded, this, &QgsVectorLayer::dataChanged ); disconnect( lyr, &QgsVectorLayer::featureDeleted, this, &QgsVectorLayer::dataChanged ); disconnect( lyr, &QgsVectorLayer::geometryChanged, this, &QgsVectorLayer::dataChanged ); disconnect( lyr, &QgsVectorLayer::dataChanged, this, &QgsVectorLayer::dataChanged ); disconnect( lyr, &QgsVectorLayer::repaintRequested, this, &QgsVectorLayer::triggerRepaint ); } // assign new dependencies if ( mDataProvider ) mDependencies = mDataProvider->dependencies() + deps; else mDependencies = deps; emit dependenciesChanged(); // connect to new layers Q_FOREACH ( const QgsMapLayerDependency &dep, mDependencies ) { QgsVectorLayer *lyr = static_cast( QgsProject::instance()->mapLayer( dep.layerId() ) ); if ( !lyr ) continue; connect( lyr, &QgsVectorLayer::featureAdded, this, &QgsVectorLayer::dataChanged ); connect( lyr, &QgsVectorLayer::featureDeleted, this, &QgsVectorLayer::dataChanged ); connect( lyr, &QgsVectorLayer::geometryChanged, this, &QgsVectorLayer::dataChanged ); connect( lyr, &QgsVectorLayer::dataChanged, this, &QgsVectorLayer::dataChanged ); connect( lyr, &QgsVectorLayer::repaintRequested, this, &QgsVectorLayer::triggerRepaint ); } // if new layers are present, emit a data change if ( ! toAdd.isEmpty() ) emit dataChanged(); return true; } QgsFieldConstraints::Constraints QgsVectorLayer::fieldConstraints( int fieldIndex ) const { if ( fieldIndex < 0 || fieldIndex >= mFields.count() ) return nullptr; QgsFieldConstraints::Constraints constraints = mFields.at( fieldIndex ).constraints().constraints(); // make sure provider constraints are always present! if ( mFields.fieldOrigin( fieldIndex ) == QgsFields::OriginProvider ) { constraints |= mDataProvider->fieldConstraints( mFields.fieldOriginIndex( fieldIndex ) ); } return constraints; } QMap< QgsFieldConstraints::Constraint, QgsFieldConstraints::ConstraintStrength> QgsVectorLayer::fieldConstraintsAndStrength( int fieldIndex ) const { QMap< QgsFieldConstraints::Constraint, QgsFieldConstraints::ConstraintStrength > m; if ( fieldIndex < 0 || fieldIndex >= mFields.count() ) return m; QString name = mFields.at( fieldIndex ).name(); QMap< QPair< QString, QgsFieldConstraints::Constraint >, QgsFieldConstraints::ConstraintStrength >::const_iterator conIt = mFieldConstraintStrength.constBegin(); for ( ; conIt != mFieldConstraintStrength.constEnd(); ++conIt ) { if ( conIt.key().first == name ) { m[ conIt.key().second ] = mFieldConstraintStrength.value( conIt.key() ); } } return m; } void QgsVectorLayer::setFieldConstraint( int index, QgsFieldConstraints::Constraint constraint, QgsFieldConstraints::ConstraintStrength strength ) { if ( index < 0 || index >= mFields.count() ) return; QString name = mFields.at( index ).name(); // add constraint to existing constraints QgsFieldConstraints::Constraints constraints = mFieldConstraints.value( name, nullptr ); constraints |= constraint; mFieldConstraints.insert( name, constraints ); mFieldConstraintStrength.insert( qMakePair( name, constraint ), strength ); updateFields(); } void QgsVectorLayer::removeFieldConstraint( int index, QgsFieldConstraints::Constraint constraint ) { if ( index < 0 || index >= mFields.count() ) return; QString name = mFields.at( index ).name(); // remove constraint from existing constraints QgsFieldConstraints::Constraints constraints = mFieldConstraints.value( name, nullptr ); constraints &= ~constraint; mFieldConstraints.insert( name, constraints ); mFieldConstraintStrength.remove( qMakePair( name, constraint ) ); updateFields(); } QString QgsVectorLayer::constraintExpression( int index ) const { if ( index < 0 || index >= mFields.count() ) return QString(); return mFields.at( index ).constraints().constraintExpression(); } QString QgsVectorLayer::constraintDescription( int index ) const { if ( index < 0 || index >= mFields.count() ) return QString(); return mFields.at( index ).constraints().constraintDescription(); } void QgsVectorLayer::setConstraintExpression( int index, const QString &expression, const QString &description ) { if ( index < 0 || index >= mFields.count() ) return; if ( expression.isEmpty() ) { mFieldConstraintExpressions.remove( mFields.at( index ).name() ); } else { mFieldConstraintExpressions.insert( mFields.at( index ).name(), qMakePair( expression, description ) ); } updateFields(); } void QgsVectorLayer::setEditorWidgetSetup( int index, const QgsEditorWidgetSetup &setup ) { if ( index < 0 || index >= mFields.count() ) return; if ( setup.isNull() ) mFieldWidgetSetups.remove( mFields.at( index ).name() ); else mFieldWidgetSetups.insert( mFields.at( index ).name(), setup ); updateFields(); } QgsEditorWidgetSetup QgsVectorLayer::editorWidgetSetup( int index ) const { if ( index < 0 || index >= mFields.count() ) return QgsEditorWidgetSetup(); return mFields.at( index ).editorWidgetSetup(); } QgsAbstractVectorLayerLabeling *QgsVectorLayer::readLabelingFromCustomProperties() { QgsAbstractVectorLayerLabeling *labeling = nullptr; if ( customProperty( QStringLiteral( "labeling" ) ).toString() == QLatin1String( "pal" ) ) { if ( customProperty( QStringLiteral( "labeling/enabled" ), QVariant( false ) ).toBool() == true ) { // try to load from custom properties QgsPalLayerSettings settings; settings.readFromLayerCustomProperties( this ); labeling = new QgsVectorLayerSimpleLabeling( settings ); } // also clear old-style labeling config removeCustomProperty( QStringLiteral( "labeling" ) ); Q_FOREACH ( const QString &key, customPropertyKeys() ) { if ( key.startsWith( QLatin1String( "labeling/" ) ) ) removeCustomProperty( key ); } } return labeling; } void QgsVectorLayer::setReadExtentFromXml( bool readExtentFromXml ) { mReadExtentFromXml = readExtentFromXml; } bool QgsVectorLayer::readExtentFromXml() const { return mReadExtentFromXml; } void QgsVectorLayer::onDirtyTransaction( const QString &sql, const QString &name ) { QgsTransaction *tr = dataProvider()->transaction(); if ( tr && mEditBuffer ) { qobject_cast( mEditBuffer )->update( tr, sql, name ); } }