19 #include <QtAlgorithms> 31 , mFilenameExpressionString( QStringLiteral(
"'output_'||@atlas_featurenumber" ) )
40 return QStringLiteral(
"atlas" );
50 return mLayout.data();
55 QDomElement atlasElem = document.createElement( QStringLiteral(
"Atlas" ) );
56 atlasElem.setAttribute( QStringLiteral(
"enabled" ), mEnabled ? QStringLiteral(
"1" ) : QStringLiteral(
"0" ) );
60 atlasElem.setAttribute( QStringLiteral(
"coverageLayer" ), mCoverageLayer.
layerId );
61 atlasElem.setAttribute( QStringLiteral(
"coverageLayerName" ), mCoverageLayer.
name );
62 atlasElem.setAttribute( QStringLiteral(
"coverageLayerSource" ), mCoverageLayer.
source );
63 atlasElem.setAttribute( QStringLiteral(
"coverageLayerProvider" ), mCoverageLayer.
provider );
67 atlasElem.setAttribute( QStringLiteral(
"coverageLayer" ), QString() );
70 atlasElem.setAttribute( QStringLiteral(
"hideCoverage" ), mHideCoverage ? QStringLiteral(
"1" ) : QStringLiteral(
"0" ) );
71 atlasElem.setAttribute( QStringLiteral(
"filenamePattern" ), mFilenameExpressionString );
72 atlasElem.setAttribute( QStringLiteral(
"pageNameExpression" ), mPageNameExpression );
74 atlasElem.setAttribute( QStringLiteral(
"sortFeatures" ), mSortFeatures ? QStringLiteral(
"1" ) : QStringLiteral(
"0" ) );
77 atlasElem.setAttribute( QStringLiteral(
"sortKey" ), mSortExpression );
78 atlasElem.setAttribute( QStringLiteral(
"sortAscending" ), mSortAscending ? QStringLiteral(
"1" ) : QStringLiteral(
"0" ) );
80 atlasElem.setAttribute( QStringLiteral(
"filterFeatures" ), mFilterFeatures ? QStringLiteral(
"1" ) : QStringLiteral(
"0" ) );
81 if ( mFilterFeatures )
83 atlasElem.setAttribute( QStringLiteral(
"featureFilter" ), mFilterExpression );
86 parentElement.appendChild( atlasElem );
93 mEnabled = atlasElem.attribute( QStringLiteral(
"enabled" ), QStringLiteral(
"0" ) ).toInt();
96 QString layerId = atlasElem.attribute( QStringLiteral(
"coverageLayer" ) );
97 QString layerName = atlasElem.attribute( QStringLiteral(
"coverageLayerName" ) );
98 QString layerSource = atlasElem.attribute( QStringLiteral(
"coverageLayerSource" ) );
99 QString layerProvider = atlasElem.attribute( QStringLiteral(
"coverageLayerProvider" ) );
101 mCoverageLayer =
QgsVectorLayerRef( layerId, layerName, layerSource, layerProvider );
103 mLayout->reportContext().setLayer( mCoverageLayer.
get() );
105 mPageNameExpression = atlasElem.attribute( QStringLiteral(
"pageNameExpression" ), QString() );
107 setFilenameExpression( atlasElem.attribute( QStringLiteral(
"filenamePattern" ), QString() ), error );
109 mSortFeatures = atlasElem.attribute( QStringLiteral(
"sortFeatures" ), QStringLiteral(
"0" ) ).toInt();
110 mSortExpression = atlasElem.attribute( QStringLiteral(
"sortKey" ) );
111 mSortAscending = atlasElem.attribute( QStringLiteral(
"sortAscending" ), QStringLiteral(
"1" ) ).toInt();
112 mFilterFeatures = atlasElem.attribute( QStringLiteral(
"filterFeatures" ), QStringLiteral(
"0" ) ).toInt();
113 mFilterExpression = atlasElem.attribute( QStringLiteral(
"featureFilter" ) );
115 mHideCoverage = atlasElem.attribute( QStringLiteral(
"hideCoverage" ), QStringLiteral(
"0" ) ).toInt();
124 if ( enabled == mEnabled )
134 void QgsLayoutAtlas::removeLayers(
const QStringList &layers )
136 if ( !mCoverageLayer )
141 for (
const QString &layerId : layers )
143 if ( layerId == mCoverageLayer.
layerId )
155 if ( layer == mCoverageLayer.
get() )
166 if ( pageNumber < 0 || pageNumber >= mFeatureIds.count() )
169 return mFeatureIds.at( pageNumber ).second;
175 mFilterExpression = expression;
194 , mAscending( ascending )
197 bool operator()(
const QPair< QgsFeatureId, QString > &id1,
const QPair< QgsFeatureId, QString > &id2 )
199 return mAscending ?
qgsVariantLessThan( mKeys.value( id1.first ), mKeys.value( id2.first ) )
204 QgsLayoutAtlas::SorterKeys &mKeys;
212 mCurrentFeatureNo = -1;
213 if ( !mCoverageLayer )
221 updateFilenameExpression( error );
228 mFilterParserError.clear();
229 if ( mFilterFeatures && !mFilterExpression.isEmpty() )
244 std::unique_ptr<QgsExpression> nameExpression;
245 if ( !mPageNameExpression.isEmpty() )
247 nameExpression = qgis::make_unique< QgsExpression >( mPageNameExpression );
248 if ( nameExpression->hasParserError() )
250 nameExpression.reset(
nullptr );
254 nameExpression->prepare( &expressionContext );
262 mFeatureKeys.clear();
265 if ( mSortFeatures && !mSortExpression.isEmpty() )
267 sortExpression = qgis::make_unique< QgsExpression >( mSortExpression );
268 if ( sortExpression->hasParserError() )
270 sortExpression.reset(
nullptr );
274 sortExpression->prepare( &expressionContext );
283 if ( nameExpression )
285 QVariant result = nameExpression->evaluate( &expressionContext );
286 if ( nameExpression->hasEvalError() )
290 pageName = result.toString();
293 mFeatureIds.push_back( qMakePair( feat.
id(), pageName ) );
295 if ( sortExpression )
297 QVariant result = sortExpression->evaluate( &expressionContext );
298 if ( sortExpression->hasEvalError() )
302 mFeatureKeys.insert( feat.
id(), result );
307 if ( !mFeatureKeys.isEmpty() )
310 std::sort( mFeatureIds.begin(), mFeatureIds.end(), sorter );
314 return mFeatureIds.size();
319 if ( !mCoverageLayer )
344 return mFeatureIds.size();
349 QFileInfo fi( baseFilePath );
351 QString base = dir.filePath( mCurrentFilename );
352 if ( !extension.startsWith(
'.' ) )
360 int newFeatureNo = mCurrentFeatureNo + 1;
361 if ( newFeatureNo >= mFeatureIds.size() )
366 return prepareForFeature( newFeatureNo );
371 int newFeatureNo = mCurrentFeatureNo - 1;
372 if ( newFeatureNo < 0 )
377 return prepareForFeature( newFeatureNo );
382 return prepareForFeature( 0 );
387 return prepareForFeature( mFeatureIds.size() - 1 );
392 return prepareForFeature( feature );
398 auto it = mFeatureIds.constBegin();
399 for (
int currentIdx = 0; it != mFeatureIds.constEnd(); ++it, ++currentIdx )
401 if ( ( *it ).first == feature.
id() )
419 prepareForFeature( mCurrentFeatureNo );
424 mHideCoverage = hide;
432 mFilenameExpressionString = pattern;
433 return updateFilenameExpression( errorString );
438 return mCurrentFilename;
451 if ( mCoverageLayer )
454 if ( mLayout && mEnabled )
457 return expressionContext;
460 bool QgsLayoutAtlas::updateFilenameExpression( QString &error )
462 if ( !mCoverageLayer )
469 if ( !mFilenameExpressionString.isEmpty() )
471 mFilenameExpression =
QgsExpression( mFilenameExpressionString );
474 if ( mFilenameExpression.hasParserError() )
476 error = mFilenameExpression.parserErrorString();
481 mFilenameExpression.prepare( &expressionContext );
485 evalFeatureFilename( expressionContext );
492 if ( !mFilenameExpressionString.isEmpty() && mFilenameExpression.isValid() )
494 QVariant filenameRes = mFilenameExpression.evaluate( &context );
495 if ( mFilenameExpression.hasEvalError() )
497 QgsMessageLog::logMessage( tr(
"Atlas filename evaluation error: %1" ).arg( mFilenameExpression.evalErrorString() ), tr(
"Layout" ) );
501 mCurrentFilename = filenameRes.toString();
506 bool QgsLayoutAtlas::prepareForFeature(
const int featureI )
508 if ( !mCoverageLayer )
513 if ( mFeatureIds.isEmpty() )
519 if ( featureI >= mFeatureIds.size() )
524 mCurrentFeatureNo = featureI;
527 if ( !mCoverageLayer->getFeatures(
QgsFeatureRequest().setFilterFid( mFeatureIds[ featureI ].
first ) ).nextFeature( mCurrentFeature ) )
530 mLayout->reportContext().blockSignals(
true );
531 mLayout->reportContext().setLayer( mCoverageLayer.get() );
532 mLayout->reportContext().blockSignals(
false );
533 mLayout->reportContext().setFeature( mCurrentFeature );
539 if ( !evalFeatureFilename( expressionContext ) )
546 emit
messagePushed( QString( tr(
"Atlas feature %1 of %2" ) ).arg( featureI + 1 ).arg( mFeatureIds.size() ) );
548 return mCurrentFeature.isValid();
void setCoverageLayer(QgsVectorLayer *layer)
Sets the coverage layer to use for the atlas features.
Class for parsing and evaluation of expressions (formerly called "search strings").
The class is used as a container of context for various read/write operations on other objects...
Wrapper for iterator of features from vector data provider or vector layer.
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
QString filePath(const QString &baseFilePath, const QString &extension) override
Returns the file path for the current feature, based on a specified base file path and extension...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
TYPE * resolveWeakly(const QgsProject *project)
Resolves the map layer by attempting to find a matching layer in a project using a weak match...
QString stringType() const override
Returns the object type as a string.
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the scope.
TYPE * get() const
Returns a pointer to the layer, or nullptr if the reference has not yet been matched to a layer...
void toggled(bool)
Emitted when atlas is enabled or disabled.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
bool qgsVariantGreaterThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is greater than the second.
bool writeXml(QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores the objects's state in a DOM element.
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
QgsLayoutAtlas(QgsLayout *layout)
Constructor for new QgsLayoutAtlas.
QgsLayout * layout() override
Returns the layout associated with the iterator.
QString filterExpression() const
Returns the expression used for filtering features in the coverage layer.
bool endRender() override
Ends the render, performing any required cleanup tasks.
bool setFilenameExpression(const QString &expression, QString &errorString)
Sets the filename expression used for generating output filenames for each atlas page.
QgsFeatureRequest & setExpressionContext(const QgsExpressionContext &context)
Sets the expression context used to evaluate filter expressions.
void refreshCurrentFeature()
Refreshes the current atlas feature, by refetching its attributes from the vector layer provider...
void numberFeaturesChanged(int numFeatures)
Emitted when the number of features for the atlas changes.
QgsExpressionContextScope * lastScope()
Returns the last scope added to the context.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
QString provider
Weak reference to layer provider.
QString layerId
Original layer ID.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
bool readXml(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets the objects's state from a DOM element.
QString nameForPage(int page) const
Returns the calculated name for a specified atlas page number.
bool enabled() const
Returns whether the atlas generation is enabled.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
QString name
Weak reference to layer name.
bool last()
Seeks to the last feature, returning false if no feature was found.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
Hide coverage layer in outputs.
Reads and writes project states.
QString currentFilename() const
Returns the current feature filename.
void setHideCoverage(bool hide)
Sets whether the coverage layer should be hidden in map items in the layouts.
int count() override
Returns the number of features to iterate over.
bool first()
Seeks to the first feature, returning false if no feature was found.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the scope.
void setLayer(TYPE *l)
Sets the reference to point to a specified layer.
void renderBegun()
Emitted when atlas rendering has begun.
QString source
Weak reference to layer public source.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
bool previous()
Iterates to the previous feature, returning false if no previous feature exists.
bool setFilterExpression(const QString &expression, QString &errorString)
Sets the expression used for filtering features in the coverage layer.
void renderEnded()
Emitted when atlas rendering has ended.
static QgsExpressionContextScope * atlasScope(QgsLayoutAtlas *atlas)
Creates a new scope which contains variables and functions relating to a QgsLayoutAtlas.
void layersWillBeRemoved(const QStringList &layerIds)
Emitted when one or more layers are about to be removed from the registry.
static QgsExpressionContextScope * layoutScope(const QgsLayout *layout)
Creates a new scope which contains variables and functions relating to a QgsLayout layout...
void setEnabled(bool enabled)
Sets whether the atlas is enabled.
friend class AtlasFeatureSorter
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void featureChanged(const QgsFeature &feature)
Is emitted when the current atlas feature changes.
_LayerRef< QgsVectorLayer > QgsVectorLayerRef
int updateFeatures()
Requeries the current atlas coverage layer and applies filtering and sorting.
bool beginRender() override
Called when rendering begins, before iteration commences.
void messagePushed(const QString &message)
Is emitted when the atlas has an updated status bar message.
bool nextFeature(QgsFeature &f)
QString sortExpression() const
Returns the expression (or field name) to use for sorting features.
Represents a vector layer which manages a vector based data sets.
QString parserErrorString() const
Returns parser error.
void changed()
Emitted when one of the atlas parameters changes.
bool seekTo(int feature)
Seeks to the specified feature number.
void coverageLayerChanged(QgsVectorLayer *layer)
Emitted when the coverage layer for the atlas changes.