QGIS API Documentation
qgsatlascomposition.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsatlascomposition.cpp
3  -----------------------
4  begin : October 2012
5  copyright : (C) 2005 by Hugo Mercier
6  email : hugo dot mercier at oslandia dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 #include <stdexcept>
18 #include <QtAlgorithms>
19 
20 #include "qgsatlascomposition.h"
21 #include "qgsvectorlayer.h"
22 #include "qgscomposermap.h"
23 #include "qgscomposition.h"
24 #include "qgsvectordataprovider.h"
25 #include "qgsexpression.h"
26 #include "qgsgeometry.h"
27 #include "qgsmaplayerregistry.h"
28 #include "qgsproject.h"
29 #include "qgsmessagelog.h"
30 #include "qgsexpressioncontext.h"
31 #include "qgscrscache.h"
32 
34  : mComposition( composition )
35  , mEnabled( false )
36  , mHideCoverage( false )
37  , mFilenamePattern( "'output_'[email protected]_featurenumber" )
38  , mCoverageLayer( nullptr )
39  , mSingleFile( false )
40  , mSortFeatures( false )
41  , mSortAscending( true )
42  , mCurrentFeatureNo( 0 )
43  , mFilterFeatures( false )
44 {
45 
46  //listen out for layer removal
47  connect( QgsMapLayerRegistry::instance(), SIGNAL( layersWillBeRemoved( QStringList ) ), this, SLOT( removeLayers( QStringList ) ) );
48 }
49 
51 {
52 }
53 
55 {
56  if ( enabled == mEnabled )
57  {
58  return;
59  }
60 
61  mEnabled = enabled;
62  mComposition->setAtlasMode( QgsComposition::AtlasOff );
63  emit toggled( enabled );
64  emit parameterChanged();
65 }
66 
67 void QgsAtlasComposition::removeLayers( const QStringList& layers )
68 {
69  if ( !mCoverageLayer )
70  {
71  return;
72  }
73 
74  Q_FOREACH ( const QString& layerId, layers )
75  {
76  if ( layerId == mCoverageLayer->id() )
77  {
78  //current coverage layer removed
79  mCoverageLayer = nullptr;
80  setEnabled( false );
81  return;
82  }
83  }
84 }
85 
87 {
88  if ( layer == mCoverageLayer )
89  {
90  return;
91  }
92 
93  mCoverageLayer = layer;
94  emit coverageLayerChanged( layer );
95 }
96 
98 {
99  if ( pageNumber < 0 || pageNumber >= mFeatureIds.count() )
100  return QString();
101 
102  return mFeatureIds.at( pageNumber ).second;
103 }
104 
106 {
107  //deprecated method. Until removed just return the first atlas-enabled composer map
108 
109  //build a list of composer maps
111  mComposition->composerItems( maps );
112  for ( QList<QgsComposerMap*>::iterator mit = maps.begin(); mit != maps.end(); ++mit )
113  {
114  QgsComposerMap* currentMap = ( *mit );
115  if ( currentMap->atlasDriven() )
116  {
117  return currentMap;
118  }
119  }
120 
121  return nullptr;
122 }
123 
125 {
126  //deprecated
127 
128  if ( !map )
129  {
130  return;
131  }
132 
133  map->setAtlasDriven( true );
134 }
135 
136 
138 {
139  if ( !mCoverageLayer )
140  {
141  return -1;
142  }
143  return mCoverageLayer->fieldNameIndex( mSortKeyAttributeName );
144 }
145 
147 {
148  if ( mCoverageLayer )
149  {
150  QgsFields fields = mCoverageLayer->fields();
151  if ( idx >= 0 && idx < fields.count() )
152  {
153  mSortKeyAttributeName = fields.at( idx ).name();
154  return;
155  }
156  }
157  mSortKeyAttributeName = "";
158 }
159 
160 //
161 // Private class only used for the sorting of features
163 {
164  public:
165  FieldSorter( QgsAtlasComposition::SorterKeys& keys, bool ascending = true ) : mKeys( keys ), mAscending( ascending ) {}
166 
168  {
169  return mAscending ? qgsVariantLessThan( mKeys.value( id1.first ), mKeys.value( id2.first ) )
170  : qgsVariantGreaterThan( mKeys.value( id1.first ), mKeys.value( id2.first ) );
171  }
172 
173  private:
175  bool mAscending;
176 };
177 
179 {
180  //needs to be called when layer, filter, sort changes
181 
182  if ( !mCoverageLayer )
183  {
184  return 0;
185  }
186 
187  QgsExpressionContext expressionContext = createExpressionContext();
188 
189  updateFilenameExpression();
190 
191  // select all features with all attributes
192  QgsFeatureRequest req;
193 
194  QScopedPointer<QgsExpression> filterExpression;
195  if ( mFilterFeatures && !mFeatureFilter.isEmpty() )
196  {
197  filterExpression.reset( new QgsExpression( mFeatureFilter ) );
198  if ( filterExpression->hasParserError() )
199  {
200  mFilterParserError = filterExpression->parserErrorString();
201  return 0;
202  }
203 
204  //filter good to go
205  req.setFilterExpression( mFeatureFilter );
206  }
207  mFilterParserError = QString();
208 
209  QgsFeatureIterator fit = mCoverageLayer->getFeatures( req );
210 
211  QScopedPointer<QgsExpression> nameExpression;
212  if ( !mPageNameExpression.isEmpty() )
213  {
214  nameExpression.reset( new QgsExpression( mPageNameExpression ) );
215  if ( nameExpression->hasParserError() )
216  {
217  nameExpression.reset( nullptr );
218  }
219  nameExpression->prepare( &expressionContext );
220  }
221 
222  // We cannot use nextFeature() directly since the feature pointer is rewinded by the rendering process
223  // We thus store the feature ids for future extraction
224  QgsFeature feat;
225  mFeatureIds.clear();
226  mFeatureKeys.clear();
227  int sortIdx = mCoverageLayer->fieldNameIndex( mSortKeyAttributeName );
228 
229  while ( fit.nextFeature( feat ) )
230  {
231  expressionContext.setFeature( feat );
232 
233  QString pageName;
234  if ( !nameExpression.isNull() )
235  {
236  QVariant result = nameExpression->evaluate( &expressionContext );
237  if ( nameExpression->hasEvalError() )
238  {
239  QgsMessageLog::logMessage( tr( "Atlas name eval error: %1" ).arg( nameExpression->evalErrorString() ), tr( "Composer" ) );
240  }
241  pageName = result.toString();
242  }
243 
244  mFeatureIds.push_back( qMakePair( feat.id(), pageName ) );
245 
246  if ( mSortFeatures && sortIdx != -1 )
247  {
248  mFeatureKeys.insert( feat.id(), feat.attributes().at( sortIdx ) );
249  }
250  }
251 
252  // sort features, if asked for
253  if ( !mFeatureKeys.isEmpty() )
254  {
255  FieldSorter sorter( mFeatureKeys, mSortAscending );
256  qSort( mFeatureIds.begin(), mFeatureIds.end(), sorter );
257  }
258 
259  emit numberFeaturesChanged( mFeatureIds.size() );
260 
261  //jump to first feature if currently using an atlas preview
262  //need to do this in case filtering/layer change has altered matching features
263  if ( mComposition->atlasMode() == QgsComposition::PreviewAtlas )
264  {
265  firstFeature();
266  }
267 
268  return mFeatureIds.size();
269 }
270 
272 {
273  return nameForPage( currentFeatureNumber() );
274 }
275 
277 {
278  if ( !mCoverageLayer )
279  {
280  return false;
281  }
282 
283  emit renderBegun();
284 
285  bool featuresUpdated = updateFeatures();
286  if ( !featuresUpdated )
287  {
288  //no matching features found
289  return false;
290  }
291 
292  return true;
293 }
294 
296 {
297  if ( !mCoverageLayer )
298  {
299  return;
300  }
301 
302  emit featureChanged( nullptr );
303 
304  updateAtlasMaps();
305 
306  emit renderEnded();
307 }
308 
309 void QgsAtlasComposition::updateAtlasMaps()
310 {
311  //update atlas-enabled composer maps
313  mComposition->composerItems( maps );
314  for ( QList<QgsComposerMap*>::iterator mit = maps.begin(); mit != maps.end(); ++mit )
315  {
316  QgsComposerMap* currentMap = ( *mit );
317  if ( !currentMap->atlasDriven() )
318  {
319  continue;
320  }
321 
322  currentMap->cache();
323  }
324 }
325 
327 {
328  return mFeatureIds.size();
329 }
330 
332 {
333  int newFeatureNo = mCurrentFeatureNo + 1;
334  if ( newFeatureNo >= mFeatureIds.size() )
335  {
336  newFeatureNo = mFeatureIds.size() - 1;
337  }
338 
339  prepareForFeature( newFeatureNo );
340 }
341 
343 {
344  int newFeatureNo = mCurrentFeatureNo - 1;
345  if ( newFeatureNo < 0 )
346  {
347  newFeatureNo = 0;
348  }
349 
350  prepareForFeature( newFeatureNo );
351 }
352 
354 {
355  prepareForFeature( 0 );
356 }
357 
359 {
360  prepareForFeature( mFeatureIds.size() - 1 );
361 }
362 
364 {
365  int featureI = -1;
366  QVector< QPair<QgsFeatureId, QString> >::const_iterator it = mFeatureIds.constBegin();
367  int currentIdx = 0;
368  for ( ; it != mFeatureIds.constEnd(); ++it, ++currentIdx )
369  {
370  if (( *it ).first == feat->id() )
371  {
372  featureI = currentIdx;
373  break;
374  }
375  }
376 
377  if ( featureI < 0 )
378  {
379  //feature not found
380  return false;
381  }
382 
383  return prepareForFeature( featureI );
384 }
385 
387 {
388  prepareForFeature( mCurrentFeatureNo, false );
389 }
390 
391 bool QgsAtlasComposition::prepareForFeature( const int featureI, const bool updateMaps )
392 {
393  if ( !mCoverageLayer )
394  {
395  return false;
396  }
397 
398  if ( mFeatureIds.isEmpty() )
399  {
400  emit statusMsgChanged( tr( "No matching atlas features" ) );
401  return false;
402  }
403 
404  if ( featureI >= mFeatureIds.size() )
405  {
406  return false;
407  }
408 
409  mCurrentFeatureNo = featureI;
410 
411  // retrieve the next feature, based on its id
412  mCoverageLayer->getFeatures( QgsFeatureRequest().setFilterFid( mFeatureIds[ featureI ].first ) ).nextFeature( mCurrentFeature );
413 
414  QgsExpressionContext expressionContext = createExpressionContext();
415 
416  // generate filename for current feature
417  if ( !evalFeatureFilename( expressionContext ) )
418  {
419  //error evaluating filename
420  return false;
421  }
422 
423  mGeometryCache.clear();
424  emit featureChanged( &mCurrentFeature );
425  emit statusMsgChanged( QString( tr( "Atlas feature %1 of %2" ) ).arg( featureI + 1 ).arg( mFeatureIds.size() ) );
426 
427  if ( !mCurrentFeature.isValid() )
428  {
429  //bad feature
430  return true;
431  }
432 
433  if ( !updateMaps )
434  {
435  //nothing more to do
436  return true;
437  }
438 
439  //update composer maps
440 
441  //build a list of atlas-enabled composer maps
443  QList<QgsComposerMap*> atlasMaps;
444  mComposition->composerItems( maps );
445  if ( maps.isEmpty() )
446  {
447  return true;
448  }
449  for ( QList<QgsComposerMap*>::iterator mit = maps.begin(); mit != maps.end(); ++mit )
450  {
451  QgsComposerMap* currentMap = ( *mit );
452  if ( !currentMap->atlasDriven() )
453  {
454  continue;
455  }
456  atlasMaps << currentMap;
457  }
458 
459  if ( !atlasMaps.isEmpty() )
460  {
461  //clear the transformed bounds of the previous feature
462  mTransformedFeatureBounds = QgsRectangle();
463 
464  // compute extent of current feature in the map CRS. This should be set on a per-atlas map basis,
465  // but given that it's not currently possible to have maps with different CRSes we can just
466  // calculate it once based on the first atlas maps' CRS.
467  computeExtent( atlasMaps[0] );
468  }
469 
470  for ( QList<QgsComposerMap*>::iterator mit = maps.begin(); mit != maps.end(); ++mit )
471  {
472  if (( *mit )->atlasDriven() )
473  {
474  // map is atlas driven, so update it's bounds (causes a redraw)
475  prepareMap( *mit );
476  }
477  else
478  {
479  // map is not atlas driven, so manually force a redraw (to reflect possibly atlas
480  // dependent symbology)
481  ( *mit )->cache();
482  }
483  }
484 
485  return true;
486 }
487 
488 void QgsAtlasComposition::computeExtent( QgsComposerMap* map )
489 {
490  // QgsGeometry::boundingBox is expressed in the geometry"s native CRS
491  // We have to transform the grometry to the destination CRS and ask for the bounding box
492  // Note: we cannot directly take the transformation of the bounding box, since transformations are not linear
493  mTransformedFeatureBounds = currentGeometry( map->composition()->mapSettings().destinationCrs() ).boundingBox();
494 }
495 
497 {
498  if ( !map->atlasDriven() || mCoverageLayer->wkbType() == QGis::WKBNoGeometry )
499  {
500  return;
501  }
502 
503  if ( mTransformedFeatureBounds.isEmpty() )
504  {
505  //transformed extent of current feature hasn't been calculated yet. This can happen if
506  //a map has been set to be atlas controlled after prepare feature was called
507  computeExtent( map );
508  }
509 
510  double xa1 = mTransformedFeatureBounds.xMinimum();
511  double xa2 = mTransformedFeatureBounds.xMaximum();
512  double ya1 = mTransformedFeatureBounds.yMinimum();
513  double ya2 = mTransformedFeatureBounds.yMaximum();
514  QgsRectangle newExtent = mTransformedFeatureBounds;
515  QgsRectangle mOrigExtent( map->extent() );
516 
517  //sanity check - only allow fixed scale mode for point layers
518  bool isPointLayer = false;
519  switch ( mCoverageLayer->wkbType() )
520  {
521  case QGis::WKBPoint:
522  case QGis::WKBPoint25D:
523  case QGis::WKBMultiPoint:
525  isPointLayer = true;
526  break;
527  default:
528  isPointLayer = false;
529  break;
530  }
531 
532  if ( map->atlasScalingMode() == QgsComposerMap::Fixed || map->atlasScalingMode() == QgsComposerMap::Predefined || isPointLayer )
533  {
534  QgsScaleCalculator calc;
535  calc.setMapUnits( composition()->mapSettings().mapUnits() );
536  calc.setDpi( 25.4 );
537  double originalScale = calc.calculate( mOrigExtent, map->rect().width() );
538  double geomCenterX = ( xa1 + xa2 ) / 2.0;
539  double geomCenterY = ( ya1 + ya2 ) / 2.0;
540 
541  if ( map->atlasScalingMode() == QgsComposerMap::Fixed || isPointLayer )
542  {
543  // only translate, keep the original scale (i.e. width x height)
544  double xMin = geomCenterX - mOrigExtent.width() / 2.0;
545  double yMin = geomCenterY - mOrigExtent.height() / 2.0;
546  newExtent = QgsRectangle( xMin,
547  yMin,
548  xMin + mOrigExtent.width(),
549  yMin + mOrigExtent.height() );
550 
551  //scale newExtent to match original scale of map
552  //this is required for geographic coordinate systems, where the scale varies by extent
553  double newScale = calc.calculate( newExtent, map->rect().width() );
554  newExtent.scale( originalScale / newScale );
555  }
556  else if ( map->atlasScalingMode() == QgsComposerMap::Predefined )
557  {
558  // choose one of the predefined scales
559  double newWidth = mOrigExtent.width();
560  double newHeight = mOrigExtent.height();
561  const QVector<qreal>& scales = mPredefinedScales;
562  for ( int i = 0; i < scales.size(); i++ )
563  {
564  double ratio = scales[i] / originalScale;
565  newWidth = mOrigExtent.width() * ratio;
566  newHeight = mOrigExtent.height() * ratio;
567 
568  // compute new extent, centered on feature
569  double xMin = geomCenterX - newWidth / 2.0;
570  double yMin = geomCenterY - newHeight / 2.0;
571  newExtent = QgsRectangle( xMin,
572  yMin,
573  xMin + newWidth,
574  yMin + newHeight );
575 
576  //scale newExtent to match desired map scale
577  //this is required for geographic coordinate systems, where the scale varies by extent
578  double newScale = calc.calculate( newExtent, map->rect().width() );
579  newExtent.scale( scales[i] / newScale );
580 
581  if (( newExtent.width() >= mTransformedFeatureBounds.width() ) && ( newExtent.height() >= mTransformedFeatureBounds.height() ) )
582  {
583  // this is the smallest extent that embeds the feature, stop here
584  break;
585  }
586  }
587  }
588  }
589  else if ( map->atlasScalingMode() == QgsComposerMap::Auto )
590  {
591  // auto scale
592 
593  double geomRatio = mTransformedFeatureBounds.width() / mTransformedFeatureBounds.height();
594  double mapRatio = mOrigExtent.width() / mOrigExtent.height();
595 
596  // geometry height is too big
597  if ( geomRatio < mapRatio )
598  {
599  // extent the bbox's width
600  double adjWidth = ( mapRatio * mTransformedFeatureBounds.height() - mTransformedFeatureBounds.width() ) / 2.0;
601  xa1 -= adjWidth;
602  xa2 += adjWidth;
603  }
604  // geometry width is too big
605  else if ( geomRatio > mapRatio )
606  {
607  // extent the bbox's height
608  double adjHeight = ( mTransformedFeatureBounds.width() / mapRatio - mTransformedFeatureBounds.height() ) / 2.0;
609  ya1 -= adjHeight;
610  ya2 += adjHeight;
611  }
612  newExtent = QgsRectangle( xa1, ya1, xa2, ya2 );
613 
614  if ( map->atlasMargin() > 0.0 )
615  {
616  newExtent.scale( 1 + map->atlasMargin() );
617  }
618  }
619 
620  // set the new extent (and render)
621  map->setNewAtlasFeatureExtent( newExtent );
622 }
623 
625 {
626  return mCurrentFilename;
627 }
628 
630 {
631  QDomElement atlasElem = doc.createElement( "Atlas" );
632  atlasElem.setAttribute( "enabled", mEnabled ? "true" : "false" );
633  if ( !mEnabled )
634  {
635  return;
636  }
637 
638  if ( mCoverageLayer )
639  {
640  atlasElem.setAttribute( "coverageLayer", mCoverageLayer->id() );
641  }
642  else
643  {
644  atlasElem.setAttribute( "coverageLayer", "" );
645  }
646 
647  atlasElem.setAttribute( "hideCoverage", mHideCoverage ? "true" : "false" );
648  atlasElem.setAttribute( "singleFile", mSingleFile ? "true" : "false" );
649  atlasElem.setAttribute( "filenamePattern", mFilenamePattern );
650  atlasElem.setAttribute( "pageNameExpression", mPageNameExpression );
651 
652  atlasElem.setAttribute( "sortFeatures", mSortFeatures ? "true" : "false" );
653  if ( mSortFeatures )
654  {
655  atlasElem.setAttribute( "sortKey", mSortKeyAttributeName );
656  atlasElem.setAttribute( "sortAscending", mSortAscending ? "true" : "false" );
657  }
658  atlasElem.setAttribute( "filterFeatures", mFilterFeatures ? "true" : "false" );
659  if ( mFilterFeatures )
660  {
661  atlasElem.setAttribute( "featureFilter", mFeatureFilter );
662  }
663 
664  elem.appendChild( atlasElem );
665 }
666 
667 void QgsAtlasComposition::readXML( const QDomElement& atlasElem, const QDomDocument& )
668 {
669  mEnabled = atlasElem.attribute( "enabled", "false" ) == "true" ? true : false;
670  emit toggled( mEnabled );
671  if ( !mEnabled )
672  {
673  emit parameterChanged();
674  return;
675  }
676 
677  // look for stored layer name
678  mCoverageLayer = nullptr;
680  for ( QMap<QString, QgsMapLayer*>::const_iterator it = layers.begin(); it != layers.end(); ++it )
681  {
682  if ( it.key() == atlasElem.attribute( "coverageLayer" ) )
683  {
684  mCoverageLayer = dynamic_cast<QgsVectorLayer*>( it.value() );
685  break;
686  }
687  }
688 
689  mPageNameExpression = atlasElem.attribute( "pageNameExpression", QString() );
690  mSingleFile = atlasElem.attribute( "singleFile", "false" ) == "true" ? true : false;
691  mFilenamePattern = atlasElem.attribute( "filenamePattern", "" );
692 
693  mSortFeatures = atlasElem.attribute( "sortFeatures", "false" ) == "true" ? true : false;
694  if ( mSortFeatures )
695  {
696  mSortKeyAttributeName = atlasElem.attribute( "sortKey", "" );
697  // since 2.3, the field name is saved instead of the field index
698  // following code keeps compatibility with version 2.2 projects
699  // to be removed in QGIS 3.0
700  bool isIndex;
701  int idx = mSortKeyAttributeName.toInt( &isIndex );
702  if ( isIndex && mCoverageLayer )
703  {
704  QgsFields fields = mCoverageLayer->fields();
705  if ( idx >= 0 && idx < fields.count() )
706  {
707  mSortKeyAttributeName = fields.at( idx ).name();
708  }
709  }
710  mSortAscending = atlasElem.attribute( "sortAscending", "true" ) == "true" ? true : false;
711  }
712  mFilterFeatures = atlasElem.attribute( "filterFeatures", "false" ) == "true" ? true : false;
713  if ( mFilterFeatures )
714  {
715  mFeatureFilter = atlasElem.attribute( "featureFilter", "" );
716  }
717 
718  mHideCoverage = atlasElem.attribute( "hideCoverage", "false" ) == "true" ? true : false;
719 
720  emit parameterChanged();
721 }
722 
724 {
725  Q_UNUSED( doc );
726  //look for stored composer map, to upgrade pre 2.1 projects
727  int composerMapNo = elem.attribute( "composerMap", "-1" ).toInt();
728  QgsComposerMap * composerMap = nullptr;
729  if ( composerMapNo != -1 )
730  {
732  mComposition->composerItems( maps );
733  for ( QList<QgsComposerMap*>::iterator it = maps.begin(); it != maps.end(); ++it )
734  {
735  if (( *it )->id() == composerMapNo )
736  {
737  composerMap = ( *it );
738  composerMap->setAtlasDriven( true );
739  break;
740  }
741  }
742  }
743 
744  //upgrade pre 2.1 projects
745  double margin = elem.attribute( "margin", "0.0" ).toDouble();
746  if ( composerMap && !qgsDoubleNear( margin, 0.0 ) )
747  {
748  composerMap->setAtlasMargin( margin );
749  }
750  bool fixedScale = elem.attribute( "fixedScale", "false" ) == "true" ? true : false;
751  if ( composerMap && fixedScale )
752  {
754  }
755 }
756 
758 {
759  mHideCoverage = hide;
760 
761  if ( mComposition->atlasMode() == QgsComposition::PreviewAtlas )
762  {
763  //an atlas preview is enabled, so reflect changes in coverage layer visibility immediately
764  updateAtlasMaps();
765  mComposition->update();
766  }
767 
768 }
769 
771 {
772  mFilenamePattern = pattern;
773  return updateFilenameExpression();
774 }
775 
776 QgsExpressionContext QgsAtlasComposition::createExpressionContext()
777 {
778  QgsExpressionContext expressionContext;
779  expressionContext << QgsExpressionContextUtils::globalScope()
781  if ( mComposition )
782  expressionContext << QgsExpressionContextUtils::compositionScope( mComposition );
783 
784  expressionContext.appendScope( QgsExpressionContextUtils::atlasScope( this ) );
785  if ( mCoverageLayer )
786  expressionContext.lastScope()->setFields( mCoverageLayer->fields() );
787  if ( mComposition && mComposition->atlasMode() != QgsComposition::AtlasOff )
788  expressionContext.lastScope()->setFeature( mCurrentFeature );
789 
790  return expressionContext;
791 }
792 
793 bool QgsAtlasComposition::updateFilenameExpression()
794 {
795  if ( !mCoverageLayer )
796  {
797  return false;
798  }
799 
800  QgsExpressionContext expressionContext = createExpressionContext();
801 
802  if ( !mFilenamePattern.isEmpty() )
803  {
804  mFilenameExpr.reset( new QgsExpression( mFilenamePattern ) );
805  // expression used to evaluate each filename
806  // test for evaluation errors
807  if ( mFilenameExpr->hasParserError() )
808  {
809  mFilenameParserError = mFilenameExpr->parserErrorString();
810  return false;
811  }
812 
813  // prepare the filename expression
814  mFilenameExpr->prepare( &expressionContext );
815  }
816 
817  //if atlas preview is currently enabled, regenerate filename for current feature
818  if ( mComposition->atlasMode() == QgsComposition::PreviewAtlas )
819  {
820  evalFeatureFilename( expressionContext );
821  }
822  return true;
823 }
824 
825 bool QgsAtlasComposition::evalFeatureFilename( const QgsExpressionContext &context )
826 {
827  //generate filename for current atlas feature
828  if ( !mFilenamePattern.isEmpty() && !mFilenameExpr.isNull() )
829  {
830  QVariant filenameRes = mFilenameExpr->evaluate( &context );
831  if ( mFilenameExpr->hasEvalError() )
832  {
833  QgsMessageLog::logMessage( tr( "Atlas filename evaluation error: %1" ).arg( mFilenameExpr->evalErrorString() ), tr( "Composer" ) );
834  return false;
835  }
836 
837  mCurrentFilename = filenameRes.toString();
838  }
839  return true;
840 }
841 
843 {
844  mPredefinedScales = scales;
845  // make sure the list is sorted
846  qSort( mPredefinedScales.begin(), mPredefinedScales.end() );
847 }
848 
851 {
852  //deprecated method. Until removed just return the property for the first atlas-enabled composer map
853  QgsComposerMap * map = composerMap();
854  if ( !map )
855  {
856  return false;
857  }
858 
859  return map->atlasFixedScale();
860 }
861 
863 {
864  //deprecated method. Until removed just set the property for the first atlas-enabled composer map
865  QgsComposerMap * map = composerMap();
866  if ( !map )
867  {
868  return;
869  }
870 
872 }
873 
875 {
876  //deprecated method. Until removed just return the property for the first atlas-enabled composer map
877  QgsComposerMap * map = composerMap();
878  if ( !map )
879  {
880  return 0;
881  }
882 
883  return map->atlasMargin();
884 }
885 
887 {
888  //deprecated method. Until removed just set the property for the first atlas-enabled composer map
889  QgsComposerMap * map = composerMap();
890  if ( !map )
891  {
892  return;
893  }
894 
895  map->setAtlasMargin( static_cast< double >( margin ) );
896 }
897 
899 {
900  if ( !mCoverageLayer || !mCurrentFeature.isValid() || !mCurrentFeature.constGeometry() )
901  {
902  return QgsGeometry();
903  }
904 
905  if ( !crs.isValid() )
906  {
907  // no projection, return the native geometry
908  return *mCurrentFeature.constGeometry();
909  }
910 
911  QMap<long, QgsGeometry>::const_iterator it = mGeometryCache.constFind( crs.srsid() );
912  if ( it != mGeometryCache.constEnd() )
913  {
914  // we have it in cache, return it
915  return it.value();
916  }
917 
918  if ( mCoverageLayer->crs() == crs )
919  {
920  return *mCurrentFeature.constGeometry();
921  }
922 
923  QgsGeometry transformed = *mCurrentFeature.constGeometry();
924  transformed.transform( *QgsCoordinateTransformCache::instance()->transform( mCoverageLayer->crs().authid(), crs.authid() ) );
925  mGeometryCache[crs.srsid()] = transformed;
926  return transformed;
927 }
928 
bool prepareForFeature(const int i, const bool updateMaps=true)
Prepare the atlas map for the given feature.
QgsFeatureId id() const
Get the feature ID for this feature.
Definition: qgsfeature.cpp:65
Class for parsing and evaluation of expressions (formerly called "search strings").
void setMapUnits(QGis::UnitType mapUnits)
Set the map units.
Wrapper for iterator of features from vector data provider or vector layer.
A rectangle specified with double values.
Definition: qgsrectangle.h:35
void renderEnded()
Is emitted when atlas rendering has ended.
double atlasMargin(const QgsComposerObject::PropertyValueType valueType=QgsComposerObject::EvaluatedValue)
Returns the margin size (percentage) used when the map is in atlas mode.
Q_DECL_DEPRECATED int sortKeyAttributeIndex() const
int fieldNameIndex(const QString &fieldName) const
Utility method to get attribute index from name.
Definition: qgsfeature.cpp:286
QDomNode appendChild(const QDomNode &newChild)
static QgsExpressionContextScope * atlasScope(const QgsAtlasComposition *atlas)
Creates a new scope which contains variables and functions relating to a QgsAtlasComposition.
void setNewAtlasFeatureExtent(const QgsRectangle &extent)
Sets new Extent for the current atlas preview and changes width, height (and implicitely also scale)...
QgsAtlasComposition(QgsComposition *composition)
QString attribute(const QString &name, const QString &defValue) const
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
QgsFields fields() const
Returns the list of fields of this layer.
long srsid() const
Returns the SrsId, if available.
Q_DECL_DEPRECATED bool fixedScale() const
Returns whether the atlas map uses a fixed scale.
const QgsMapSettings & mapSettings() const
Return setting of QGIS map canvas.
QString nameForPage(int pageNumber) const
Returns the calculated name for a specified atlas page number.
void cache()
Create cache image.
const_iterator constEnd() const
void scale(double scaleFactor, const QgsPoint *c=nullptr)
Scale the rectangle around its center point.
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:474
void setDpi(double dpi)
Set the dpi to be used in scale calculations.
QgsComposition * composition()
bool enabled() const
Returns whether the atlas generation is enabled.
Container of fields for a vector layer.
Definition: qgsfield.h:187
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:76
const_iterator constFind(const Key &key) const
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the scope.
void toggled(bool)
Emitted when atlas is enabled or disabled.
void setHideCoverage(bool hide)
Sets whether the coverage layer should be hidden in map items in the composition. ...
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:187
QString currentPageName() const
Returns the name of the page for the current atlas feature.
bool qgsVariantGreaterThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is greater than the second.
Definition: qgis.cpp:331
static QgsCoordinateTransformCache * instance()
Definition: qgscrscache.cpp:22
double toDouble(bool *ok) const
QString tr(const char *sourceText, const char *disambiguation, int n)
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:348
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
Definition: qgis.cpp:263
QMap< QString, QgsMapLayer * > mapLayers()
Retrieve the mapLayers collection (mainly intended for use by projection)
void endRender()
Ends the rendering.
void readXML(const QDomElement &elem, const QDomDocument &doc)
Reads general atlas settings from xml.
Q_DECL_DEPRECATED void setFixedScale(bool fixed)
Sets whether the atlas map should use a fixed scale.
void reset(T *other)
Q_DECL_DEPRECATED float margin() const
Returns the margin for the atlas map.
void setAtlasMargin(double margin)
Sets the margin size (percentage) used when the map is in atlas mode.
bool setFilenamePattern(const QString &pattern)
Sets the filename expression used for generating output filenames for each atlas page.
Q_DECL_DEPRECATED void setSortKeyAttributeIndex(int idx)
void setCoverageLayer(QgsVectorLayer *layer)
Sets the coverage layer to use for the atlas features.
void setAtlasScalingMode(AtlasScalingMode mode)
Sets the current atlas scaling mode.
QgsExpressionContextScope * lastScope()
Returns the last scope added to the context.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
bool beginRender()
Begins the rendering.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
Q_DECL_DEPRECATED void setComposerMap(QgsComposerMap *map)
Sets the map used by the atlas.
double calculate(const QgsRectangle &mapExtent, int canvasWidth)
Calculate the scale denominator.
void prepareMap(QgsComposerMap *map)
Recalculates the bounds of an atlas driven map.
QgsAttributes attributes() const
Returns the feature&#39;s attributes.
Definition: qgsfeature.cpp:110
void setAttribute(const QString &name, const QString &value)
void refreshFeature()
Refreshes the current atlas feature, by refetching its attributes from the vector layer provider...
const QgsComposition * composition() const
Returns the composition the item is attached to.
const QgsCoordinateReferenceSystem & destinationCrs() const
returns CRS of destination coordinate reference system
QString name() const
Gets the name of the field.
Definition: qgsfield.cpp:84
int toInt(bool *ok, int base) const
bool isEmpty() const
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
const_iterator constEnd() const
static void logMessage(const QString &message, const QString &tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
This class wraps a request for features to a vector layer (or directly its vector data provider)...
int numFeatures() const
Returns the number of features in the coverage layer.
QString id() const
Get this layer&#39;s unique ID, this ID is used to access this layer from map layer registry.
int count() const
Return number of items.
Definition: qgsfield.cpp:365
Q_DECL_DEPRECATED bool atlasFixedScale() const
Returns true if the map uses a fixed scale when in atlas mode.
void setEnabled(bool enabled)
Sets whether the atlas is enabled.
iterator end()
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the scope.
int updateFeatures()
Requeries the current atlas coverage layer and applies filtering and sorting.
const QgsField & at(int i) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfield.cpp:385
void writeXML(QDomElement &elem, QDomDocument &doc) const
iterator begin()
Graphics scene for map printing.
QString currentFilename() const
Returns the current filename.
Object representing map window.
void push_back(QChar ch)
QgsGeometry currentGeometry(const QgsCoordinateReferenceSystem &projectedTo=QgsCoordinateReferenceSystem()) const
Returns the current atlas geometry in the given projection system (default to the coverage layer&#39;s CR...
iterator end()
void featureChanged(QgsFeature *feature)
Is emitted when the current atlas feature changes.
void coverageLayerChanged(QgsVectorLayer *layer)
Is emitted when the coverage layer for an atlas changes.
Calculates scale for a given combination of canvas size, map extent, and monitor dpi.
bool setAtlasMode(const QgsComposition::AtlasMode mode)
Sets the current atlas mode of the composition.
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:475
bool isNull() const
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
const T & at(int i) const
const_iterator constBegin() const
void renderBegun()
Is emitted when atlas rendering has begun.
static QgsMapLayerRegistry * instance()
Returns the instance pointer, creating the object on the first call.
qreal width() const
QgsRectangle extent() const
FieldSorter(QgsAtlasComposition::SorterKeys &keys, bool ascending=true)
Q_DECL_DEPRECATED QgsComposerMap * composerMap() const
Returns the map used by the atlas.
void readXMLMapSettings(const QDomElement &elem, const QDomDocument &doc)
Reads old (pre 2.2) map related atlas settings from xml.
Q_DECL_DEPRECATED void setMargin(float margin)
Sets the margin for the atlas map.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
AtlasScalingMode atlasScalingMode() const
Returns the current atlas scaling mode.
Class for storing a coordinate reference system (CRS)
int count(const T &value) const
QString authid() const
Returns the authority identifier for the CRS, which includes both the authority (eg EPSG) and the CRS...
void parameterChanged()
Emitted when one of the parameters changes.
int transform(const QgsCoordinateTransform &ct)
Transform this geometry as described by CoordinateTransform ct.
void numberFeaturesChanged(int numFeatures)
Is emitted when the number of features for the atlas changes.
bool atlasDriven() const
Returns whether the map extent is set to follow the current atlas feature.
int currentFeatureNumber() const
Returns the current feature number, where a value of 0 corresponds to the first feature.
static QgsExpressionContextScope * projectScope()
Creates a new scope which contains variables and functions relating to the current QGIS project...
void setAtlasDriven(bool enabled)
Sets whether the map extent will follow the current atlas feature.
QDomElement createElement(const QString &tagName)
bool nextFeature(QgsFeature &f)
void composerItems(QList< T * > &itemList)
Return composer items of a specific type.
double width() const
Width of the rectangle.
Definition: qgsrectangle.h:207
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
int size() const
Represents a vector layer which manages a vector based data sets.
int fieldNameIndex(const QString &fieldName) const
Returns the index of a field name or -1 if the field does not exist.
QString toString() const
void statusMsgChanged(const QString &message)
Is emitted when the atlas has an updated status bar message for the composer window.
iterator begin()
void setPredefinedScales(const QVector< qreal > &scales)
Sets the list of predefined scales for the atlas.
bool operator()(const QPair< QgsFeatureId, QString > &id1, const QPair< QgsFeatureId, QString > &id2)
double height() const
Height of the rectangle.
Definition: qgsrectangle.h:212
QRectF rect() const
const T value(const Key &key) const
static QgsExpressionContextScope * compositionScope(const QgsComposition *composition)
Creates a new scope which contains variables and functions relating to a QgsComposition.