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