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