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