QGIS API Documentation  2.12.0-Lyon
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 
33  : mComposition( composition )
34  , mEnabled( false )
35  , mHideCoverage( false )
36  , mFilenamePattern( "'output_'||@atlas_featurenumber" )
37  , mCoverageLayer( 0 )
38  , mSingleFile( false )
39  , mSortFeatures( false )
40  , mSortAscending( true )
41  , mCurrentFeatureNo( 0 )
42  , mFilterFeatures( false )
43 {
44 
45  //listen out for layer removal
46  connect( QgsMapLayerRegistry::instance(), SIGNAL( layersWillBeRemoved( QStringList ) ), this, SLOT( removeLayers( QStringList ) ) );
47 }
48 
50 {
51 }
52 
53 void QgsAtlasComposition::setEnabled( bool enabled )
54 {
55  if ( enabled == mEnabled )
56  {
57  return;
58  }
59 
60  mEnabled = enabled;
61  mComposition->setAtlasMode( QgsComposition::AtlasOff );
62  emit toggled( enabled );
63  emit parameterChanged();
64 }
65 
66 void QgsAtlasComposition::removeLayers( const QStringList& layers )
67 {
68  if ( !mCoverageLayer )
69  {
70  return;
71  }
72 
73  Q_FOREACH ( const QString& layerId, layers )
74  {
75  if ( layerId == mCoverageLayer->id() )
76  {
77  //current coverage layer removed
78  mCoverageLayer = 0;
79  setEnabled( false );
80  return;
81  }
82  }
83 }
84 
86 {
87  if ( layer == mCoverageLayer )
88  {
89  return;
90  }
91 
92  mCoverageLayer = layer;
93  emit coverageLayerChanged( layer );
94 }
95 
97 {
98  if ( pageNumber < 0 || pageNumber >= mFeatureIds.count() )
99  return QString();
100 
101  return mFeatureIds.at( pageNumber ).second;
102 }
103 
105 {
106  //deprecated method. Until removed just return the first atlas-enabled composer map
107 
108  //build a list of composer maps
110  mComposition->composerItems( maps );
111  for ( QList<QgsComposerMap*>::iterator mit = maps.begin(); mit != maps.end(); ++mit )
112  {
113  QgsComposerMap* currentMap = ( *mit );
114  if ( currentMap->atlasDriven() )
115  {
116  return currentMap;
117  }
118  }
119 
120  return 0;
121 }
122 
124 {
125  //deprecated
126 
127  if ( !map )
128  {
129  return;
130  }
131 
132  map->setAtlasDriven( true );
133 }
134 
135 
137 {
138  if ( !mCoverageLayer )
139  {
140  return -1;
141  }
142  return mCoverageLayer->fieldNameIndex( mSortKeyAttributeName );
143 }
144 
146 {
147  if ( mCoverageLayer )
148  {
149  const QgsFields fields = mCoverageLayer->fields();
150  if ( idx >= 0 && idx < fields.count() )
151  {
152  mSortKeyAttributeName = fields[idx].name();
153  return;
154  }
155  }
156  mSortKeyAttributeName = "";
157 }
158 
159 //
160 // Private class only used for the sorting of features
162 {
163  public:
164  FieldSorter( QgsAtlasComposition::SorterKeys& keys, bool ascending = true ) : mKeys( keys ), mAscending( ascending ) {}
165 
167  {
168  bool result = true;
169 
170  if ( mKeys[ id1.first ].type() == QVariant::Int )
171  {
172  result = mKeys[ id1.first ].toInt() < mKeys[ id2.first ].toInt();
173  }
174  else if ( mKeys[ id1.first ].type() == QVariant::Double )
175  {
176  result = mKeys[ id1.first ].toDouble() < mKeys[ id2.first ].toDouble();
177  }
178  else if ( mKeys[ id1.first ].type() == QVariant::String )
179  {
180  result = ( QString::localeAwareCompare( mKeys[ id1.first ].toString(), mKeys[ id2.first ].toString() ) < 0 );
181  }
182 
183  return mAscending ? result : !result;
184  }
185  private:
187  bool mAscending;
188 };
189 
191 {
192  //needs to be called when layer, filter, sort changes
193 
194  if ( !mCoverageLayer )
195  {
196  return 0;
197  }
198 
199  QgsExpressionContext expressionContext = createExpressionContext();
200 
201  updateFilenameExpression();
202 
203  // select all features with all attributes
204  QgsFeatureRequest req;
205 
206  QScopedPointer<QgsExpression> filterExpression;
207  if ( mFilterFeatures && !mFeatureFilter.isEmpty() )
208  {
209  filterExpression.reset( new QgsExpression( mFeatureFilter ) );
210  if ( filterExpression->hasParserError() )
211  {
212  mFilterParserError = filterExpression->parserErrorString();
213  return 0;
214  }
215 
216  //filter good to go
217  req.setFilterExpression( mFeatureFilter );
218  }
219  mFilterParserError = QString();
220 
221  QgsFeatureIterator fit = mCoverageLayer->getFeatures( req );
222 
223  QScopedPointer<QgsExpression> nameExpression;
224  if ( !mPageNameExpression.isEmpty() )
225  {
226  nameExpression.reset( new QgsExpression( mPageNameExpression ) );
227  if ( nameExpression->hasParserError() )
228  {
229  nameExpression.reset( 0 );
230  }
231  nameExpression->prepare( &expressionContext );
232  }
233 
234  // We cannot use nextFeature() directly since the feature pointer is rewinded by the rendering process
235  // We thus store the feature ids for future extraction
236  QgsFeature feat;
237  mFeatureIds.clear();
238  mFeatureKeys.clear();
239  int sortIdx = mCoverageLayer->fieldNameIndex( mSortKeyAttributeName );
240 
241  while ( fit.nextFeature( feat ) )
242  {
243  expressionContext.setFeature( feat );
244 
245  QString pageName;
246  if ( !nameExpression.isNull() )
247  {
248  QVariant result = nameExpression->evaluate( &expressionContext );
249  if ( nameExpression->hasEvalError() )
250  {
251  QgsMessageLog::logMessage( tr( "Atlas name eval error: %1" ).arg( nameExpression->evalErrorString() ), tr( "Composer" ) );
252  }
253  pageName = result.toString();
254  }
255 
256  mFeatureIds.push_back( qMakePair( feat.id(), pageName ) );
257 
258  if ( mSortFeatures && sortIdx != -1 )
259  {
260  mFeatureKeys.insert( feat.id(), feat.attributes().at( sortIdx ) );
261  }
262  }
263 
264  // sort features, if asked for
265  if ( mFeatureKeys.count() )
266  {
267  FieldSorter sorter( mFeatureKeys, mSortAscending );
268  qSort( mFeatureIds.begin(), mFeatureIds.end(), sorter );
269  }
270 
271  emit numberFeaturesChanged( mFeatureIds.size() );
272 
273  //jump to first feature if currently using an atlas preview
274  //need to do this in case filtering/layer change has altered matching features
275  if ( mComposition->atlasMode() == QgsComposition::PreviewAtlas )
276  {
277  firstFeature();
278  }
279 
280  return mFeatureIds.size();
281 }
282 
284 {
285  return nameForPage( currentFeatureNumber() );
286 }
287 
289 {
290  if ( !mCoverageLayer )
291  {
292  return false;
293  }
294 
295  emit renderBegun();
296 
297  bool featuresUpdated = updateFeatures();
298  if ( !featuresUpdated )
299  {
300  //no matching features found
301  return false;
302  }
303 
304  return true;
305 }
306 
308 {
309  if ( !mCoverageLayer )
310  {
311  return;
312  }
313 
314  emit featureChanged( 0 );
315 
316  updateAtlasMaps();
317 
318  emit renderEnded();
319 }
320 
321 void QgsAtlasComposition::updateAtlasMaps()
322 {
323  //update atlas-enabled composer maps
325  mComposition->composerItems( maps );
326  for ( QList<QgsComposerMap*>::iterator mit = maps.begin(); mit != maps.end(); ++mit )
327  {
328  QgsComposerMap* currentMap = ( *mit );
329  if ( !currentMap->atlasDriven() )
330  {
331  continue;
332  }
333 
334  currentMap->cache();
335  }
336 }
337 
339 {
340  return mFeatureIds.size();
341 }
342 
344 {
345  int newFeatureNo = mCurrentFeatureNo + 1;
346  if ( newFeatureNo >= mFeatureIds.size() )
347  {
348  newFeatureNo = mFeatureIds.size() - 1;
349  }
350 
351  prepareForFeature( newFeatureNo );
352 }
353 
355 {
356  int newFeatureNo = mCurrentFeatureNo - 1;
357  if ( newFeatureNo < 0 )
358  {
359  newFeatureNo = 0;
360  }
361 
362  prepareForFeature( newFeatureNo );
363 }
364 
366 {
367  prepareForFeature( 0 );
368 }
369 
371 {
372  prepareForFeature( mFeatureIds.size() - 1 );
373 }
374 
376 {
377  int featureI = -1;
378  QVector< QPair<QgsFeatureId, QString> >::const_iterator it = mFeatureIds.constBegin();
379  int currentIdx = 0;
380  for ( ; it != mFeatureIds.constEnd(); ++it, ++currentIdx )
381  {
382  if (( *it ).first == feat->id() )
383  {
384  featureI = currentIdx;
385  break;
386  }
387  }
388 
389  if ( featureI < 0 )
390  {
391  //feature not found
392  return false;
393  }
394 
395  return prepareForFeature( featureI );
396 }
397 
399 {
400  prepareForFeature( mCurrentFeatureNo, false );
401 }
402 
403 bool QgsAtlasComposition::prepareForFeature( const int featureI, const bool updateMaps )
404 {
405  if ( !mCoverageLayer )
406  {
407  return false;
408  }
409 
410  if ( mFeatureIds.size() == 0 )
411  {
412  emit statusMsgChanged( tr( "No matching atlas features" ) );
413  return false;
414  }
415 
416  if ( featureI >= mFeatureIds.size() )
417  {
418  return false;
419  }
420 
421  mCurrentFeatureNo = featureI;
422 
423  // retrieve the next feature, based on its id
424  mCoverageLayer->getFeatures( QgsFeatureRequest().setFilterFid( mFeatureIds[ featureI ].first ) ).nextFeature( mCurrentFeature );
425 
426  QgsExpressionContext expressionContext = createExpressionContext();
427 
428  // generate filename for current feature
429  if ( !evalFeatureFilename( expressionContext ) )
430  {
431  //error evaluating filename
432  return false;
433  }
434 
435  emit featureChanged( &mCurrentFeature );
436  emit statusMsgChanged( QString( tr( "Atlas feature %1 of %2" ) ).arg( featureI + 1 ).arg( mFeatureIds.size() ) );
437 
438  if ( !mCurrentFeature.isValid() )
439  {
440  //bad feature
441  return true;
442  }
443 
444  if ( !updateMaps )
445  {
446  //nothing more to do
447  return true;
448  }
449 
450  //update composer maps
451 
452  //build a list of atlas-enabled composer maps
454  QList<QgsComposerMap*> atlasMaps;
455  mComposition->composerItems( maps );
456  if ( maps.isEmpty() )
457  {
458  return true;
459  }
460  for ( QList<QgsComposerMap*>::iterator mit = maps.begin(); mit != maps.end(); ++mit )
461  {
462  QgsComposerMap* currentMap = ( *mit );
463  if ( !currentMap->atlasDriven() )
464  {
465  continue;
466  }
467  atlasMaps << currentMap;
468  }
469 
470  if ( atlasMaps.count() > 0 )
471  {
472  //clear the transformed bounds of the previous feature
473  mTransformedFeatureBounds = QgsRectangle();
474 
475  // compute extent of current feature in the map CRS. This should be set on a per-atlas map basis,
476  // but given that it's not currently possible to have maps with different CRSes we can just
477  // calculate it once based on the first atlas maps' CRS.
478  computeExtent( atlasMaps[0] );
479  }
480 
481  for ( QList<QgsComposerMap*>::iterator mit = maps.begin(); mit != maps.end(); ++mit )
482  {
483  if (( *mit )->atlasDriven() )
484  {
485  // map is atlas driven, so update it's bounds (causes a redraw)
486  prepareMap( *mit );
487  }
488  else
489  {
490  // map is not atlas driven, so manually force a redraw (to reflect possibly atlas
491  // dependent symbology)
492  ( *mit )->cache();
493  }
494  }
495 
496  return true;
497 }
498 
499 void QgsAtlasComposition::computeExtent( QgsComposerMap* map )
500 {
501  // compute the extent of the current feature, in the crs of the specified map
502  if ( !mCurrentFeature.constGeometry() )
503  return;
504 
505  const QgsCoordinateReferenceSystem& coverage_crs = mCoverageLayer->crs();
506  // transformation needed for feature geometries
507  const QgsCoordinateReferenceSystem& destination_crs = map->composition()->mapSettings().destinationCrs();
508  mTransform.setSourceCrs( coverage_crs );
509  mTransform.setDestCRS( destination_crs );
510 
511  // QgsGeometry::boundingBox is expressed in the geometry"s native CRS
512  // We have to transform the grometry to the destination CRS and ask for the bounding box
513  // Note: we cannot directly take the transformation of the bounding box, since transformations are not linear
514  QgsGeometry tgeom( *mCurrentFeature.constGeometry() );
515  tgeom.transform( mTransform );
516  mTransformedFeatureBounds = tgeom.boundingBox();
517 }
518 
520 {
521  if ( !map->atlasDriven() || mCoverageLayer->wkbType() == QGis::WKBNoGeometry )
522  {
523  return;
524  }
525 
526  if ( mTransformedFeatureBounds.isEmpty() )
527  {
528  //transformed extent of current feature hasn't been calculated yet. This can happen if
529  //a map has been set to be atlas controlled after prepare feature was called
530  computeExtent( map );
531  }
532 
533  double xa1 = mTransformedFeatureBounds.xMinimum();
534  double xa2 = mTransformedFeatureBounds.xMaximum();
535  double ya1 = mTransformedFeatureBounds.yMinimum();
536  double ya2 = mTransformedFeatureBounds.yMaximum();
537  QgsRectangle newExtent = mTransformedFeatureBounds;
538  QgsRectangle mOrigExtent( map->extent() );
539 
540  //sanity check - only allow fixed scale mode for point layers
541  bool isPointLayer = false;
542  switch ( mCoverageLayer->wkbType() )
543  {
544  case QGis::WKBPoint:
545  case QGis::WKBPoint25D:
546  case QGis::WKBMultiPoint:
548  isPointLayer = true;
549  break;
550  default:
551  isPointLayer = false;
552  break;
553  }
554 
555  if ( map->atlasScalingMode() == QgsComposerMap::Fixed || map->atlasScalingMode() == QgsComposerMap::Predefined || isPointLayer )
556  {
557  QgsScaleCalculator calc;
558  calc.setMapUnits( composition()->mapSettings().mapUnits() );
559  calc.setDpi( 25.4 );
560  double originalScale = calc.calculate( mOrigExtent, map->rect().width() );
561  double geomCenterX = ( xa1 + xa2 ) / 2.0;
562  double geomCenterY = ( ya1 + ya2 ) / 2.0;
563 
564  if ( map->atlasScalingMode() == QgsComposerMap::Fixed || isPointLayer )
565  {
566  // only translate, keep the original scale (i.e. width x height)
567  double xMin = geomCenterX - mOrigExtent.width() / 2.0;
568  double yMin = geomCenterY - mOrigExtent.height() / 2.0;
569  newExtent = QgsRectangle( xMin,
570  yMin,
571  xMin + mOrigExtent.width(),
572  yMin + mOrigExtent.height() );
573 
574  //scale newExtent to match original scale of map
575  //this is required for geographic coordinate systems, where the scale varies by extent
576  double newScale = calc.calculate( newExtent, map->rect().width() );
577  newExtent.scale( originalScale / newScale );
578  }
579  else if ( map->atlasScalingMode() == QgsComposerMap::Predefined )
580  {
581  // choose one of the predefined scales
582  double newWidth = mOrigExtent.width();
583  double newHeight = mOrigExtent.height();
584  const QVector<qreal>& scales = mPredefinedScales;
585  for ( int i = 0; i < scales.size(); i++ )
586  {
587  double ratio = scales[i] / originalScale;
588  newWidth = mOrigExtent.width() * ratio;
589  newHeight = mOrigExtent.height() * ratio;
590 
591  // compute new extent, centered on feature
592  double xMin = geomCenterX - newWidth / 2.0;
593  double yMin = geomCenterY - newHeight / 2.0;
594  newExtent = QgsRectangle( xMin,
595  yMin,
596  xMin + newWidth,
597  yMin + newHeight );
598 
599  //scale newExtent to match desired map scale
600  //this is required for geographic coordinate systems, where the scale varies by extent
601  double newScale = calc.calculate( newExtent, map->rect().width() );
602  newExtent.scale( scales[i] / newScale );
603 
604  if (( newExtent.width() >= mTransformedFeatureBounds.width() ) && ( newExtent.height() >= mTransformedFeatureBounds.height() ) )
605  {
606  // this is the smallest extent that embeds the feature, stop here
607  break;
608  }
609  }
610  }
611  }
612  else if ( map->atlasScalingMode() == QgsComposerMap::Auto )
613  {
614  // auto scale
615 
616  double geomRatio = mTransformedFeatureBounds.width() / mTransformedFeatureBounds.height();
617  double mapRatio = mOrigExtent.width() / mOrigExtent.height();
618 
619  // geometry height is too big
620  if ( geomRatio < mapRatio )
621  {
622  // extent the bbox's width
623  double adjWidth = ( mapRatio * mTransformedFeatureBounds.height() - mTransformedFeatureBounds.width() ) / 2.0;
624  xa1 -= adjWidth;
625  xa2 += adjWidth;
626  }
627  // geometry width is too big
628  else if ( geomRatio > mapRatio )
629  {
630  // extent the bbox's height
631  double adjHeight = ( mTransformedFeatureBounds.width() / mapRatio - mTransformedFeatureBounds.height() ) / 2.0;
632  ya1 -= adjHeight;
633  ya2 += adjHeight;
634  }
635  newExtent = QgsRectangle( xa1, ya1, xa2, ya2 );
636 
637  if ( map->atlasMargin() > 0.0 )
638  {
639  newExtent.scale( 1 + map->atlasMargin() );
640  }
641  }
642 
643  // set the new extent (and render)
644  map->setNewAtlasFeatureExtent( newExtent );
645 }
646 
648 {
649  return mCurrentFilename;
650 }
651 
653 {
654  QDomElement atlasElem = doc.createElement( "Atlas" );
655  atlasElem.setAttribute( "enabled", mEnabled ? "true" : "false" );
656  if ( !mEnabled )
657  {
658  return;
659  }
660 
661  if ( mCoverageLayer )
662  {
663  atlasElem.setAttribute( "coverageLayer", mCoverageLayer->id() );
664  }
665  else
666  {
667  atlasElem.setAttribute( "coverageLayer", "" );
668  }
669 
670  atlasElem.setAttribute( "hideCoverage", mHideCoverage ? "true" : "false" );
671  atlasElem.setAttribute( "singleFile", mSingleFile ? "true" : "false" );
672  atlasElem.setAttribute( "filenamePattern", mFilenamePattern );
673  atlasElem.setAttribute( "pageNameExpression", mPageNameExpression );
674 
675  atlasElem.setAttribute( "sortFeatures", mSortFeatures ? "true" : "false" );
676  if ( mSortFeatures )
677  {
678  atlasElem.setAttribute( "sortKey", mSortKeyAttributeName );
679  atlasElem.setAttribute( "sortAscending", mSortAscending ? "true" : "false" );
680  }
681  atlasElem.setAttribute( "filterFeatures", mFilterFeatures ? "true" : "false" );
682  if ( mFilterFeatures )
683  {
684  atlasElem.setAttribute( "featureFilter", mFeatureFilter );
685  }
686 
687  elem.appendChild( atlasElem );
688 }
689 
690 void QgsAtlasComposition::readXML( const QDomElement& atlasElem, const QDomDocument& )
691 {
692  mEnabled = atlasElem.attribute( "enabled", "false" ) == "true" ? true : false;
693  emit toggled( mEnabled );
694  if ( !mEnabled )
695  {
696  emit parameterChanged();
697  return;
698  }
699 
700  // look for stored layer name
701  mCoverageLayer = 0;
703  for ( QMap<QString, QgsMapLayer*>::const_iterator it = layers.begin(); it != layers.end(); ++it )
704  {
705  if ( it.key() == atlasElem.attribute( "coverageLayer" ) )
706  {
707  mCoverageLayer = dynamic_cast<QgsVectorLayer*>( it.value() );
708  break;
709  }
710  }
711 
712  mPageNameExpression = atlasElem.attribute( "pageNameExpression", QString() );
713  mSingleFile = atlasElem.attribute( "singleFile", "false" ) == "true" ? true : false;
714  mFilenamePattern = atlasElem.attribute( "filenamePattern", "" );
715 
716  mSortFeatures = atlasElem.attribute( "sortFeatures", "false" ) == "true" ? true : false;
717  if ( mSortFeatures )
718  {
719  mSortKeyAttributeName = atlasElem.attribute( "sortKey", "" );
720  // since 2.3, the field name is saved instead of the field index
721  // following code keeps compatibility with version 2.2 projects
722  // to be removed in QGIS 3.0
723  bool isIndex;
724  int idx = mSortKeyAttributeName.toInt( &isIndex );
725  if ( isIndex && mCoverageLayer )
726  {
727  const QgsFields fields = mCoverageLayer->fields();
728  if ( idx >= 0 && idx < fields.count() )
729  {
730  mSortKeyAttributeName = fields[idx].name();
731  }
732  }
733  mSortAscending = atlasElem.attribute( "sortAscending", "true" ) == "true" ? true : false;
734  }
735  mFilterFeatures = atlasElem.attribute( "filterFeatures", "false" ) == "true" ? true : false;
736  if ( mFilterFeatures )
737  {
738  mFeatureFilter = atlasElem.attribute( "featureFilter", "" );
739  }
740 
741  mHideCoverage = atlasElem.attribute( "hideCoverage", "false" ) == "true" ? true : false;
742 
743  emit parameterChanged();
744 }
745 
747 {
748  Q_UNUSED( doc );
749  //look for stored composer map, to upgrade pre 2.1 projects
750  int composerMapNo = elem.attribute( "composerMap", "-1" ).toInt();
752  if ( composerMapNo != -1 )
753  {
755  mComposition->composerItems( maps );
756  for ( QList<QgsComposerMap*>::iterator it = maps.begin(); it != maps.end(); ++it )
757  {
758  if (( *it )->id() == composerMapNo )
759  {
760  composerMap = ( *it );
761  composerMap->setAtlasDriven( true );
762  break;
763  }
764  }
765  }
766 
767  //upgrade pre 2.1 projects
768  double margin = elem.attribute( "margin", "0.0" ).toDouble();
769  if ( composerMap && margin != 0 )
770  {
771  composerMap->setAtlasMargin( margin );
772  }
773  bool fixedScale = elem.attribute( "fixedScale", "false" ) == "true" ? true : false;
774  if ( composerMap && fixedScale )
775  {
777  }
778 }
779 
781 {
782  mHideCoverage = hide;
783 
784  if ( mComposition->atlasMode() == QgsComposition::PreviewAtlas )
785  {
786  //an atlas preview is enabled, so reflect changes in coverage layer visibility immediately
787  updateAtlasMaps();
788  mComposition->update();
789  }
790 
791 }
792 
794 {
795  mFilenamePattern = pattern;
796  return updateFilenameExpression();
797 }
798 
799 QgsExpressionContext QgsAtlasComposition::createExpressionContext()
800 {
801  QgsExpressionContext expressionContext;
802  expressionContext << QgsExpressionContextUtils::globalScope()
804  if ( mComposition )
805  expressionContext << QgsExpressionContextUtils::compositionScope( mComposition );
806 
807  expressionContext.appendScope( QgsExpressionContextUtils::atlasScope( this ) );
808  if ( mCoverageLayer )
809  expressionContext.lastScope()->setFields( mCoverageLayer->fields() );
810  if ( mComposition && mComposition->atlasMode() != QgsComposition::AtlasOff )
811  expressionContext.lastScope()->setFeature( mCurrentFeature );
812 
813  return expressionContext;
814 }
815 
816 bool QgsAtlasComposition::updateFilenameExpression()
817 {
818  if ( !mCoverageLayer )
819  {
820  return false;
821  }
822 
823  QgsExpressionContext expressionContext = createExpressionContext();
824 
825  if ( mFilenamePattern.size() > 0 )
826  {
827  mFilenameExpr.reset( new QgsExpression( mFilenamePattern ) );
828  // expression used to evaluate each filename
829  // test for evaluation errors
830  if ( mFilenameExpr->hasParserError() )
831  {
832  mFilenameParserError = mFilenameExpr->parserErrorString();
833  return false;
834  }
835 
836  // prepare the filename expression
837  mFilenameExpr->prepare( &expressionContext );
838  }
839 
840  //if atlas preview is currently enabled, regenerate filename for current feature
841  if ( mComposition->atlasMode() == QgsComposition::PreviewAtlas )
842  {
843  evalFeatureFilename( expressionContext );
844  }
845  return true;
846 }
847 
848 bool QgsAtlasComposition::evalFeatureFilename( const QgsExpressionContext &context )
849 {
850  //generate filename for current atlas feature
851  if ( mFilenamePattern.size() > 0 && !mFilenameExpr.isNull() )
852  {
853  QVariant filenameRes = mFilenameExpr->evaluate( &context );
854  if ( mFilenameExpr->hasEvalError() )
855  {
856  QgsMessageLog::logMessage( tr( "Atlas filename evaluation error: %1" ).arg( mFilenameExpr->evalErrorString() ), tr( "Composer" ) );
857  return false;
858  }
859 
860  mCurrentFilename = filenameRes.toString();
861  }
862  return true;
863 }
864 
866 {
867  mPredefinedScales = scales;
868  // make sure the list is sorted
869  qSort( mPredefinedScales.begin(), mPredefinedScales.end() );
870 }
871 
874 {
875  //deprecated method. Until removed just return the property for the first atlas-enabled composer map
876  QgsComposerMap * map = composerMap();
877  if ( !map )
878  {
879  return false;
880  }
881 
882  return map->atlasFixedScale();
883 }
884 
886 {
887  //deprecated method. Until removed just set the property for the first atlas-enabled composer map
888  QgsComposerMap * map = composerMap();
889  if ( !map )
890  {
891  return;
892  }
893 
895 }
896 
898 {
899  //deprecated method. Until removed just return the property for the first atlas-enabled composer map
900  QgsComposerMap * map = composerMap();
901  if ( !map )
902  {
903  return 0;
904  }
905 
906  return map->atlasMargin();
907 }
908 
909 void QgsAtlasComposition::setMargin( float margin )
910 {
911  //deprecated method. Until removed just set the property for the first atlas-enabled composer map
912  QgsComposerMap * map = composerMap();
913  if ( !map )
914  {
915  return;
916  }
917 
918  map->setAtlasMargin(( double ) margin );
919 }
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:53
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:92
void setMapUnits(QGis::UnitType mapUnits)
Set the map units.
Wrapper for iterator of features from vector data provider or vector layer.
QgsComposition::AtlasMode atlasMode() const
Returns the current atlas mode of the composition.
A rectangle specified with double values.
Definition: qgsrectangle.h:35
bool isEmpty() const
test if rectangle is empty.
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 localeAwareCompare(const QString &other) const
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:170
QDomNode appendChild(const QDomNode &newChild)
iterator begin()
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
double yMaximum() const
Get the y maximum value (top side of rectangle)
Definition: qgsrectangle.h:196
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.
Q_DECL_DEPRECATED bool fixedScale() const
Returns whether the atlas map uses a fixed scale.
void setSourceCrs(const QgsCoordinateReferenceSystem &theCRS)
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.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest())
Query the provider for features specified in request.
void cache()
Create cache image.
const_iterator constEnd() const
int size() const
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:390
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:177
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:76
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:176
QString currentPageName() const
Returns the name of the page for the current atlas feature.
void clear()
double toDouble(bool *ok) const
QString tr(const char *sourceText, const char *disambiguation, int n)
void endRender()
Ends the rendering.
void readXML(const QDomElement &elem, const QDomDocument &doc)
Reads general atlas settings from xml.
QGis::WkbType wkbType() const
Returns the WKBType or WKBUnknown in case of error.
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 clear()
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.
int count(const T &value) const
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.
double yMinimum() const
Get the y minimum value (bottom side of rectangle)
Definition: qgsrectangle.h:201
double xMaximum() const
Get the x maximum value (right side of rectangle)
Definition: qgsrectangle.h:186
void prepareMap(QgsComposerMap *map)
Recalculates the bounds of an atlas driven map.
void statusMsgChanged(QString message)
Is emitted when the atlas has an updated status bar message for the composer window.
QgsAttributes attributes() const
Returns the feature's attributes.
Definition: qgsfeature.cpp:92
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
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...
bool isEmpty() 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'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:311
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.
void writeXML(QDomElement &elem, QDomDocument &doc) const
iterator begin()
Graphics scene for map printing.
Object representing map window.
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:391
void setDestCRS(const QgsCoordinateReferenceSystem &theCRS)
bool isNull() const
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)
void update(qreal x, qreal y, qreal w, qreal h)
int count(const T &value) const
const QgsGeometry * constGeometry() const
Gets a const pointer to the geometry object associated with this feature.
Definition: qgsfeature.cpp:70
const QMap< QString, QgsMapLayer * > & mapLayers()
Retrieve the mapLayers collection (mainly intended for use by projection)
void parameterChanged()
Emitted when one of the parameters changes.
int transform(const QgsCoordinateTransform &ct)
Transform this geometry as described by CoordinateTransform ct.
void push_back(const T &value)
const QgsCoordinateReferenceSystem & crs() const
Returns layer's spatial reference system.
const QString & currentFilename() const
Returns the current filename.
iterator insert(const Key &key, const T &value)
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:206
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.
double xMinimum() const
Get the x minimum value (left side of rectangle)
Definition: qgsrectangle.h:191
QString toString() const
iterator end()
int count(const Key &key) const
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:211
QRectF rect() const
void scale(double scaleFactor, const QgsPoint *c=0)
Scale the rectangle around its center point.
static QgsExpressionContextScope * compositionScope(const QgsComposition *composition)
Creates a new scope which contains variables and functions relating to a QgsComposition.