19 #include <QtAlgorithms> 
   32   , mFilenameExpressionString( QStringLiteral( 
"'output_'||@atlas_featurenumber" ) )
 
   41   return QStringLiteral( 
"atlas" );
 
   51   return mLayout.data();
 
   56   QDomElement atlasElem = document.createElement( QStringLiteral( 
"Atlas" ) );
 
   57   atlasElem.setAttribute( QStringLiteral( 
"enabled" ), mEnabled ? QStringLiteral( 
"1" ) : QStringLiteral( 
"0" ) );
 
   61     atlasElem.setAttribute( QStringLiteral( 
"coverageLayer" ), mCoverageLayer.
layerId );
 
   62     atlasElem.setAttribute( QStringLiteral( 
"coverageLayerName" ), mCoverageLayer.
name );
 
   63     atlasElem.setAttribute( QStringLiteral( 
"coverageLayerSource" ), mCoverageLayer.
source );
 
   64     atlasElem.setAttribute( QStringLiteral( 
"coverageLayerProvider" ), mCoverageLayer.
provider );
 
   68     atlasElem.setAttribute( QStringLiteral( 
"coverageLayer" ), QString() );
 
   71   atlasElem.setAttribute( QStringLiteral( 
"hideCoverage" ), mHideCoverage ? QStringLiteral( 
"1" ) : QStringLiteral( 
"0" ) );
 
   72   atlasElem.setAttribute( QStringLiteral( 
"filenamePattern" ), mFilenameExpressionString );
 
   73   atlasElem.setAttribute( QStringLiteral( 
"pageNameExpression" ), mPageNameExpression );
 
   75   atlasElem.setAttribute( QStringLiteral( 
"sortFeatures" ), mSortFeatures ? QStringLiteral( 
"1" ) : QStringLiteral( 
"0" ) );
 
   78     atlasElem.setAttribute( QStringLiteral( 
"sortKey" ), mSortExpression );
 
   79     atlasElem.setAttribute( QStringLiteral( 
"sortAscending" ), mSortAscending ? QStringLiteral( 
"1" ) : QStringLiteral( 
"0" ) );
 
   81   atlasElem.setAttribute( QStringLiteral( 
"filterFeatures" ), mFilterFeatures ? QStringLiteral( 
"1" ) : QStringLiteral( 
"0" ) );
 
   82   if ( mFilterFeatures )
 
   84     atlasElem.setAttribute( QStringLiteral( 
"featureFilter" ), mFilterExpression );
 
   87   parentElement.appendChild( atlasElem );
 
   94   mEnabled = atlasElem.attribute( QStringLiteral( 
"enabled" ), QStringLiteral( 
"0" ) ).toInt();
 
   97   QString layerId = atlasElem.attribute( QStringLiteral( 
"coverageLayer" ) );
 
   98   QString layerName = atlasElem.attribute( QStringLiteral( 
"coverageLayerName" ) );
 
   99   QString layerSource = atlasElem.attribute( QStringLiteral( 
"coverageLayerSource" ) );
 
  100   QString layerProvider = atlasElem.attribute( QStringLiteral( 
"coverageLayerProvider" ) );
 
  102   mCoverageLayer = 
QgsVectorLayerRef( layerId, layerName, layerSource, layerProvider );
 
  104   mLayout->reportContext().setLayer( mCoverageLayer.
get() );
 
  106   mPageNameExpression = atlasElem.attribute( QStringLiteral( 
"pageNameExpression" ), QString() );
 
  108   setFilenameExpression( atlasElem.attribute( QStringLiteral( 
"filenamePattern" ), QString() ), error );
 
  110   mSortFeatures = atlasElem.attribute( QStringLiteral( 
"sortFeatures" ), QStringLiteral( 
"0" ) ).toInt();
 
  111   mSortExpression = atlasElem.attribute( QStringLiteral( 
"sortKey" ) );
 
  112   mSortAscending = atlasElem.attribute( QStringLiteral( 
"sortAscending" ), QStringLiteral( 
"1" ) ).toInt();
 
  113   mFilterFeatures = atlasElem.attribute( QStringLiteral( 
"filterFeatures" ), QStringLiteral( 
"0" ) ).toInt();
 
  114   mFilterExpression = atlasElem.attribute( QStringLiteral( 
"featureFilter" ) );
 
  116   mHideCoverage = atlasElem.attribute( QStringLiteral( 
"hideCoverage" ), QStringLiteral( 
"0" ) ).toInt();
 
  135 void QgsLayoutAtlas::removeLayers( 
const QStringList &layers )
 
  137   if ( !mCoverageLayer )
 
  142   for ( 
const QString &layerId : layers )
 
  144     if ( layerId == mCoverageLayer.
layerId )
 
  156   if ( layer == mCoverageLayer.
get() )
 
  167   if ( mPageNameExpression == expression )
 
  170   mPageNameExpression = expression;
 
  176   if ( pageNumber < 0 || pageNumber >= mFeatureIds.count() )
 
  179   return mFeatureIds.at( pageNumber ).second;
 
  184   if ( mSortFeatures == 
enabled )
 
  193   if ( mSortAscending == ascending )
 
  196   mSortAscending = ascending;
 
  202   if ( mSortExpression == expression )
 
  205   mSortExpression = expression;
 
  211   if ( mFilterFeatures == filtered )
 
  214   mFilterFeatures = filtered;
 
  221   const bool hasChanged = mFilterExpression != expression;
 
  222   mFilterExpression = expression;
 
  238 class AtlasFeatureSorter
 
  241     AtlasFeatureSorter( QgsLayoutAtlas::SorterKeys &keys, 
bool ascending = 
true )
 
  243       , mAscending( ascending )
 
  246     bool operator()( 
const QPair< QgsFeatureId, QString > &id1, 
const QPair< QgsFeatureId, QString > &id2 )
 
  248       return mAscending ? 
qgsVariantLessThan( mKeys.value( id1.first ), mKeys.value( id2.first ) )
 
  253     QgsLayoutAtlas::SorterKeys &mKeys;
 
  261   mCurrentFeatureNo = -1;
 
  262   if ( !mCoverageLayer )
 
  270   updateFilenameExpression( error );
 
  277   mFilterParserError.clear();
 
  278   if ( mFilterFeatures && !mFilterExpression.isEmpty() )
 
  293   std::unique_ptr<QgsExpression> nameExpression;
 
  294   if ( !mPageNameExpression.isEmpty() )
 
  296     nameExpression = qgis::make_unique< QgsExpression >( mPageNameExpression );
 
  297     if ( nameExpression->hasParserError() )
 
  299       nameExpression.reset( 
nullptr );
 
  303       nameExpression->prepare( &expressionContext );
 
  311   mFeatureKeys.clear();
 
  314   if ( mSortFeatures && !mSortExpression.isEmpty() )
 
  316     sortExpression = qgis::make_unique< QgsExpression >( mSortExpression );
 
  332     if ( nameExpression )
 
  334       QVariant result = nameExpression->evaluate( &expressionContext );
 
  335       if ( nameExpression->hasEvalError() )
 
  339       pageName = result.toString();
 
  342     mFeatureIds.push_back( qMakePair( feat.
id(), pageName ) );
 
  351       mFeatureKeys.insert( feat.
id(), result );
 
  356   if ( !mFeatureKeys.isEmpty() )
 
  359     std::sort( mFeatureIds.begin(), mFeatureIds.end(), sorter ); 
 
  363   return mFeatureIds.size();
 
  368   if ( !mCoverageLayer )
 
  393   return mFeatureIds.size();
 
  398   QFileInfo fi( baseFilePath );
 
  400   QString base = dir.filePath( mCurrentFilename );
 
  401   if ( !extension.startsWith( 
'.' ) )
 
  409   int newFeatureNo = mCurrentFeatureNo + 1;
 
  410   if ( newFeatureNo >= mFeatureIds.size() )
 
  415   return prepareForFeature( newFeatureNo );
 
  420   int newFeatureNo = mCurrentFeatureNo - 1;
 
  421   if ( newFeatureNo < 0 )
 
  426   return prepareForFeature( newFeatureNo );
 
  431   return prepareForFeature( 0 );
 
  436   return prepareForFeature( mFeatureIds.size() - 1 );
 
  441   return prepareForFeature( feature );
 
  447   auto it = mFeatureIds.constBegin();
 
  448   for ( 
int currentIdx = 0; it != mFeatureIds.constEnd(); ++it, ++currentIdx )
 
  450     if ( ( *it ).first == feature.
id() )
 
  468   prepareForFeature( mCurrentFeatureNo );
 
  474   if ( hide == mHideCoverage )
 
  477   mHideCoverage = hide;
 
  484   const bool hasChanged = mFilenameExpressionString != pattern;
 
  485   mFilenameExpressionString = pattern;
 
  490   return updateFilenameExpression( errorString );
 
  495   return mCurrentFilename;
 
  508   if ( mCoverageLayer )
 
  511   if ( mLayout && mEnabled )
 
  513     if ( mCurrentFeature.
isValid() )
 
  517     else if ( mCoverageLayer )  
 
  520       feature.setValid( 
true );
 
  524   return expressionContext;
 
  527 bool QgsLayoutAtlas::updateFilenameExpression( QString &error )
 
  529   if ( !mCoverageLayer )
 
  535   bool evalResult { 
true };
 
  537   if ( !mFilenameExpressionString.isEmpty() )
 
  539     mFilenameExpression = 
QgsExpression( mFilenameExpressionString );
 
  549     evalResult = mFilenameExpression.
prepare( &expressionContext );
 
  555     evalResult = evalFeatureFilename( expressionContext );
 
  569   if ( !mFilenameExpressionString.isEmpty() && mFilenameExpression.
isValid() )
 
  571     QVariant filenameRes = mFilenameExpression.
evaluate( &context );
 
  578     mCurrentFilename = filenameRes.toString();
 
  583 bool QgsLayoutAtlas::prepareForFeature( 
const int featureI )
 
  585   if ( !mCoverageLayer )
 
  590   if ( mFeatureIds.isEmpty() )
 
  596   if ( featureI >= mFeatureIds.size() )
 
  601   mCurrentFeatureNo = featureI;
 
  607   mLayout->reportContext().blockSignals( 
true ); 
 
  608   mLayout->reportContext().setLayer( mCoverageLayer.
get() );
 
  609   mLayout->reportContext().blockSignals( 
false );
 
  610   mLayout->reportContext().setFeature( mCurrentFeature );
 
  616   if ( !evalFeatureFilename( expressionContext ) )
 
  623   emit 
messagePushed( tr( 
"Atlas feature %1 of %2" ).arg( featureI + 1 ).arg( mFeatureIds.size() ) );
 
  625   return mCurrentFeature.
isValid();