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