QGIS API Documentation  2.17.0-Master (eef6f05)
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 
161 class FieldSorter
162 {
163  public:
164  FieldSorter( QgsAtlasComposition::SorterKeys& keys, bool ascending = true )
165  : mKeys( keys )
166  , mAscending( ascending )
167  {}
168 
169  bool operator()( const QPair< QgsFeatureId, QString > & id1, const QPair< QgsFeatureId, QString >& id2 )
170  {
171  return mAscending ? qgsVariantLessThan( mKeys.value( id1.first ), mKeys.value( id2.first ) )
172  : qgsVariantGreaterThan( mKeys.value( id1.first ), mKeys.value( id2.first ) );
173  }
174 
175  private:
177  bool mAscending;
178 };
179 
181 
183 {
184  //needs to be called when layer, filter, sort changes
185 
186  if ( !mCoverageLayer )
187  {
188  return 0;
189  }
190 
191  QgsExpressionContext expressionContext = createExpressionContext();
192 
193  updateFilenameExpression();
194 
195  // select all features with all attributes
196  QgsFeatureRequest req;
197 
198  QScopedPointer<QgsExpression> filterExpression;
199  if ( mFilterFeatures && !mFeatureFilter.isEmpty() )
200  {
201  filterExpression.reset( new QgsExpression( mFeatureFilter ) );
202  if ( filterExpression->hasParserError() )
203  {
204  mFilterParserError = filterExpression->parserErrorString();
205  return 0;
206  }
207 
208  //filter good to go
209  req.setFilterExpression( mFeatureFilter );
210  }
211  mFilterParserError = QString();
212 
213  QgsFeatureIterator fit = mCoverageLayer->getFeatures( req );
214 
215  QScopedPointer<QgsExpression> nameExpression;
216  if ( !mPageNameExpression.isEmpty() )
217  {
218  nameExpression.reset( new QgsExpression( mPageNameExpression ) );
219  if ( nameExpression->hasParserError() )
220  {
221  nameExpression.reset( nullptr );
222  }
223  else
224  {
225  nameExpression->prepare( &expressionContext );
226  }
227  }
228 
229  // We cannot use nextFeature() directly since the feature pointer is rewinded by the rendering process
230  // We thus store the feature ids for future extraction
231  QgsFeature feat;
232  mFeatureIds.clear();
233  mFeatureKeys.clear();
234  int sortIdx = mCoverageLayer->fieldNameIndex( mSortKeyAttributeName );
235 
236  while ( fit.nextFeature( feat ) )
237  {
238  expressionContext.setFeature( feat );
239 
240  QString pageName;
241  if ( !nameExpression.isNull() )
242  {
243  QVariant result = nameExpression->evaluate( &expressionContext );
244  if ( nameExpression->hasEvalError() )
245  {
246  QgsMessageLog::logMessage( tr( "Atlas name eval error: %1" ).arg( nameExpression->evalErrorString() ), tr( "Composer" ) );
247  }
248  pageName = result.toString();
249  }
250 
251  mFeatureIds.push_back( qMakePair( feat.id(), pageName ) );
252 
253  if ( mSortFeatures && sortIdx != -1 )
254  {
255  mFeatureKeys.insert( feat.id(), feat.attributes().at( sortIdx ) );
256  }
257  }
258 
259  // sort features, if asked for
260  if ( !mFeatureKeys.isEmpty() )
261  {
262  FieldSorter sorter( mFeatureKeys, mSortAscending );
263  qSort( mFeatureIds.begin(), mFeatureIds.end(), sorter );
264  }
265 
266  emit numberFeaturesChanged( mFeatureIds.size() );
267 
268  //jump to first feature if currently using an atlas preview
269  //need to do this in case filtering/layer change has altered matching features
270  if ( mComposition->atlasMode() == QgsComposition::PreviewAtlas )
271  {
272  firstFeature();
273  }
274 
275  return mFeatureIds.size();
276 }
277 
279 {
280  return nameForPage( currentFeatureNumber() );
281 }
282 
284 {
285  if ( !mCoverageLayer )
286  {
287  return false;
288  }
289 
290  emit renderBegun();
291 
292  bool featuresUpdated = updateFeatures();
293  if ( !featuresUpdated )
294  {
295  //no matching features found
296  return false;
297  }
298 
299  return true;
300 }
301 
303 {
304  if ( !mCoverageLayer )
305  {
306  return;
307  }
308 
309  emit featureChanged( nullptr );
310 
311  updateAtlasMaps();
312 
313  emit renderEnded();
314 }
315 
316 void QgsAtlasComposition::updateAtlasMaps()
317 {
318  //update atlas-enabled composer maps
320  mComposition->composerItems( maps );
321  for ( QList<QgsComposerMap*>::iterator mit = maps.begin(); mit != maps.end(); ++mit )
322  {
323  QgsComposerMap* currentMap = ( *mit );
324  if ( !currentMap->atlasDriven() )
325  {
326  continue;
327  }
328 
329  currentMap->cache();
330  }
331 }
332 
334 {
335  return mFeatureIds.size();
336 }
337 
339 {
340  int newFeatureNo = mCurrentFeatureNo + 1;
341  if ( newFeatureNo >= mFeatureIds.size() )
342  {
343  newFeatureNo = mFeatureIds.size() - 1;
344  }
345 
346  prepareForFeature( newFeatureNo );
347 }
348 
350 {
351  int newFeatureNo = mCurrentFeatureNo - 1;
352  if ( newFeatureNo < 0 )
353  {
354  newFeatureNo = 0;
355  }
356 
357  prepareForFeature( newFeatureNo );
358 }
359 
361 {
362  prepareForFeature( 0 );
363 }
364 
366 {
367  prepareForFeature( mFeatureIds.size() - 1 );
368 }
369 
371 {
372  int featureI = -1;
373  QVector< QPair<QgsFeatureId, QString> >::const_iterator it = mFeatureIds.constBegin();
374  int currentIdx = 0;
375  for ( ; it != mFeatureIds.constEnd(); ++it, ++currentIdx )
376  {
377  if (( *it ).first == feat->id() )
378  {
379  featureI = currentIdx;
380  break;
381  }
382  }
383 
384  if ( featureI < 0 )
385  {
386  //feature not found
387  return false;
388  }
389 
390  return prepareForFeature( featureI );
391 }
392 
394 {
395  prepareForFeature( mCurrentFeatureNo, false );
396 }
397 
398 bool QgsAtlasComposition::prepareForFeature( const int featureI, const bool updateMaps )
399 {
400  if ( !mCoverageLayer )
401  {
402  return false;
403  }
404 
405  if ( mFeatureIds.isEmpty() )
406  {
407  emit statusMsgChanged( tr( "No matching atlas features" ) );
408  return false;
409  }
410 
411  if ( featureI >= mFeatureIds.size() )
412  {
413  return false;
414  }
415 
416  mCurrentFeatureNo = featureI;
417 
418  // retrieve the next feature, based on its id
419  mCoverageLayer->getFeatures( QgsFeatureRequest().setFilterFid( mFeatureIds[ featureI ].first ) ).nextFeature( mCurrentFeature );
420 
421  QgsExpressionContext expressionContext = createExpressionContext();
422 
423  // generate filename for current feature
424  if ( !evalFeatureFilename( expressionContext ) )
425  {
426  //error evaluating filename
427  return false;
428  }
429 
430  mGeometryCache.clear();
431  emit featureChanged( &mCurrentFeature );
432  emit statusMsgChanged( QString( tr( "Atlas feature %1 of %2" ) ).arg( featureI + 1 ).arg( mFeatureIds.size() ) );
433 
434  if ( !mCurrentFeature.isValid() )
435  {
436  //bad feature
437  return true;
438  }
439 
440  if ( !updateMaps )
441  {
442  //nothing more to do
443  return true;
444  }
445 
446  //update composer maps
447 
448  //build a list of atlas-enabled composer maps
450  QList<QgsComposerMap*> atlasMaps;
451  mComposition->composerItems( maps );
452  if ( maps.isEmpty() )
453  {
454  return true;
455  }
456  for ( QList<QgsComposerMap*>::iterator mit = maps.begin(); mit != maps.end(); ++mit )
457  {
458  QgsComposerMap* currentMap = ( *mit );
459  if ( !currentMap->atlasDriven() )
460  {
461  continue;
462  }
463  atlasMaps << currentMap;
464  }
465 
466  if ( !atlasMaps.isEmpty() )
467  {
468  //clear the transformed bounds of the previous feature
469  mTransformedFeatureBounds = QgsRectangle();
470 
471  // compute extent of current feature in the map CRS. This should be set on a per-atlas map basis,
472  // but given that it's not currently possible to have maps with different CRSes we can just
473  // calculate it once based on the first atlas maps' CRS.
474  computeExtent( atlasMaps[0] );
475  }
476 
477  for ( QList<QgsComposerMap*>::iterator mit = maps.begin(); mit != maps.end(); ++mit )
478  {
479  if (( *mit )->atlasDriven() )
480  {
481  // map is atlas driven, so update it's bounds (causes a redraw)
482  prepareMap( *mit );
483  }
484  else
485  {
486  // map is not atlas driven, so manually force a redraw (to reflect possibly atlas
487  // dependent symbology)
488  ( *mit )->cache();
489  }
490  }
491 
492  return true;
493 }
494 
495 void QgsAtlasComposition::computeExtent( QgsComposerMap* map )
496 {
497  // QgsGeometry::boundingBox is expressed in the geometry"s native CRS
498  // We have to transform the grometry to the destination CRS and ask for the bounding box
499  // Note: we cannot directly take the transformation of the bounding box, since transformations are not linear
500  mTransformedFeatureBounds = currentGeometry( map->composition()->mapSettings().destinationCrs() ).boundingBox();
501 }
502 
504 {
505  if ( !map->atlasDriven() || mCoverageLayer->wkbType() == QGis::WKBNoGeometry )
506  {
507  return;
508  }
509 
510  if ( mTransformedFeatureBounds.isEmpty() )
511  {
512  //transformed extent of current feature hasn't been calculated yet. This can happen if
513  //a map has been set to be atlas controlled after prepare feature was called
514  computeExtent( map );
515  }
516 
517  double xa1 = mTransformedFeatureBounds.xMinimum();
518  double xa2 = mTransformedFeatureBounds.xMaximum();
519  double ya1 = mTransformedFeatureBounds.yMinimum();
520  double ya2 = mTransformedFeatureBounds.yMaximum();
521  QgsRectangle newExtent = mTransformedFeatureBounds;
522  QgsRectangle mOrigExtent( map->extent() );
523 
524  //sanity check - only allow fixed scale mode for point layers
525  bool isPointLayer = false;
526  switch ( mCoverageLayer->wkbType() )
527  {
528  case QGis::WKBPoint:
529  case QGis::WKBPoint25D:
530  case QGis::WKBMultiPoint:
532  isPointLayer = true;
533  break;
534  default:
535  isPointLayer = false;
536  break;
537  }
538 
539  if ( map->atlasScalingMode() == QgsComposerMap::Fixed || map->atlasScalingMode() == QgsComposerMap::Predefined || isPointLayer )
540  {
541  QgsScaleCalculator calc;
542  calc.setMapUnits( composition()->mapSettings().mapUnits() );
543  calc.setDpi( 25.4 );
544  double originalScale = calc.calculate( mOrigExtent, map->rect().width() );
545  double geomCenterX = ( xa1 + xa2 ) / 2.0;
546  double geomCenterY = ( ya1 + ya2 ) / 2.0;
547 
548  if ( map->atlasScalingMode() == QgsComposerMap::Fixed || isPointLayer )
549  {
550  // only translate, keep the original scale (i.e. width x height)
551  double xMin = geomCenterX - mOrigExtent.width() / 2.0;
552  double yMin = geomCenterY - mOrigExtent.height() / 2.0;
553  newExtent = QgsRectangle( xMin,
554  yMin,
555  xMin + mOrigExtent.width(),
556  yMin + mOrigExtent.height() );
557 
558  //scale newExtent to match original scale of map
559  //this is required for geographic coordinate systems, where the scale varies by extent
560  double newScale = calc.calculate( newExtent, map->rect().width() );
561  newExtent.scale( originalScale / newScale );
562  }
563  else if ( map->atlasScalingMode() == QgsComposerMap::Predefined )
564  {
565  // choose one of the predefined scales
566  double newWidth = mOrigExtent.width();
567  double newHeight = mOrigExtent.height();
568  const QVector<qreal>& scales = mPredefinedScales;
569  for ( int i = 0; i < scales.size(); i++ )
570  {
571  double ratio = scales[i] / originalScale;
572  newWidth = mOrigExtent.width() * ratio;
573  newHeight = mOrigExtent.height() * ratio;
574 
575  // compute new extent, centered on feature
576  double xMin = geomCenterX - newWidth / 2.0;
577  double yMin = geomCenterY - newHeight / 2.0;
578  newExtent = QgsRectangle( xMin,
579  yMin,
580  xMin + newWidth,
581  yMin + newHeight );
582 
583  //scale newExtent to match desired map scale
584  //this is required for geographic coordinate systems, where the scale varies by extent
585  double newScale = calc.calculate( newExtent, map->rect().width() );
586  newExtent.scale( scales[i] / newScale );
587 
588  if (( newExtent.width() >= mTransformedFeatureBounds.width() ) && ( newExtent.height() >= mTransformedFeatureBounds.height() ) )
589  {
590  // this is the smallest extent that embeds the feature, stop here
591  break;
592  }
593  }
594  }
595  }
596  else if ( map->atlasScalingMode() == QgsComposerMap::Auto )
597  {
598  // auto scale
599 
600  double geomRatio = mTransformedFeatureBounds.width() / mTransformedFeatureBounds.height();
601  double mapRatio = mOrigExtent.width() / mOrigExtent.height();
602 
603  // geometry height is too big
604  if ( geomRatio < mapRatio )
605  {
606  // extent the bbox's width
607  double adjWidth = ( mapRatio * mTransformedFeatureBounds.height() - mTransformedFeatureBounds.width() ) / 2.0;
608  xa1 -= adjWidth;
609  xa2 += adjWidth;
610  }
611  // geometry width is too big
612  else if ( geomRatio > mapRatio )
613  {
614  // extent the bbox's height
615  double adjHeight = ( mTransformedFeatureBounds.width() / mapRatio - mTransformedFeatureBounds.height() ) / 2.0;
616  ya1 -= adjHeight;
617  ya2 += adjHeight;
618  }
619  newExtent = QgsRectangle( xa1, ya1, xa2, ya2 );
620 
621  if ( map->atlasMargin() > 0.0 )
622  {
623  newExtent.scale( 1 + map->atlasMargin() );
624  }
625  }
626 
627  // set the new extent (and render)
628  map->setNewAtlasFeatureExtent( newExtent );
629 }
630 
632 {
633  return mCurrentFilename;
634 }
635 
637 {
638  QDomElement atlasElem = doc.createElement( "Atlas" );
639  atlasElem.setAttribute( "enabled", mEnabled ? "true" : "false" );
640  if ( !mEnabled )
641  {
642  return;
643  }
644 
645  if ( mCoverageLayer )
646  {
647  atlasElem.setAttribute( "coverageLayer", mCoverageLayer->id() );
648  }
649  else
650  {
651  atlasElem.setAttribute( "coverageLayer", "" );
652  }
653 
654  atlasElem.setAttribute( "hideCoverage", mHideCoverage ? "true" : "false" );
655  atlasElem.setAttribute( "singleFile", mSingleFile ? "true" : "false" );
656  atlasElem.setAttribute( "filenamePattern", mFilenamePattern );
657  atlasElem.setAttribute( "pageNameExpression", mPageNameExpression );
658 
659  atlasElem.setAttribute( "sortFeatures", mSortFeatures ? "true" : "false" );
660  if ( mSortFeatures )
661  {
662  atlasElem.setAttribute( "sortKey", mSortKeyAttributeName );
663  atlasElem.setAttribute( "sortAscending", mSortAscending ? "true" : "false" );
664  }
665  atlasElem.setAttribute( "filterFeatures", mFilterFeatures ? "true" : "false" );
666  if ( mFilterFeatures )
667  {
668  atlasElem.setAttribute( "featureFilter", mFeatureFilter );
669  }
670 
671  elem.appendChild( atlasElem );
672 }
673 
674 void QgsAtlasComposition::readXML( const QDomElement& atlasElem, const QDomDocument& )
675 {
676  mEnabled = atlasElem.attribute( "enabled", "false" ) == "true" ? true : false;
677  emit toggled( mEnabled );
678  if ( !mEnabled )
679  {
680  emit parameterChanged();
681  return;
682  }
683 
684  // look for stored layer name
685  mCoverageLayer = nullptr;
687  for ( QMap<QString, QgsMapLayer*>::const_iterator it = layers.begin(); it != layers.end(); ++it )
688  {
689  if ( it.key() == atlasElem.attribute( "coverageLayer" ) )
690  {
691  mCoverageLayer = dynamic_cast<QgsVectorLayer*>( it.value() );
692  break;
693  }
694  }
695 
696  mPageNameExpression = atlasElem.attribute( "pageNameExpression", QString() );
697  mSingleFile = atlasElem.attribute( "singleFile", "false" ) == "true" ? true : false;
698  mFilenamePattern = atlasElem.attribute( "filenamePattern", "" );
699 
700  mSortFeatures = atlasElem.attribute( "sortFeatures", "false" ) == "true" ? true : false;
701  if ( mSortFeatures )
702  {
703  mSortKeyAttributeName = atlasElem.attribute( "sortKey", "" );
704  // since 2.3, the field name is saved instead of the field index
705  // following code keeps compatibility with version 2.2 projects
706  // to be removed in QGIS 3.0
707  bool isIndex;
708  int idx = mSortKeyAttributeName.toInt( &isIndex );
709  if ( isIndex && mCoverageLayer )
710  {
711  QgsFields fields = mCoverageLayer->fields();
712  if ( idx >= 0 && idx < fields.count() )
713  {
714  mSortKeyAttributeName = fields.at( idx ).name();
715  }
716  }
717  mSortAscending = atlasElem.attribute( "sortAscending", "true" ) == "true" ? true : false;
718  }
719  mFilterFeatures = atlasElem.attribute( "filterFeatures", "false" ) == "true" ? true : false;
720  if ( mFilterFeatures )
721  {
722  mFeatureFilter = atlasElem.attribute( "featureFilter", "" );
723  }
724 
725  mHideCoverage = atlasElem.attribute( "hideCoverage", "false" ) == "true" ? true : false;
726 
727  emit parameterChanged();
728 }
729 
731 {
732  Q_UNUSED( doc );
733  //look for stored composer map, to upgrade pre 2.1 projects
734  int composerMapNo = elem.attribute( "composerMap", "-1" ).toInt();
735  QgsComposerMap * composerMap = nullptr;
736  if ( composerMapNo != -1 )
737  {
739  mComposition->composerItems( maps );
740  for ( QList<QgsComposerMap*>::iterator it = maps.begin(); it != maps.end(); ++it )
741  {
742  if (( *it )->id() == composerMapNo )
743  {
744  composerMap = ( *it );
745  composerMap->setAtlasDriven( true );
746  break;
747  }
748  }
749  }
750 
751  //upgrade pre 2.1 projects
752  double margin = elem.attribute( "margin", "0.0" ).toDouble();
753  if ( composerMap && !qgsDoubleNear( margin, 0.0 ) )
754  {
755  composerMap->setAtlasMargin( margin );
756  }
757  bool fixedScale = elem.attribute( "fixedScale", "false" ) == "true" ? true : false;
758  if ( composerMap && fixedScale )
759  {
761  }
762 }
763 
765 {
766  mHideCoverage = hide;
767 
768  if ( mComposition->atlasMode() == QgsComposition::PreviewAtlas )
769  {
770  //an atlas preview is enabled, so reflect changes in coverage layer visibility immediately
771  updateAtlasMaps();
772  mComposition->update();
773  }
774 
775 }
776 
778 {
779  mFilenamePattern = pattern;
780  return updateFilenameExpression();
781 }
782 
783 QgsExpressionContext QgsAtlasComposition::createExpressionContext()
784 {
785  QgsExpressionContext expressionContext;
786  expressionContext << QgsExpressionContextUtils::globalScope()
788  if ( mComposition )
789  expressionContext << QgsExpressionContextUtils::compositionScope( mComposition );
790 
791  expressionContext.appendScope( QgsExpressionContextUtils::atlasScope( this ) );
792  if ( mCoverageLayer )
793  expressionContext.lastScope()->setFields( mCoverageLayer->fields() );
794  if ( mComposition && mComposition->atlasMode() != QgsComposition::AtlasOff )
795  expressionContext.lastScope()->setFeature( mCurrentFeature );
796 
797  return expressionContext;
798 }
799 
800 bool QgsAtlasComposition::updateFilenameExpression()
801 {
802  if ( !mCoverageLayer )
803  {
804  return false;
805  }
806 
807  QgsExpressionContext expressionContext = createExpressionContext();
808 
809  if ( !mFilenamePattern.isEmpty() )
810  {
811  mFilenameExpr.reset( new QgsExpression( mFilenamePattern ) );
812  // expression used to evaluate each filename
813  // test for evaluation errors
814  if ( mFilenameExpr->hasParserError() )
815  {
816  mFilenameParserError = mFilenameExpr->parserErrorString();
817  return false;
818  }
819 
820  // prepare the filename expression
821  mFilenameExpr->prepare( &expressionContext );
822  }
823 
824  //if atlas preview is currently enabled, regenerate filename for current feature
825  if ( mComposition->atlasMode() == QgsComposition::PreviewAtlas )
826  {
827  evalFeatureFilename( expressionContext );
828  }
829  return true;
830 }
831 
832 bool QgsAtlasComposition::evalFeatureFilename( const QgsExpressionContext &context )
833 {
834  //generate filename for current atlas feature
835  if ( !mFilenamePattern.isEmpty() && !mFilenameExpr.isNull() )
836  {
837  QVariant filenameRes = mFilenameExpr->evaluate( &context );
838  if ( mFilenameExpr->hasEvalError() )
839  {
840  QgsMessageLog::logMessage( tr( "Atlas filename evaluation error: %1" ).arg( mFilenameExpr->evalErrorString() ), tr( "Composer" ) );
841  return false;
842  }
843 
844  mCurrentFilename = filenameRes.toString();
845  }
846  return true;
847 }
848 
850 {
851  mPredefinedScales = scales;
852  // make sure the list is sorted
853  qSort( mPredefinedScales.begin(), mPredefinedScales.end() );
854 }
855 
858 {
859  //deprecated method. Until removed just return the property for the first atlas-enabled composer map
860  QgsComposerMap * map = composerMap();
861  if ( !map )
862  {
863  return false;
864  }
865 
866  return map->atlasFixedScale();
867 }
868 
870 {
871  //deprecated method. Until removed just set the property for the first atlas-enabled composer map
872  QgsComposerMap * map = composerMap();
873  if ( !map )
874  {
875  return;
876  }
877 
879 }
880 
882 {
883  //deprecated method. Until removed just return the property for the first atlas-enabled composer map
884  QgsComposerMap * map = composerMap();
885  if ( !map )
886  {
887  return 0;
888  }
889 
890  return map->atlasMargin();
891 }
892 
894 {
895  //deprecated method. Until removed just set the property for the first atlas-enabled composer map
896  QgsComposerMap * map = composerMap();
897  if ( !map )
898  {
899  return;
900  }
901 
902  map->setAtlasMargin( static_cast< double >( margin ) );
903 }
904 
906 {
907  if ( !mCoverageLayer || !mCurrentFeature.isValid() || !mCurrentFeature.constGeometry() )
908  {
909  return QgsGeometry();
910  }
911 
912  if ( !crs.isValid() )
913  {
914  // no projection, return the native geometry
915  return *mCurrentFeature.constGeometry();
916  }
917 
918  QMap<long, QgsGeometry>::const_iterator it = mGeometryCache.constFind( crs.srsid() );
919  if ( it != mGeometryCache.constEnd() )
920  {
921  // we have it in cache, return it
922  return it.value();
923  }
924 
925  if ( mCoverageLayer->crs() == crs )
926  {
927  return *mCurrentFeature.constGeometry();
928  }
929 
930  QgsGeometry transformed = *mCurrentFeature.constGeometry();
931  transformed.transform( *QgsCoordinateTransformCache::instance()->transform( mCoverageLayer->crs().authid(), crs.authid() ) );
932  mGeometryCache[crs.srsid()] = transformed;
933  return transformed;
934 }
935 
bool prepareForFeature(const int i, const bool updateMaps=true)
Prepare the atlas map for the given feature.
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.
bool atlasDriven() const
Returns whether the map extent is set to follow the current atlas feature.
A rectangle specified with double values.
Definition: qgsrectangle.h:35
QgsAttributes attributes() const
Returns the feature&#39;s attributes.
Definition: qgsfeature.cpp:110
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.
void composerItems(QList< T *> &itemList)
Return composer items of a specific type.
QString name
Definition: qgsfield.h:52
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.
QgsRectangle extent() const
void cache()
Create cache image.
const_iterator constEnd() const
QString currentPageName() const
Returns the name of the page for the current atlas feature.
void scale(double scaleFactor, const QgsPoint *c=nullptr)
Scale the rectangle around its center point.
QString nameForPage(int pageNumber) const
Returns the calculated name for a specified atlas page number.
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:515
void setDpi(double dpi)
Set the dpi to be used in scale calculations.
QgsComposition * composition()
QString currentFilename() const
Returns the current filename.
Container of fields for a vector layer.
Definition: qgsfield.h:252
QMap< QString, QgsMapLayer * > mapLayers() const
Returns a map of all registered layers by layer ID.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:76
int numFeatures() const
Returns the number of features in the coverage layer.
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
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:337
static QgsCoordinateTransformCache * instance()
Definition: qgscrscache.cpp:22
double toDouble(bool *ok) const
int count() const
Return number of items.
Definition: qgsfield.cpp:402
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:353
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:269
const QgsField & at(int i) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfield.cpp:422
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)
QgsFields fields() const
Returns the list of fields of this layer.
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.
QString id() const
Get this layer&#39;s unique ID, this ID is used to access this layer from map layer registry.
double calculate(const QgsRectangle &mapExtent, int canvasWidth)
Calculate the scale denominator.
void prepareMap(QgsComposerMap *map)
Recalculates the bounds of an atlas driven map.
void setAttribute(const QString &name, const QString &value)
void refreshFeature()
Refreshes the current atlas feature, by refetching its attributes from the vector layer provider...
double width() const
Width of the rectangle.
Definition: qgsrectangle.h:207
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)
Q_DECL_DEPRECATED bool atlasFixedScale() const
Returns true if the map uses a fixed scale when in atlas mode.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
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.
iterator begin()
Graphics scene for map printing.
const QgsMapSettings & mapSettings() const
Return setting of QGIS map canvas.
Object representing map window.
void push_back(QChar ch)
int currentFeatureNumber() const
Returns the current feature number, where a value of 0 corresponds to the first feature.
iterator end()
QgsFeatureId id() const
Get the feature ID for this feature.
Definition: qgsfeature.cpp:65
void featureChanged(QgsFeature *feature)
Is emitted when the current atlas feature changes.
Q_DECL_DEPRECATED bool fixedScale() const
Returns whether the atlas map uses a fixed scale.
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:516
int fieldNameIndex(const QString &fieldName) const
Utility method to get attribute index from name.
Definition: qgsfeature.cpp:286
bool isNull() const
const T & at(int i) const
const_iterator constBegin() const
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...
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
const QgsComposition * composition() const
Returns the composition the item is attached to.
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.
AtlasScalingMode atlasScalingMode() const
Returns the current atlas scaling mode.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
Class for storing a coordinate reference system (CRS)
int count(const T &value) const
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.
Q_DECL_DEPRECATED QgsComposerMap * composerMap() const
Returns the map used by the atlas.
static QgsExpressionContextScope * projectScope()
Creates a new scope which contains variables and functions relating to the current QGIS project...
Q_DECL_DEPRECATED int sortKeyAttributeIndex() const
void setAtlasDriven(bool enabled)
Sets whether the map extent will follow the current atlas feature.
Q_DECL_DEPRECATED float margin() const
Returns the margin for the atlas map.
QDomElement createElement(const QString &tagName)
bool nextFeature(QgsFeature &f)
long srsid() const
Returns the SrsId, if available.
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
int size() const
void writeXML(QDomElement &elem, QDomDocument &doc) const
Represents a vector layer which manages a vector based data sets.
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()
const QgsCoordinateReferenceSystem & destinationCrs() const
returns CRS of destination coordinate reference system
QString authid() const
Returns the authority identifier for the CRS, which includes both the authority (eg EPSG) and the CRS...
int fieldNameIndex(const QString &fieldName) const
Returns the index of a field name or -1 if the field does not exist.
bool enabled() const
Returns whether the atlas generation is enabled.
void setPredefinedScales(const QVector< qreal > &scales)
Sets the list of predefined scales for the atlas.
double height() const
Height of the rectangle.
Definition: qgsrectangle.h:212
QRectF rect() const
const T value(const Key &key) const
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
static QgsExpressionContextScope * compositionScope(const QgsComposition *composition)
Creates a new scope which contains variables and functions relating to a QgsComposition.