@@ -30,7 +30,8 @@ QgsAtlasComposition::QgsAtlasComposition( QgsComposition* composition ) :
30
30
mEnabled( false ),
31
31
mComposerMap( 0 ),
32
32
mHideCoverage( false ), mFixedScale( false ), mMargin( 0.10 ), mFilenamePattern( " 'output_'||$feature" ),
33
- mCoverageLayer( 0 ), mSingleFile( false )
33
+ mCoverageLayer( 0 ), mSingleFile( false ),
34
+ mSortFeatures( false ), mSortAscending( true ), mFeatureFilter( " " )
34
35
{
35
36
36
37
// declare special columns with a default value
@@ -52,6 +53,33 @@ void QgsAtlasComposition::setCoverageLayer( QgsVectorLayer* layer )
52
53
QgsExpression::setSpecialColumn ( " $numfeatures" , QVariant (( int )mFeatureIds .size () ) );
53
54
}
54
55
56
+ //
57
+ // Private class only used for the sorting of features
58
+ class FieldSorter
59
+ {
60
+ public:
61
+ FieldSorter ( QgsAtlasComposition::SorterKeys& keys, bool ascending = true ) : mKeys ( keys ), mAscending ( ascending ) {}
62
+
63
+ bool operator ()( const QgsFeatureId& id1, const QgsFeatureId& id2 )
64
+ {
65
+ bool result;
66
+ if ( mKeys [ id1 ].type () == QVariant::Int ) {
67
+ result = mKeys [ id1 ].toInt () < mKeys [ id2 ].toInt ();
68
+ }
69
+ else if ( mKeys [ id1 ].type () == QVariant::Double ) {
70
+ result = mKeys [ id1 ].toDouble () < mKeys [ id2 ].toDouble ();
71
+ }
72
+ else if ( mKeys [ id1 ].type () == QVariant::String ) {
73
+ result = (QString::localeAwareCompare (mKeys [ id1 ].toString (), mKeys [ id2 ].toString ()) < 0 );
74
+ }
75
+
76
+ return mAscending ? result : !result;
77
+ }
78
+ private:
79
+ QgsAtlasComposition::SorterKeys& mKeys ;
80
+ bool mAscending ;
81
+ };
82
+
55
83
void QgsAtlasComposition::beginRender ()
56
84
{
57
85
if ( !mComposerMap || !mCoverageLayer )
@@ -84,13 +112,45 @@ void QgsAtlasComposition::beginRender()
84
112
// select all features with all attributes
85
113
QgsFeatureIterator fit = mCoverageLayer ->getFeatures ();
86
114
115
+ std::auto_ptr<QgsExpression> filterExpression;
116
+ if ( mFeatureFilter .size () > 0 ) {
117
+ filterExpression = std::auto_ptr<QgsExpression>(new QgsExpression ( mFeatureFilter ));
118
+ if ( filterExpression->hasParserError () )
119
+ {
120
+ throw std::runtime_error ( " Feature filter parser error: " + filterExpression->parserErrorString ().toStdString () );
121
+ }
122
+ }
123
+
87
124
// We cannot use nextFeature() directly since the feature pointer is rewinded by the rendering process
88
125
// We thus store the feature ids for future extraction
89
126
QgsFeature feat;
90
127
mFeatureIds .clear ();
128
+ mFeatureKeys .clear ();
91
129
while ( fit.nextFeature ( feat ) )
92
130
{
93
- mFeatureIds .push_back ( feat.id () );
131
+ if ( mFeatureFilter .size () > 0 ) {
132
+ QVariant result = filterExpression->evaluate ( &feat, mCoverageLayer ->pendingFields () );
133
+ if ( filterExpression->hasEvalError () )
134
+ {
135
+ throw std::runtime_error ( " Feature filter eval error: " + filterExpression->evalErrorString ().toStdString () );
136
+ }
137
+
138
+ // skip this feature if the filter evaluation if false
139
+ if ( !result.toBool () ) {
140
+ continue ;
141
+ }
142
+ }
143
+ mFeatureIds .push_back ( feat.id () );
144
+
145
+ if ( mSortFeatures ) {
146
+ mFeatureKeys .insert ( std::make_pair ( feat.id (), feat.attributes ()[ mSortKeyAttributeIdx ] ) );
147
+ }
148
+ }
149
+
150
+ // sort features, if asked for
151
+ if ( mSortFeatures ) {
152
+ FieldSorter sorter ( mFeatureKeys , mSortAscending );
153
+ std::sort ( mFeatureIds .begin (), mFeatureIds .end (), sorter );
94
154
}
95
155
96
156
mOrigExtent = mComposerMap ->extent ();
@@ -159,10 +219,6 @@ void QgsAtlasComposition::prepareForFeature( size_t featureI )
159
219
{
160
220
QgsExpression::setSpecialColumn ( " $feature" , QVariant (( int )featureI + 1 ) );
161
221
QVariant filenameRes = mFilenameExpr ->evaluate ( &mCurrentFeature );
162
- if ( mFilenameExpr ->hasEvalError () )
163
- {
164
- throw std::runtime_error ( " Filename eval error: " + mFilenameExpr ->evalErrorString ().toStdString () );
165
- }
166
222
167
223
mCurrentFilename = filenameRes.toString ();
168
224
}
@@ -284,6 +340,13 @@ void QgsAtlasComposition::writeXML( QDomElement& elem, QDomDocument& doc ) const
284
340
atlasElem.setAttribute ( " margin" , QString::number ( mMargin ) );
285
341
atlasElem.setAttribute ( " filenamePattern" , mFilenamePattern );
286
342
343
+ atlasElem.setAttribute ( " sortFeatures" , mSortFeatures ? " true" : " false" );
344
+ if ( mSortFeatures ) {
345
+ atlasElem.setAttribute ( " sortKey" , QString::number (mSortKeyAttributeIdx ) );
346
+ atlasElem.setAttribute ( " sortAscending" , mSortAscending ? " true" : " false" );
347
+ }
348
+ atlasElem.setAttribute ( " featureFilter" , mFeatureFilter );
349
+
287
350
elem.appendChild ( atlasElem );
288
351
}
289
352
@@ -324,5 +387,12 @@ void QgsAtlasComposition::readXML( const QDomElement& atlasElem, const QDomDocum
324
387
mSingleFile = atlasElem.attribute ( " singleFile" , " false" ) == " true" ? true : false ;
325
388
mFilenamePattern = atlasElem.attribute ( " filenamePattern" , " " );
326
389
390
+ mSortFeatures = atlasElem.attribute ( " sortFeatures" , " false" ) == " true" ? true : false ;
391
+ if ( mSortFeatures ) {
392
+ mSortKeyAttributeIdx = atlasElem.attribute ( " sortKey" , " 0" ).toInt ();
393
+ mSortAscending = atlasElem.attribute ( " sortAscending" , " true" ) == " true" ? true : false ;
394
+ }
395
+ mFeatureFilter = atlasElem.attribute ( " featureFilter" , " " );
396
+
327
397
emit parameterChanged ();
328
398
}
0 commit comments