19 #include <QtAlgorithms>
32 , mFilenameExpressionString( QStringLiteral(
"'output_'||@atlas_featurenumber" ) )
38 if ( mLayout->customProperty( QStringLiteral(
"singleFile" ) ).isNull() )
39 mLayout->setCustomProperty( QStringLiteral(
"singleFile" ),
true );
44 return QStringLiteral(
"atlas" );
54 return mLayout.data();
59 QDomElement atlasElem = document.createElement( QStringLiteral(
"Atlas" ) );
60 atlasElem.setAttribute( QStringLiteral(
"enabled" ), mEnabled ? QStringLiteral(
"1" ) : QStringLiteral(
"0" ) );
64 atlasElem.setAttribute( QStringLiteral(
"coverageLayer" ), mCoverageLayer.
layerId );
65 atlasElem.setAttribute( QStringLiteral(
"coverageLayerName" ), mCoverageLayer.
name );
66 atlasElem.setAttribute( QStringLiteral(
"coverageLayerSource" ), mCoverageLayer.
source );
67 atlasElem.setAttribute( QStringLiteral(
"coverageLayerProvider" ), mCoverageLayer.
provider );
71 atlasElem.setAttribute( QStringLiteral(
"coverageLayer" ), QString() );
74 atlasElem.setAttribute( QStringLiteral(
"hideCoverage" ), mHideCoverage ? QStringLiteral(
"1" ) : QStringLiteral(
"0" ) );
75 atlasElem.setAttribute( QStringLiteral(
"filenamePattern" ), mFilenameExpressionString );
76 atlasElem.setAttribute( QStringLiteral(
"pageNameExpression" ), mPageNameExpression );
78 atlasElem.setAttribute( QStringLiteral(
"sortFeatures" ), mSortFeatures ? QStringLiteral(
"1" ) : QStringLiteral(
"0" ) );
81 atlasElem.setAttribute( QStringLiteral(
"sortKey" ), mSortExpression );
82 atlasElem.setAttribute( QStringLiteral(
"sortAscending" ), mSortAscending ? QStringLiteral(
"1" ) : QStringLiteral(
"0" ) );
84 atlasElem.setAttribute( QStringLiteral(
"filterFeatures" ), mFilterFeatures ? QStringLiteral(
"1" ) : QStringLiteral(
"0" ) );
85 if ( mFilterFeatures )
87 atlasElem.setAttribute( QStringLiteral(
"featureFilter" ), mFilterExpression );
90 parentElement.appendChild( atlasElem );
97 mEnabled = atlasElem.attribute( QStringLiteral(
"enabled" ), QStringLiteral(
"0" ) ).toInt();
100 const QString layerId = atlasElem.attribute( QStringLiteral(
"coverageLayer" ) );
101 const QString layerName = atlasElem.attribute( QStringLiteral(
"coverageLayerName" ) );
102 const QString layerSource = atlasElem.attribute( QStringLiteral(
"coverageLayerSource" ) );
103 const QString layerProvider = atlasElem.attribute( QStringLiteral(
"coverageLayerProvider" ) );
105 mCoverageLayer =
QgsVectorLayerRef( layerId, layerName, layerSource, layerProvider );
107 mLayout->reportContext().setLayer( mCoverageLayer.
get() );
109 mPageNameExpression = atlasElem.attribute( QStringLiteral(
"pageNameExpression" ), QString() );
111 setFilenameExpression( atlasElem.attribute( QStringLiteral(
"filenamePattern" ), QString() ), error );
113 mSortFeatures = atlasElem.attribute( QStringLiteral(
"sortFeatures" ), QStringLiteral(
"0" ) ).toInt();
114 mSortExpression = atlasElem.attribute( QStringLiteral(
"sortKey" ) );
115 mSortAscending = atlasElem.attribute( QStringLiteral(
"sortAscending" ), QStringLiteral(
"1" ) ).toInt();
116 mFilterFeatures = atlasElem.attribute( QStringLiteral(
"filterFeatures" ), QStringLiteral(
"0" ) ).toInt();
117 mFilterExpression = atlasElem.attribute( QStringLiteral(
"featureFilter" ) );
119 mHideCoverage = atlasElem.attribute( QStringLiteral(
"hideCoverage" ), QStringLiteral(
"0" ) ).toInt();
138 void QgsLayoutAtlas::removeLayers(
const QStringList &layers )
140 if ( !mCoverageLayer )
145 for (
const QString &layerId : layers )
147 if ( layerId == mCoverageLayer.
layerId )
159 if ( layer == mCoverageLayer.
get() )
170 if ( mPageNameExpression == expression )
173 mPageNameExpression = expression;
179 if ( pageNumber < 0 || pageNumber >= mFeatureIds.count() )
182 return mFeatureIds.at( pageNumber ).second;
187 if ( mSortFeatures ==
enabled )
196 if ( mSortAscending == ascending )
199 mSortAscending = ascending;
205 if ( mSortExpression == expression )
208 mSortExpression = expression;
214 if ( mFilterFeatures == filtered )
217 mFilterFeatures = filtered;
224 const bool hasChanged = mFilterExpression != expression;
225 mFilterExpression = expression;
241 class AtlasFeatureSorter
244 AtlasFeatureSorter( QgsLayoutAtlas::SorterKeys &keys,
bool ascending =
true )
246 , mAscending( ascending )
249 bool operator()(
const QPair< QgsFeatureId, QString > &id1,
const QPair< QgsFeatureId, QString > &id2 )
251 return mAscending ?
qgsVariantLessThan( mKeys.value( id1.first ), mKeys.value( id2.first ) )
256 QgsLayoutAtlas::SorterKeys &mKeys;
264 mCurrentFeatureNo = -1;
265 if ( !mCoverageLayer )
273 updateFilenameExpression( error );
280 mFilterParserError.clear();
281 if ( mFilterFeatures && !mFilterExpression.isEmpty() )
294 #ifdef HAVE_SERVER_PYTHON_PLUGINS
295 if ( mLayout->renderContext().featureFilterProvider() )
297 mLayout->renderContext().featureFilterProvider()->filterFeatures( mCoverageLayer.
get(), req );
303 std::unique_ptr<QgsExpression> nameExpression;
304 if ( !mPageNameExpression.isEmpty() )
306 nameExpression = std::make_unique< QgsExpression >( mPageNameExpression );
307 if ( nameExpression->hasParserError() )
309 nameExpression.reset(
nullptr );
313 nameExpression->prepare( &expressionContext );
321 mFeatureKeys.clear();
324 if ( mSortFeatures && !mSortExpression.isEmpty() )
326 sortExpression = std::make_unique< QgsExpression >( mSortExpression );
342 if ( nameExpression )
344 const QVariant result = nameExpression->evaluate( &expressionContext );
345 if ( nameExpression->hasEvalError() )
349 pageName = result.toString();
352 mFeatureIds.push_back( qMakePair( feat.
id(), pageName ) );
356 const QVariant result =
sortExpression->evaluate( &expressionContext );
361 mFeatureKeys.insert( feat.
id(), result );
366 if ( !mFeatureKeys.isEmpty() )
369 std::sort( mFeatureIds.begin(), mFeatureIds.end(), sorter );
373 return mFeatureIds.size();
378 if ( !mCoverageLayer )
403 return mFeatureIds.size();
408 const QFileInfo fi( baseFilePath );
409 const QDir dir = fi.dir();
410 QString base = dir.filePath( mCurrentFilename );
411 if ( !extension.startsWith(
'.' ) )
419 const int newFeatureNo = mCurrentFeatureNo + 1;
420 if ( newFeatureNo >= mFeatureIds.size() )
425 return prepareForFeature( newFeatureNo );
430 const int newFeatureNo = mCurrentFeatureNo - 1;
431 if ( newFeatureNo < 0 )
436 return prepareForFeature( newFeatureNo );
441 return prepareForFeature( 0 );
446 return prepareForFeature( mFeatureIds.size() - 1 );
451 return prepareForFeature( feature );
457 auto it = mFeatureIds.constBegin();
458 for (
int currentIdx = 0; it != mFeatureIds.constEnd(); ++it, ++currentIdx )
460 if ( ( *it ).first == feature.
id() )
478 prepareForFeature( mCurrentFeatureNo );
484 if ( hide == mHideCoverage )
487 mHideCoverage = hide;
494 const bool hasChanged = mFilenameExpressionString != pattern;
495 mFilenameExpressionString = pattern;
500 return updateFilenameExpression( errorString );
505 return mCurrentFilename;
518 if ( mCoverageLayer )
521 if ( mLayout && mEnabled )
523 if ( mCurrentFeature.
isValid() )
527 else if ( mCoverageLayer )
530 feature.setValid(
true );
534 return expressionContext;
537 bool QgsLayoutAtlas::updateFilenameExpression( QString &error )
539 if ( !mCoverageLayer )
545 bool evalResult {
true };
547 if ( !mFilenameExpressionString.isEmpty() )
565 evalResult = evalFeatureFilename( expressionContext );
570 error = mFilenameExpressionError;
579 mFilenameExpressionError.clear();
580 if ( !mFilenameExpressionString.isEmpty() )
592 mCurrentFilename = filenameRes.toString();
597 bool QgsLayoutAtlas::prepareForFeature(
const int featureI )
599 if ( !mCoverageLayer )
604 if ( mFeatureIds.isEmpty() )
610 if ( featureI >= mFeatureIds.size() )
615 mCurrentFeatureNo = featureI;
621 mLayout->reportContext().blockSignals(
true );
622 mLayout->reportContext().setLayer( mCoverageLayer.
get() );
623 mLayout->reportContext().blockSignals(
false );
624 mLayout->reportContext().setFeature( mCurrentFeature );
630 if ( !evalFeatureFilename( expressionContext ) )
637 emit
messagePushed( tr(
"Atlas feature %1 of %2" ).arg( featureI + 1 ).arg( mFeatureIds.size() ) );
639 return mCurrentFeature.
isValid();