QGIS/src/core/qgsvectorlayer.cpp
2018-03-14 14:52:51 +01:00

4576 lines
139 KiB
C++

/***************************************************************************
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 <limits>
#include <QImage>
#include <QPainter>
#include <QPainterPath>
#include <QPolygonF>
#include <QProgressDialog>
#include <QString>
#include <QDomNode>
#include <QVector>
#include <QStringBuilder>
#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 <dlfcn.h>
#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<QgsVectorLayerJoinInfo> 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<QgsDiagramSettings> 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<QgsPointXY> &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<QgsPointXY> &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<QgsPointXY> &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<QgsPointXY> &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<QgsMapLayerDependency> 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<QgsVectorDataProvider *>( 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<QString, QgsMapLayer *> &layers = QgsProject::instance()->mapLayers();
QMap<QString, QgsMapLayer *>::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 <maplayer>" ), 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<QString>::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<QString>::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<int> attrs )
{
bool deleted = false;
// Remove multiple occurrences of same attribute
attrs = attrs.toSet().toList();
std::sort( attrs.begin(), attrs.end(), std::greater<int>() );
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<const QgsExpressionNodeColumnRef *>( 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<QVariant> QgsVectorLayer::uniqueValues( int index, int limit ) const
{
QSet<QVariant> 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<QString> 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<QString, QVariant> 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<double>::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<double>::max();
double currentValue = 0;
while ( fit.nextFeature( f ) )
{
currentValue = f.attribute( index ).toDouble();
if ( currentValue > maximumValue )
{
maximumValue = currentValue;
}
}
return QVariant( maximumValue );
}
}
Q_ASSERT_X( false, "QgsVectorLayer::maximumValue()", "Unknown source of the field!" );
return QVariant();
}
QVariant QgsVectorLayer::aggregate( QgsAggregateCalculator::Aggregate aggregate, const QString &fieldOrExpression,
const QgsAggregateCalculator::AggregateParameters &parameters, QgsExpressionContext *context, bool *ok ) 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( "<html>\n<body>\n" );
// Begin Provider section
myMetadata += QStringLiteral( "<h1>" ) + tr( "Information from provider" ) + QStringLiteral( "</h1>\n<hr>\n" );
myMetadata += QLatin1String( "<table class=\"list-view\">\n" );
// original name
myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Original" ) + QStringLiteral( "</td><td>" ) + name() + QStringLiteral( "</td></tr>\n" );
// name
myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Name" ) + QStringLiteral( "</td><td>" ) + name() + QStringLiteral( "</td></tr>\n" );
// data source
myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Source" ) + QStringLiteral( "</td><td>" ) + publicSource() + QStringLiteral( "</td></tr>\n" );
// storage type
myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Storage" ) + QStringLiteral( "</td><td>" ) + storageType() + QStringLiteral( "</td></tr>\n" );
// comment
myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Comment" ) + QStringLiteral( "</td><td>" ) + dataComment() + QStringLiteral( "</td></tr>\n" );
// encoding
myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Encoding" ) + QStringLiteral( "</td><td>" ) + dataProvider()->encoding() + QStringLiteral( "</td></tr>\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( "<tr><td class=\"highlight\">" ) + tr( "Geometry" ) + QStringLiteral( "</td><td>" ) + typeString + QStringLiteral( "</td></tr>\n" );
}
// EPSG
myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "CRS" ) + QStringLiteral( "</td><td>" );
if ( crs().isValid() )
{
myMetadata += crs().authid() + QStringLiteral( " - " );
myMetadata += crs().description() + QStringLiteral( " - " );
if ( crs().isGeographic() )
myMetadata += tr( "Geographic" );
else
myMetadata += tr( "Projected" );
}
myMetadata += QLatin1String( "</td></tr>\n" );
// Extent
myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Extent" ) + QStringLiteral( "</td><td>" ) + extent().toString() + QStringLiteral( "</td></tr>\n" );
// unit
myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Unit" ) + QStringLiteral( "</td><td>" ) + QgsUnitTypes::toString( crs().mapUnits() ) + QStringLiteral( "</td></tr>\n" );
// feature count
myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Feature count" ) + QStringLiteral( "</td><td>" ) + QString::number( featureCount() ) + QStringLiteral( "</td></tr>\n" );
// End Provider section
myMetadata += QLatin1String( "</table>\n<br><br>" );
// identification section
myMetadata += QStringLiteral( "<h1>" ) + tr( "Identification" ) + QStringLiteral( "</h1>\n<hr>\n" );
myMetadata += htmlFormatter.identificationSectionHtml( );
myMetadata += QLatin1String( "<br><br>\n" );
// extent section
myMetadata += QStringLiteral( "<h1>" ) + tr( "Extent" ) + QStringLiteral( "</h1>\n<hr>\n" );
myMetadata += htmlFormatter.extentSectionHtml( );
myMetadata += QLatin1String( "<br><br>\n" );
// Start the Access section
myMetadata += QStringLiteral( "<h1>" ) + tr( "Access" ) + QStringLiteral( "</h1>\n<hr>\n" );
myMetadata += htmlFormatter.accessSectionHtml( );
myMetadata += QLatin1String( "<br><br>\n" );
// Fields section
myMetadata += QStringLiteral( "<h1>" ) + tr( "Fields" ) + QStringLiteral( "</h1>\n<hr>\n<table class=\"list-view\">\n" );
// primary key
QgsAttributeList pkAttrList = primaryKeyAttributes();
if ( !pkAttrList.isEmpty() )
{
myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Primary key attributes" ) + QStringLiteral( "</td><td>" );
Q_FOREACH ( int idx, pkAttrList )
{
myMetadata += fields().at( idx ).name() + ' ';
}
myMetadata += QLatin1String( "</td></tr>\n" );
}
const QgsFields myFields = fields();
// count fields
myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Count" ) + QStringLiteral( "</td><td>" ) + QString::number( myFields.size() ) + QStringLiteral( "</td></tr>\n" );
myMetadata += QLatin1String( "</table>\n<br><table width=\"100%\" class=\"tabular-view\">\n" );
myMetadata += "<tr><th>" + tr( "Field" ) + "</th><th>" + tr( "Type" ) + "</th><th>" + tr( "Length" ) + "</th><th>" + tr( "Precision" ) + "</th><th>" + tr( "Comment" ) + "</th></tr>\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 += "<tr " + rowClass + "><td>" + myField.name() + "</td><td>" + myField.typeName() + "</td><td>" + QString::number( myField.length() ) + "</td><td>" + QString::number( myField.precision() ) + "</td><td>" + myField.comment() + "</td></tr>\n";
}
//close field list
myMetadata += QLatin1String( "</table>\n<br><br>" );
// Start the contacts section
myMetadata += QStringLiteral( "<h1>" ) + tr( "Contacts" ) + QStringLiteral( "</h1>\n<hr>\n" );
myMetadata += htmlFormatter.contactsSectionHtml( );
myMetadata += QLatin1String( "<br><br>\n" );
// Start the links section
myMetadata += QStringLiteral( "<h1>" ) + tr( "Links" ) + QStringLiteral( "</h1>\n<hr>\n" );
myMetadata += htmlFormatter.linksSectionHtml( );
myMetadata += QLatin1String( "<br><br>\n" );
// Start the history section
myMetadata += QStringLiteral( "<h1>" ) + tr( "History" ) + QStringLiteral( "</h1>\n<hr>\n" );
myMetadata += htmlFormatter.historySectionHtml( );
myMetadata += QLatin1String( "<br><br>\n" );
myMetadata += QStringLiteral( "\n</body>\n</html>\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<QgsRelation> 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<QLibrary> 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<QLibrary> 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<QLibrary> 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<QLibrary> 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<QLibrary> 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<QgsMapLayerDependency> QgsVectorLayer::dependencies() const
{
if ( mDataProvider )
return mDataProvider->dependencies() + mDependencies;
return mDependencies;
}
bool QgsVectorLayer::setDependencies( const QSet<QgsMapLayerDependency> &oDeps )
{
QSet<QgsMapLayerDependency> deps;
Q_FOREACH ( const QgsMapLayerDependency &dep, oDeps )
{
if ( dep.origin() == QgsMapLayerDependency::FromUser )
deps << dep;
}
if ( hasDependencyCycle( deps ) )
return false;
QSet<QgsMapLayerDependency> 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<QgsVectorLayer *>( 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<QgsVectorLayer *>( 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<QgsVectorLayerEditPassthrough *>( mEditBuffer )->update( tr, sql, name );
}
}