Skip to content

Commit faa9c33

Browse files
author
Hugo Mercier
committed
Robustify atlas generation
1 parent 5bd9c3d commit faa9c33

File tree

6 files changed

+157
-96
lines changed

6 files changed

+157
-96
lines changed

src/app/composer/qgscomposer.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,7 @@ void QgsComposer::on_mActionExportAsPDF_triggered()
608608
{
609609
return;
610610
}
611-
atlasMap->setAtlasFilenamePattern( "'output_'||$id" );
611+
atlasMap->setAtlasFilenamePattern( "'output_'||$feature" );
612612
}
613613

614614
QSettings myQSettings;
@@ -858,7 +858,7 @@ void QgsComposer::on_mActionExportAsImage_triggered()
858858
{
859859
return;
860860
}
861-
atlasMap->setAtlasFilenamePattern( "'output_'||$id" );
861+
atlasMap->setAtlasFilenamePattern( "'output_'||$feature" );
862862
}
863863

864864
QSettings myQSettings;
@@ -1054,7 +1054,7 @@ void QgsComposer::on_mActionExportAsSVG_triggered()
10541054
{
10551055
return;
10561056
}
1057-
atlasMap->setAtlasFilenamePattern( "'output_'||$id||'_'||$page" );
1057+
atlasMap->setAtlasFilenamePattern( "'output_'||$feature" );
10581058
}
10591059

10601060
QSettings myQSettings;

src/core/composer/qgscomposerlabel.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,9 @@ QString QgsComposerLabel::displayText() const
8989
{
9090
QString displayText = mText;
9191
replaceDateText( displayText );
92-
return QgsExpression::replaceExpressionText( displayText, mExpressionFeature, mExpressionLayer, &mSubstitutions );
92+
QMap<QString, QVariant> subs = mSubstitutions;
93+
subs[ "$page" ] = QVariant((int)mComposition->itemPageNumber( this ) + 1);
94+
return QgsExpression::replaceExpressionText( displayText, mExpressionFeature, mExpressionLayer, &subs );
9395
}
9496

9597
void QgsComposerLabel::replaceDateText( QString& text ) const

src/core/composer/qgscomposermap.cpp

+9-2
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ QgsComposerMap::QgsComposerMap( QgsComposition *composition, int x, int y, int w
4545
mBottomGridAnnotationPosition( OutsideMapFrame ), mAnnotationFrameDistance( 1.0 ), mLeftGridAnnotationDirection( Horizontal ), mRightGridAnnotationDirection( Horizontal ),
4646
mTopGridAnnotationDirection( Horizontal ), mBottomGridAnnotationDirection( Horizontal ), mGridFrameStyle( NoGridFrame ), mGridFrameWidth( 2.0 ),
4747
mCrossLength( 3 ), mMapCanvas( 0 ), mDrawCanvasItems( true ),
48-
mAtlasMargin( 0.10 ), mAtlasFilenamePattern("'output_'||$id"), mAtlasCoverageLayer(0)
48+
mAtlasMargin( 0.10 ), mAtlasFilenamePattern("'output_'||$feature"), mAtlasCoverageLayer(0)
4949
{
5050
mComposition = composition;
5151
mOverviewFrameMapSymbol = 0;
@@ -87,7 +87,7 @@ QgsComposerMap::QgsComposerMap( QgsComposition *composition )
8787
mBottomGridAnnotationPosition( OutsideMapFrame ), mAnnotationFrameDistance( 1.0 ), mLeftGridAnnotationDirection( Horizontal ), mRightGridAnnotationDirection( Horizontal ),
8888
mTopGridAnnotationDirection( Horizontal ), mBottomGridAnnotationDirection( Horizontal ), mGridFrameStyle( NoGridFrame ), mGridFrameWidth( 2.0 ), mCrossLength( 3 ),
8989
mMapCanvas( 0 ), mDrawCanvasItems( true ),
90-
mAtlasMargin( 0.10 ), mAtlasFilenamePattern("'output_'||$id"), mAtlasCoverageLayer(0)
90+
mAtlasMargin( 0.10 ), mAtlasFilenamePattern("'output_'||$feature"), mAtlasCoverageLayer(0)
9191
{
9292
mOverviewFrameMapSymbol = 0;
9393
createDefaultOverviewFrameSymbol();
@@ -633,6 +633,13 @@ void QgsComposerMap::syncAtlasCoverageLayer( QString lname )
633633
}
634634
}
635635

636+
void QgsComposerMap::setAtlasCoverageLayer( QgsVectorLayer* map )
637+
{
638+
mAtlasCoverageLayer = map;
639+
640+
emit atlasCoverageLayerChanged( map );
641+
}
642+
636643
bool QgsComposerMap::writeXML( QDomElement& elem, QDomDocument & doc ) const
637644
{
638645
if ( elem.isNull() )

src/core/composer/qgscomposermap.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -330,14 +330,16 @@ class CORE_EXPORT QgsComposerMap : public QgsComposerItem
330330
void setAtlasFilenamePattern( const QString& pattern ) { mAtlasFilenamePattern = pattern; }
331331

332332
QgsVectorLayer* atlasCoverageLayer() const { return mAtlasCoverageLayer; }
333-
void setAtlasCoverageLayer( QgsVectorLayer* lmap ) { mAtlasCoverageLayer = lmap; }
333+
void setAtlasCoverageLayer( QgsVectorLayer* lmap );
334334

335335
bool atlasSingleFile() const { return mAtlasSingleFile; }
336336
void setAtlasSingleFile( bool single ) { mAtlasSingleFile = single; }
337337

338338
signals:
339339
void extentChanged();
340340

341+
void atlasCoverageLayerChanged( QgsVectorLayer* );
342+
341343
public slots:
342344

343345
/**Called if map canvas has changed*/

src/core/composer/qgscomposition.cpp

+128-88
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ QgsAtlasRendering::QgsAtlasRendering( QgsComposition* composition )
7474

7575
void QgsAtlasRendering::begin( const QString& filenamePattern )
7676
{
77+
if ( !impl->composition || !impl->composition->atlasMap() || !impl->composition->atlasMap()->atlasCoverageLayer() )
78+
return;
79+
7780
impl->filenamePattern = filenamePattern;
7881

7982
QgsVectorLayer* coverage = impl->composition->atlasMap()->atlasCoverageLayer();
@@ -103,12 +106,7 @@ void QgsAtlasRendering::begin( const QString& filenamePattern )
103106
}
104107

105108
// select all features with all attributes
106-
QgsAttributeList selectedAttributes;
107-
for ( QgsFieldMap::const_iterator fit = fieldmap.begin(); fit != fieldmap.end(); ++fit )
108-
{
109-
selectedAttributes.push_back( fit.key() );
110-
}
111-
provider->select( selectedAttributes );
109+
provider->select( provider->attributeIndexes() );
112110

113111
// features must be stored in a list, since modifying the layer's extent rewinds nextFeature()
114112
QgsFeature feature;
@@ -134,101 +132,104 @@ void QgsAtlasRendering::begin( const QString& filenamePattern )
134132

135133
// special columns for expressions
136134
QgsExpression::setSpecialColumn( "$numfeatures", QVariant( (int)impl->nFeatures ) );
137-
QgsExpression::setSpecialColumn( "$numpages", QVariant( (int)impl->composition->numPages() ) );
138135
}
139136

140137
void QgsAtlasRendering::prepareForFeature( size_t featureI )
141138
{
142-
QgsFeature* fit = &impl->features[featureI];
139+
if ( !impl->composition || !impl->composition->atlasMap() || !impl->composition->atlasMap()->atlasCoverageLayer() )
140+
return;
143141

144-
if ( impl->filenamePattern.size() > 0 )
142+
QgsFeature* fit = &impl->features[featureI];
143+
144+
if ( impl->filenamePattern.size() > 0 )
145+
{
146+
QgsExpression::setSpecialColumn( "$feature", QVariant( (int)featureI + 1 ) );
147+
QVariant filenameRes = impl->filenameExpr->evaluate( &*fit );
148+
if ( impl->filenameExpr->hasEvalError() )
145149
{
146-
QgsExpression::setSpecialColumn( "$feature", QVariant( (int)featureI + 1 ) );
147-
QVariant filenameRes = impl->filenameExpr->evaluate( &*fit );
148-
if ( impl->filenameExpr->hasEvalError() )
149-
{
150-
throw std::runtime_error( "Filename eval error: " + impl->filenameExpr->evalErrorString().toStdString() );
151-
}
152-
153-
impl->currentFilename = filenameRes.toString();
150+
throw std::runtime_error( "Filename eval error: " + impl->filenameExpr->evalErrorString().toStdString() );
154151
}
155-
156-
//
157-
// compute the new extent
158-
// keep the original aspect ratio
159-
// and apply a margin
160-
161-
// QgsGeometry::boundingBox is expressed in the geometry"s native CRS
162-
// They have to be transformed to the MapRenderer's one
163-
QgsRectangle geom_rect = impl->transform.transform( fit->geometry()->boundingBox() );
164-
double xa1 = geom_rect.xMinimum();
165-
double xa2 = geom_rect.xMaximum();
166-
double ya1 = geom_rect.yMinimum();
167-
double ya2 = geom_rect.yMaximum();
168-
QgsRectangle new_extent = geom_rect;
169-
170-
// restore the original extent
171-
// (successive calls to setNewExtent tend to deform the original rectangle)
172-
impl->composition->atlasMap()->setNewExtent( impl->origExtent );
173-
174-
if ( impl->composition->atlasMap()->atlasFixedScale() )
152+
153+
impl->currentFilename = filenameRes.toString();
154+
}
155+
156+
//
157+
// compute the new extent
158+
// keep the original aspect ratio
159+
// and apply a margin
160+
161+
// QgsGeometry::boundingBox is expressed in the geometry"s native CRS
162+
// We have to transform the grometry to the destination CRS and ask for the bounding box
163+
// Note: we cannot directly take the transformation of the bounding box, since transformations are not linear
164+
165+
QgsGeometry tgeom( *fit->geometry() );
166+
tgeom.transform( impl->transform );
167+
QgsRectangle geom_rect = tgeom.boundingBox();
168+
169+
double xa1 = geom_rect.xMinimum();
170+
double xa2 = geom_rect.xMaximum();
171+
double ya1 = geom_rect.yMinimum();
172+
double ya2 = geom_rect.yMaximum();
173+
QgsRectangle new_extent = geom_rect;
174+
175+
// restore the original extent
176+
// (successive calls to setNewExtent tend to deform the original rectangle)
177+
impl->composition->atlasMap()->setNewExtent( impl->origExtent );
178+
179+
if ( impl->composition->atlasMap()->atlasFixedScale() )
180+
{
181+
// only translate, keep the original scale (i.e. width x height)
182+
183+
double geom_center_x = (xa1 + xa2) / 2.0;
184+
double geom_center_y = (ya1 + ya2) / 2.0;
185+
double xx = geom_center_x - impl->origExtent.width() / 2.0;
186+
double yy = geom_center_y - impl->origExtent.height() / 2.0;
187+
new_extent = QgsRectangle( xx,
188+
yy,
189+
xx + impl->origExtent.width(),
190+
yy + impl->origExtent.height() );
191+
}
192+
else
193+
{
194+
// auto scale
195+
196+
double geom_ratio = geom_rect.width() / geom_rect.height();
197+
double map_ratio = impl->origExtent.width() / impl->origExtent.height();
198+
199+
// geometry height is too big
200+
if ( geom_ratio < map_ratio )
175201
{
176-
// only translate, keep the original scale (i.e. width x height)
177-
178-
double geom_center_x = (xa1 + xa2) / 2.0;
179-
double geom_center_y = (ya1 + ya2) / 2.0;
180-
double xx = geom_center_x - impl->origExtent.width() / 2.0;
181-
double yy = geom_center_y - impl->origExtent.height() / 2.0;
182-
new_extent = QgsRectangle( xx,
183-
yy,
184-
xx + impl->origExtent.width(),
185-
yy + impl->origExtent.height() );
202+
new_extent = QgsRectangle( (xa1 + xa2 + map_ratio * (ya1 - ya2)) / 2.0,
203+
ya1,
204+
xa1 + map_ratio * (ya2 - ya1),
205+
ya2);
186206
}
187-
else
207+
// geometry width is too big
208+
else if ( geom_ratio > map_ratio )
188209
{
189-
// auto scale
190-
191-
double geom_ratio = geom_rect.width() / geom_rect.height();
192-
double map_ratio = impl->origExtent.width() / impl->origExtent.height();
193-
194-
// geometry height is too big
195-
if ( geom_ratio < map_ratio )
196-
{
197-
new_extent = QgsRectangle( (xa1 + xa2 + map_ratio * (ya1 - ya2)) / 2.0,
198-
ya1,
199-
xa1 + map_ratio * (ya2 - ya1),
200-
ya2);
201-
}
202-
// geometry width is too big
203-
else if ( geom_ratio > map_ratio )
204-
{
205-
new_extent = QgsRectangle( xa1,
206-
(ya1 + ya2 + (xa1 - xa2) / map_ratio) / 2.0,
207-
xa2,
208-
ya1 + (xa2 - xa1) / map_ratio);
209-
}
210-
if ( impl->composition->atlasMap()->atlasMargin() > 0.0 )
211-
{
212-
new_extent.scale( 1 + impl->composition->atlasMap()->atlasMargin() );
213-
}
210+
new_extent = QgsRectangle( xa1,
211+
(ya1 + ya2 + (xa1 - xa2) / map_ratio) / 2.0,
212+
xa2,
213+
ya1 + (xa2 - xa1) / map_ratio);
214214
}
215-
216-
// evaluate label expressions
217-
QList<QgsComposerLabel*> labels;
218-
impl->composition->composerItems( labels );
219-
QgsExpression::setSpecialColumn( "$feature", QVariant( (int)featureI + 1 ) );
220-
221-
for ( QList<QgsComposerLabel*>::iterator lit = labels.begin(); lit != labels.end(); ++lit )
215+
if ( impl->composition->atlasMap()->atlasMargin() > 0.0 )
222216
{
223-
// build a local substitution map
224-
QMap<QString, QVariant> pageMap;
225-
pageMap.insert( "$page", QVariant( (int)impl->composition->itemPageNumber( *lit ) + 1 ) );
226-
(*lit)->setExpressionContext( fit, impl->composition->atlasMap()->atlasCoverageLayer(), pageMap );
217+
new_extent.scale( 1 + impl->composition->atlasMap()->atlasMargin() );
227218
}
228-
229-
230-
// set the new extent (and render)
231-
impl->composition->atlasMap()->setNewExtent( new_extent );
219+
}
220+
221+
// evaluate label expressions
222+
QList<QgsComposerLabel*> labels;
223+
impl->composition->composerItems( labels );
224+
QgsExpression::setSpecialColumn( "$feature", QVariant( (int)featureI + 1 ) );
225+
226+
for ( QList<QgsComposerLabel*>::iterator lit = labels.begin(); lit != labels.end(); ++lit )
227+
{
228+
(*lit)->setExpressionContext( fit, impl->composition->atlasMap()->atlasCoverageLayer() );
229+
}
230+
231+
// set the new extent (and render)
232+
impl->composition->atlasMap()->setNewExtent( new_extent );
232233
}
233234

234235
size_t QgsAtlasRendering::numFeatures() const
@@ -243,6 +244,9 @@ const QString& QgsAtlasRendering::currentFilename() const
243244

244245
void QgsAtlasRendering::end()
245246
{
247+
if ( !impl->composition || !impl->composition->atlasMap() || !impl->composition->atlasMap()->atlasCoverageLayer() )
248+
return;
249+
246250
// reset label expression contexts
247251
QList<QgsComposerLabel*> labels;
248252
impl->composition->composerItems( labels );
@@ -350,6 +354,10 @@ void QgsComposition::setNumPages( int pages )
350354
mPages.removeLast();
351355
}
352356
}
357+
358+
// update the corresponding variable
359+
QgsExpression::setSpecialColumn( "$numpages", QVariant((int)numPages()) );
360+
353361
emit nPagesChanged();
354362
}
355363

@@ -1700,6 +1708,8 @@ void QgsComposition::addPaperItem()
17001708
addItem( paperItem );
17011709
paperItem->setZValue( 0 );
17021710
mPages.push_back( paperItem );
1711+
1712+
QgsExpression::setSpecialColumn( "$numpages", QVariant((int)mPages.size()) );
17031713
}
17041714

17051715
void QgsComposition::removePaperItems()
@@ -1709,6 +1719,7 @@ void QgsComposition::removePaperItems()
17091719
delete mPages.at( i );
17101720
}
17111721
mPages.clear();
1722+
QgsExpression::setSpecialColumn( "$numpages", QVariant((int)0) );
17121723
}
17131724

17141725
void QgsComposition::deleteAndRemoveMultiFrames()
@@ -1836,3 +1847,32 @@ void QgsComposition::renderPage( QPainter* p, int page )
18361847

18371848
mPlotStyle = savedPlotStyle;
18381849
}
1850+
1851+
void QgsComposition::setAtlasMap( QgsComposerMap* map )
1852+
{
1853+
mAtlasMap = map;
1854+
if ( map != 0 )
1855+
{
1856+
QObject::connect( map, SIGNAL( atlasCoverageLayerChanged( QgsVectorLayer* )), this, SLOT( onAtlasCoverageChanged( QgsVectorLayer* ) ) );
1857+
}
1858+
else
1859+
{
1860+
QObject::disconnect( map, SIGNAL( atlasCoverageLayerChanged( QgsVectorLayer* )), this, SLOT( onAtlasCoverageChanged( QgsVectorLayer* ) ) );
1861+
}
1862+
}
1863+
1864+
void QgsComposition::onAtlasCoverageChanged( QgsVectorLayer* )
1865+
{
1866+
// update variables
1867+
if ( mAtlasMap != 0 && mAtlasMap->atlasCoverageLayer() != 0 )
1868+
{
1869+
QgsVectorDataProvider* provider = mAtlasMap->atlasCoverageLayer()->dataProvider();
1870+
QgsExpression::setSpecialColumn( "$numfeatures", QVariant( (int)provider->featureCount() ) );
1871+
}
1872+
else
1873+
{
1874+
QgsExpression::setSpecialColumn( "$numfeatures", QVariant( (int)0 ) );
1875+
}
1876+
//
1877+
QgsExpression::setSpecialColumn( "$numpages", QVariant( (int)numPages() ) );
1878+
}

0 commit comments

Comments
 (0)