QGIS API Documentation  2.17.0-Master (8784312)
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  nameExpression->prepare( &expressionContext );
224  }
225 
226  // We cannot use nextFeature() directly since the feature pointer is rewinded by the rendering process
227  // We thus store the feature ids for future extraction
228  QgsFeature feat;
229  mFeatureIds.clear();
230  mFeatureKeys.clear();
231  int sortIdx = mCoverageLayer->fieldNameIndex( mSortKeyAttributeName );
232 
233  while ( fit.nextFeature( feat ) )
234  {
235  expressionContext.setFeature( feat );
236 
237  QString pageName;
238  if ( !nameExpression.isNull() )
239  {
240  QVariant result = nameExpression->evaluate( &expressionContext );
241  if ( nameExpression->hasEvalError() )
242  {
243  QgsMessageLog::logMessage( tr( "Atlas name eval error: %1" ).arg( nameExpression->evalErrorString() ), tr( "Composer" ) );
244  }
245  pageName = result.toString();
246  }
247 
248  mFeatureIds.push_back( qMakePair( feat.id(), pageName ) );
249 
250  if ( mSortFeatures && sortIdx != -1 )
251  {
252  mFeatureKeys.insert( feat.id(), feat.attributes().at( sortIdx ) );
253  }
254  }
255 
256  // sort features, if asked for
257  if ( !mFeatureKeys.isEmpty() )
258  {
259  FieldSorter sorter( mFeatureKeys, mSortAscending );
260  qSort( mFeatureIds.begin(), mFeatureIds.end(), sorter );
261  }
262 
263  emit numberFeaturesChanged( mFeatureIds.size() );
264 
265  //jump to first feature if currently using an atlas preview
266  //need to do this in case filtering/layer change has altered matching features
267  if ( mComposition->atlasMode() == QgsComposition::PreviewAtlas )
268  {
269  firstFeature();
270  }
271 
272  return mFeatureIds.size();
273 }
274 
276 {
277  return nameForPage( currentFeatureNumber() );
278 }
279 
281 {
282  if ( !mCoverageLayer )
283  {
284  return false;
285  }
286 
287  emit renderBegun();
288 
289  bool featuresUpdated = updateFeatures();
290  if ( !featuresUpdated )
291  {
292  //no matching features found
293  return false;
294  }
295 
296  return true;
297 }
298 
300 {
301  if ( !mCoverageLayer )
302  {
303  return;
304  }
305 
306  emit featureChanged( nullptr );
307 
308  updateAtlasMaps();
309 
310  emit renderEnded();
311 }
312 
313 void QgsAtlasComposition::updateAtlasMaps()
314 {
315  //update atlas-enabled composer maps
317  mComposition->composerItems( maps );
318  for ( QList<QgsComposerMap*>::iterator mit = maps.begin(); mit != maps.end(); ++mit )
319  {
320  QgsComposerMap* currentMap = ( *mit );
321  if ( !currentMap->atlasDriven() )
322  {
323  continue;
324  }
325 
326  currentMap->cache();
327  }
328 }
329 
331 {
332  return mFeatureIds.size();
333 }
334 
336 {
337  int newFeatureNo = mCurrentFeatureNo + 1;
338  if ( newFeatureNo >= mFeatureIds.size() )
339  {
340  newFeatureNo = mFeatureIds.size() - 1;
341  }
342 
343  prepareForFeature( newFeatureNo );
344 }
345 
347 {
348  int newFeatureNo = mCurrentFeatureNo - 1;
349  if ( newFeatureNo < 0 )
350  {
351  newFeatureNo = 0;
352  }
353 
354  prepareForFeature( newFeatureNo );
355 }
356 
358 {
359  prepareForFeature( 0 );
360 }
361 
363 {
364  prepareForFeature( mFeatureIds.size() - 1 );
365 }
366 
368 {
369  int featureI = -1;
370  QVector< QPair<QgsFeatureId, QString> >::const_iterator it = mFeatureIds.constBegin();
371  int currentIdx = 0;
372  for ( ; it != mFeatureIds.constEnd(); ++it, ++currentIdx )
373  {
374  if (( *it ).first == feat->id() )
375  {
376  featureI = currentIdx;
377  break;
378  }
379  }
380 
381  if ( featureI < 0 )
382  {
383  //feature not found
384  return false;
385  }
386 
387  return prepareForFeature( featureI );
388 }
389 
391 {
392  prepareForFeature( mCurrentFeatureNo, false );
393 }
394 
395 bool QgsAtlasComposition::prepareForFeature( const int featureI, const bool updateMaps )
396 {
397  if ( !mCoverageLayer )
398  {
399  return false;
400  }
401 
402  if ( mFeatureIds.isEmpty() )
403  {
404  emit statusMsgChanged( tr( "No matching atlas features" ) );
405  return false;
406  }
407 
408  if ( featureI >= mFeatureIds.size() )
409  {
410  return false;
411  }
412 
413  mCurrentFeatureNo = featureI;
414 
415  // retrieve the next feature, based on its id
416  mCoverageLayer->getFeatures( QgsFeatureRequest().setFilterFid( mFeatureIds[ featureI ].first ) ).nextFeature( mCurrentFeature );
417 
418  QgsExpressionContext expressionContext = createExpressionContext();
419 
420  // generate filename for current feature
421  if ( !evalFeatureFilename( expressionContext ) )
422  {
423  //error evaluating filename
424  return false;
425  }
426 
427  mGeometryCache.clear();
428  emit featureChanged( &mCurrentFeature );
429  emit statusMsgChanged( QString( tr( "Atlas feature %1 of %2" ) ).arg( featureI + 1 ).arg( mFeatureIds.size() ) );
430 
431  if ( !mCurrentFeature.isValid() )
432  {
433  //bad feature
434  return true;
435  }
436 
437  if ( !updateMaps )
438  {
439  //nothing more to do
440  return true;
441  }
442 
443  //update composer maps
444 
445  //build a list of atlas-enabled composer maps
447  QList<QgsComposerMap*> atlasMaps;
448  mComposition->composerItems( maps );
449  if ( maps.isEmpty() )
450  {
451  return true;
452  }
453  for ( QList<QgsComposerMap*>::iterator mit = maps.begin(); mit != maps.end(); ++mit )
454  {
455  QgsComposerMap* currentMap = ( *mit );
456  if ( !currentMap->atlasDriven() )
457  {
458  continue;
459  }
460  atlasMaps << currentMap;
461  }
462 
463  if ( !atlasMaps.isEmpty() )
464  {
465  //clear the transformed bounds of the previous feature
466  mTransformedFeatureBounds = QgsRectangle();
467 
468  // compute extent of current feature in the map CRS. This should be set on a per-atlas map basis,
469  // but given that it's not currently possible to have maps with different CRSes we can just
470  // calculate it once based on the first atlas maps' CRS.
471  computeExtent( atlasMaps[0] );
472  }
473 
474  for ( QList<QgsComposerMap*>::iterator mit = maps.begin(); mit != maps.end(); ++mit )
475  {
476  if (( *mit )->atlasDriven() )
477  {
478  // map is atlas driven, so update it's bounds (causes a redraw)
479  prepareMap( *mit );
480  }
481  else
482  {
483  // map is not atlas driven, so manually force a redraw (to reflect possibly atlas
484  // dependent symbology)
485  ( *mit )->cache();
486  }
487  }
488 
489  return true;
490 }
491 
492 void QgsAtlasComposition::computeExtent( QgsComposerMap* map )
493 {
494  // QgsGeometry::boundingBox is expressed in the geometry"s native CRS
495  // We have to transform the grometry to the destination CRS and ask for the bounding box
496  // Note: we cannot directly take the transformation of the bounding box, since transformations are not linear
497  mTransformedFeatureBounds = currentGeometry( map->composition()->mapSettings().destinationCrs() ).boundingBox();
498 }
499 
501 {
502  if ( !map->atlasDriven() || mCoverageLayer->wkbType() == QGis::WKBNoGeometry )
503  {
504  return;
505  }
506 
507  if ( mTransformedFeatureBounds.isEmpty() )
508  {
509  //transformed extent of current feature hasn't been calculated yet. This can happen if
510  //a map has been set to be atlas controlled after prepare feature was called
511  computeExtent( map );
512  }
513 
514  double xa1 = mTransformedFeatureBounds.xMinimum();
515  double xa2 = mTransformedFeatureBounds.xMaximum();
516  double ya1 = mTransformedFeatureBounds.yMinimum();
517  double ya2 = mTransformedFeatureBounds.yMaximum();
518  QgsRectangle newExtent = mTransformedFeatureBounds;
519  QgsRectangle mOrigExtent( map->extent() );
520 
521  //sanity check - only allow fixed scale mode for point layers
522  bool isPointLayer = false;
523  switch ( mCoverageLayer->wkbType() )
524  {
525  case QGis::WKBPoint:
526  case QGis::WKBPoint25D:
527  case QGis::WKBMultiPoint:
529  isPointLayer = true;
530  break;
531  default:
532  isPointLayer = false;
533  break;
534  }
535 
536  if ( map->atlasScalingMode() == QgsComposerMap::Fixed || map->atlasScalingMode() == QgsComposerMap::Predefined || isPointLayer )
537  {
538  QgsScaleCalculator calc;
539  calc.setMapUnits( composition()->mapSettings().mapUnits() );
540  calc.setDpi( 25.4 );
541  double originalScale = calc.calculate( mOrigExtent, map->rect().width() );
542  double geomCenterX = ( xa1 + xa2 ) / 2.0;
543  double geomCenterY = ( ya1 + ya2 ) / 2.0;
544 
545  if ( map->atlasScalingMode() == QgsComposerMap::Fixed || isPointLayer )
546  {
547  // only translate, keep the original scale (i.e. width x height)
548  double xMin = geomCenterX - mOrigExtent.width() / 2.0;
549  double yMin = geomCenterY - mOrigExtent.height() / 2.0;
550  newExtent = QgsRectangle( xMin,
551  yMin,
552  xMin + mOrigExtent.width(),
553  yMin + mOrigExtent.height() );
554 
555  //scale newExtent to match original scale of map
556  //this is required for geographic coordinate systems, where the scale varies by extent
557  double newScale = calc.calculate( newExtent, map->rect().width() );
558  newExtent.scale( originalScale / newScale );
559  }
560  else if ( map->atlasScalingMode() == QgsComposerMap::Predefined )
561  {
562  // choose one of the predefined scales
563  double newWidth = mOrigExtent.width();
564  double newHeight = mOrigExtent.height();
565  const QVector<qreal>& scales = mPredefinedScales;
566  for ( int i = 0; i < scales.size(); i++ )
567  {
568  double ratio = scales[i] / originalScale;
569  newWidth = mOrigExtent.width() * ratio;
570  newHeight = mOrigExtent.height() * ratio;
571 
572  // compute new extent, centered on feature
573  double xMin = geomCenterX - newWidth / 2.0;
574  double yMin = geomCenterY - newHeight / 2.0;
575  newExtent = QgsRectangle( xMin,
576  yMin,
577  xMin + newWidth,
578  yMin + newHeight );
579 
580  //scale newExtent to match desired map scale
581  //this is required for geographic coordinate systems, where the scale varies by extent
582  double newScale = calc.calculate( newExtent, map->rect().width() );
583  newExtent.scale( scales[i] / newScale );
584 
585  if (( newExtent.width() >= mTransformedFeatureBounds.width() ) && ( newExtent.height() >= mTransformedFeatureBounds.height() ) )
586  {
587  // this is the smallest extent that embeds the feature, stop here
588  break;
589  }
590  }
591  }
592  }
593  else if ( map->atlasScalingMode() == QgsComposerMap::Auto )
594  {
595  // auto scale
596 
597  double geomRatio = mTransformedFeatureBounds.width() / mTransformedFeatureBounds.height();
598  double mapRatio = mOrigExtent.width() / mOrigExtent.height();
599 
600  // geometry height is too big
601  if ( geomRatio < mapRatio )
602  {
603  // extent the bbox's width
604  double adjWidth = ( mapRatio * mTransformedFeatureBounds.height() - mTransformedFeatureBounds.width() ) / 2.0;
605  xa1 -= adjWidth;
606  xa2 += adjWidth;
607  }
608  // geometry width is too big
609  else if ( geomRatio > mapRatio )
610  {
611  // extent the bbox's height
612  double adjHeight = ( mTransformedFeatureBounds.width() / mapRatio - mTransformedFeatureBounds.height() ) / 2.0;
613  ya1 -= adjHeight;
614  ya2 += adjHeight;
615  }
616  newExtent = QgsRectangle( xa1, ya1, xa2, ya2 );
617 
618  if ( map->atlasMargin() > 0.0 )
619  {
620  newExtent.scale( 1 + map->atlasMargin() );
621  }
622  }
623 
624  // set the new extent (and render)
625  map->setNewAtlasFeatureExtent( newExtent );
626 }
627 
629 {
630  return mCurrentFilename;
631 }
632 
634 {
635  QDomElement atlasElem = doc.createElement( "Atlas" );
636  atlasElem.setAttribute( "enabled", mEnabled ? "true" : "false" );
637  if ( !mEnabled )
638  {
639  return;
640  }
641 
642  if ( mCoverageLayer )
643  {
644  atlasElem.setAttribute( "coverageLayer", mCoverageLayer->id() );
645  }
646  else
647  {
648  atlasElem.setAttribute( "coverageLayer", "" );
649  }
650 
651  atlasElem.setAttribute( "hideCoverage", mHideCoverage ? "true" : "false" );
652  atlasElem.setAttribute( "singleFile", mSingleFile ? "true" : "false" );
653  atlasElem.setAttribute( "filenamePattern", mFilenamePattern );
654  atlasElem.setAttribute( "pageNameExpression", mPageNameExpression );
655 
656  atlasElem.setAttribute( "sortFeatures", mSortFeatures ? "true" : "false" );
657  if ( mSortFeatures )
658  {
659  atlasElem.setAttribute( "sortKey", mSortKeyAttributeName );
660  atlasElem.setAttribute( "sortAscending", mSortAscending ? "true" : "false" );
661  }
662  atlasElem.setAttribute( "filterFeatures", mFilterFeatures ? "true" : "false" );
663  if ( mFilterFeatures )
664  {
665  atlasElem.setAttribute( "featureFilter", mFeatureFilter );
666  }
667 
668  elem.appendChild( atlasElem );
669 }
670 
671 void QgsAtlasComposition::readXML( const QDomElement& atlasElem, const QDomDocument& )
672 {
673  mEnabled = atlasElem.attribute( "enabled", "false" ) == "true" ? true : false;
674  emit toggled( mEnabled );
675  if ( !mEnabled )
676  {
677  emit parameterChanged();
678  return;
679  }
680 
681  // look for stored layer name
682  mCoverageLayer = nullptr;
684  for ( QMap<QString, QgsMapLayer*>::const_iterator it = layers.begin(); it != layers.end(); ++it )
685  {
686  if ( it.key() == atlasElem.attribute( "coverageLayer" ) )
687  {
688  mCoverageLayer = dynamic_cast<QgsVectorLayer*>( it.value() );
689  break;
690  }
691  }
692 
693  mPageNameExpression = atlasElem.attribute( "pageNameExpression", QString() );
694  mSingleFile = atlasElem.attribute( "singleFile", "false" ) == "true" ? true : false;
695  mFilenamePattern = atlasElem.attribute( "filenamePattern", "" );
696 
697  mSortFeatures = atlasElem.attribute( "sortFeatures", "false" ) == "true" ? true : false;
698  if ( mSortFeatures )
699  {
700  mSortKeyAttributeName = atlasElem.attribute( "sortKey", "" );
701  // since 2.3, the field name is saved instead of the field index
702  // following code keeps compatibility with version 2.2 projects
703  // to be removed in QGIS 3.0
704  bool isIndex;
705  int idx = mSortKeyAttributeName.toInt( &isIndex );
706  if ( isIndex && mCoverageLayer )
707  {
708  QgsFields fields = mCoverageLayer->fields();
709  if ( idx >= 0 && idx < fields.count() )
710  {
711  mSortKeyAttributeName = fields.at( idx ).name();
712  }
713  }
714  mSortAscending = atlasElem.attribute( "sortAscending", "true" ) == "true" ? true : false;
715  }
716  mFilterFeatures = atlasElem.attribute( "filterFeatures", "false" ) == "true" ? true : false;
717  if ( mFilterFeatures )
718  {
719  mFeatureFilter = atlasElem.attribute( "featureFilter", "" );
720  }
721 
722  mHideCoverage = atlasElem.attribute( "hideCoverage", "false" ) == "true" ? true : false;
723 
724  emit parameterChanged();
725 }
726 
728 {
729  Q_UNUSED( doc );
730  //look for stored composer map, to upgrade pre 2.1 projects
731  int composerMapNo = elem.attribute( "composerMap", "-1" ).toInt();
732  QgsComposerMap * composerMap = nullptr;
733  if ( composerMapNo != -1 )
734  {
736  mComposition->composerItems( maps );
737  for ( QList<QgsComposerMap*>::iterator it = maps.begin(); it != maps.end(); ++it )
738  {
739  if (( *it )->id() == composerMapNo )
740  {
741  composerMap = ( *it );
742  composerMap->setAtlasDriven( true );
743  break;
744  }
745  }
746  }
747 
748  //upgrade pre 2.1 projects
749  double margin = elem.attribute( "margin", "0.0" ).toDouble();
750  if ( composerMap && !qgsDoubleNear( margin, 0.0 ) )
751  {
752  composerMap->setAtlasMargin( margin );
753  }
754  bool fixedScale = elem.attribute( "fixedScale", "false" ) == "true" ? true : false;
755  if ( composerMap && fixedScale )
756  {
758  }
759 }
760 
762 {
763  mHideCoverage = hide;
764 
765  if ( mComposition->atlasMode() == QgsComposition::PreviewAtlas )
766  {
767  //an atlas preview is enabled, so reflect changes in coverage layer visibility immediately
768  updateAtlasMaps();
769  mComposition->update();
770  }
771 
772 }
773 
775 {
776  mFilenamePattern = pattern;
777  return updateFilenameExpression();
778 }
779 
780 QgsExpressionContext QgsAtlasComposition::createExpressionContext()
781 {
782  QgsExpressionContext expressionContext;
783  expressionContext << QgsExpressionContextUtils::globalScope()
785  if ( mComposition )
786  expressionContext << QgsExpressionContextUtils::compositionScope( mComposition );
787 
788  expressionContext.appendScope( QgsExpressionContextUtils::atlasScope( this ) );
789  if ( mCoverageLayer )
790  expressionContext.lastScope()->setFields( mCoverageLayer->fields() );
791  if ( mComposition && mComposition->atlasMode() != QgsComposition::AtlasOff )
792  expressionContext.lastScope()->setFeature( mCurrentFeature );
793 
794  return expressionContext;
795 }
796 
797 bool QgsAtlasComposition::updateFilenameExpression()
798 {
799  if ( !mCoverageLayer )
800  {
801  return false;
802  }
803 
804  QgsExpressionContext expressionContext = createExpressionContext();
805 
806  if ( !mFilenamePattern.isEmpty() )
807  {
808  mFilenameExpr.reset( new QgsExpression( mFilenamePattern ) );
809  // expression used to evaluate each filename
810  // test for evaluation errors
811  if ( mFilenameExpr->hasParserError() )
812  {
813  mFilenameParserError = mFilenameExpr->parserErrorString();
814  return false;
815  }
816 
817  // prepare the filename expression
818  mFilenameExpr->prepare( &expressionContext );
819  }
820 
821  //if atlas preview is currently enabled, regenerate filename for current feature
822  if ( mComposition->atlasMode() == QgsComposition::PreviewAtlas )
823  {
824  evalFeatureFilename( expressionContext );
825  }
826  return true;
827 }
828 
829 bool QgsAtlasComposition::evalFeatureFilename( const QgsExpressionContext &context )
830 {
831  //generate filename for current atlas feature
832  if ( !mFilenamePattern.isEmpty() && !mFilenameExpr.isNull() )
833  {
834  QVariant filenameRes = mFilenameExpr->evaluate( &context );
835  if ( mFilenameExpr->hasEvalError() )
836  {
837  QgsMessageLog::logMessage( tr( "Atlas filename evaluation error: %1" ).arg( mFilenameExpr->evalErrorString() ), tr( "Composer" ) );
838  return false;
839  }
840 
841  mCurrentFilename = filenameRes.toString();
842  }
843  return true;
844 }
845 
847 {
848  mPredefinedScales = scales;
849  // make sure the list is sorted
850  qSort( mPredefinedScales.begin(), mPredefinedScales.end() );
851 }
852 
855 {
856  //deprecated method. Until removed just return the property for the first atlas-enabled composer map
857  QgsComposerMap * map = composerMap();
858  if ( !map )
859  {
860  return false;
861  }
862 
863  return map->atlasFixedScale();
864 }
865 
867 {
868  //deprecated method. Until removed just set the property for the first atlas-enabled composer map
869  QgsComposerMap * map = composerMap();
870  if ( !map )
871  {
872  return;
873  }
874 
876 }
877 
879 {
880  //deprecated method. Until removed just return the property for the first atlas-enabled composer map
881  QgsComposerMap * map = composerMap();
882  if ( !map )
883  {
884  return 0;
885  }
886 
887  return map->atlasMargin();
888 }
889 
891 {
892  //deprecated method. Until removed just set the property for the first atlas-enabled composer map
893  QgsComposerMap * map = composerMap();
894  if ( !map )
895  {
896  return;
897  }
898 
899  map->setAtlasMargin( static_cast< double >( margin ) );
900 }
901 
903 {
904  if ( !mCoverageLayer || !mCurrentFeature.isValid() || !mCurrentFeature.constGeometry() )
905  {
906  return QgsGeometry();
907  }
908 
909  if ( !crs.isValid() )
910  {
911  // no projection, return the native geometry
912  return *mCurrentFeature.constGeometry();
913  }
914 
915  QMap<long, QgsGeometry>::const_iterator it = mGeometryCache.constFind( crs.srsid() );
916  if ( it != mGeometryCache.constEnd() )
917  {
918  // we have it in cache, return it
919  return it.value();
920  }
921 
922  if ( mCoverageLayer->crs() == crs )
923  {
924  return *mCurrentFeature.constGeometry();
925  }
926 
927  QgsGeometry transformed = *mCurrentFeature.constGeometry();
928  transformed.transform( *QgsCoordinateTransformCache::instance()->transform( mCoverageLayer->crs().authid(), crs.authid() ) );
929  mGeometryCache[crs.srsid()] = transformed;
930  return transformed;
931 }
932 
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:515
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:193
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:337
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: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
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
QMap< QString, QgsMapLayer * > mapLayers() const
Returns a map of all registered layers by layer ID.
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:516
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
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.
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.