Skip to content

Commit e6a265c

Browse files
committed
Merge pull request #2600 from m-kuhn/orderby-renderer
Allow definition of feature rendering order
2 parents 52a39fc + d5585ff commit e6a265c

35 files changed

+869
-59
lines changed

python/core/qgsfeaturerequest.sip

+38-2
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,42 @@ class QgsFeatureRequest
7878

7979
};
8080

81+
class OrderBy
82+
{
83+
public:
84+
OrderBy();
85+
86+
OrderBy( const QList<QgsFeatureRequest::OrderByClause>& other );
87+
88+
/**
89+
* Get a copy as a list of OrderByClauses
90+
*
91+
* This is only required in python where the inheritance
92+
* is not properly propagated and this makes it usable.
93+
*/
94+
QList<QgsFeatureRequest::OrderByClause> list() const;
95+
96+
/**
97+
* Serialize to XML
98+
*/
99+
void save( QDomElement& elem ) const;
100+
101+
/**
102+
* Deserialize from XML
103+
*/
104+
void load( const QDomElement& elem );
105+
106+
/**
107+
* Returns a set of used attributes
108+
*/
109+
QSet<QString> usedAttributes() const;
110+
111+
/**
112+
* Dumps the content to an SQL equivalent syntax
113+
*/
114+
QString dump() const;
115+
};
116+
81117
/**
82118
* A special attribute that if set matches all attributes
83119
*/
@@ -175,12 +211,12 @@ class QgsFeatureRequest
175211
/**
176212
* Return a list of order by clauses specified for this feature request.
177213
*/
178-
QList<QgsFeatureRequest::OrderByClause> orderBys() const;
214+
QgsFeatureRequest::OrderBy orderBy() const;
179215

180216
/**
181217
* Set a list of order by clauses.
182218
*/
183-
void setOrderBys(const QList<QgsFeatureRequest::OrderByClause>& orderBys );
219+
QgsFeatureRequest& setOrderBy(const QgsFeatureRequest::OrderBy& orderBy );
184220

185221
/** Set the maximum number of features to request.
186222
* @param limit maximum number of features, or -1 to request all features.

python/core/symbology-ng/qgsrendererv2.sip

+12
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,18 @@ class QgsFeatureRendererV2
312312
*/
313313
void setForceRasterRender( bool forceRaster );
314314

315+
/**
316+
* Get the order in which features shall be processed by this renderer.
317+
* @note added in QGIS 2.14
318+
*/
319+
QgsFeatureRequest::OrderBy orderBy() const;
320+
321+
/**
322+
* Define the order in which features shall be processed by this renderer.
323+
* @note added in QGIS 2.14
324+
*/
325+
void setOrderBy( const QgsFeatureRequest::OrderBy& orderBy );
326+
315327
protected:
316328
QgsFeatureRendererV2( const QString& type );
317329

python/gui/qgsorderbydialog.sip

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/***************************************************************************
2+
qgsorderbydialog.h
3+
4+
---------------------
5+
begin : 20.12.2015
6+
copyright : (C) 2015 by Matthias Kuhn
7+
email : matthias@opengis.ch
8+
***************************************************************************
9+
* *
10+
* This program is free software; you can redistribute it and/or modify *
11+
* it under the terms of the GNU General Public License as published by *
12+
* the Free Software Foundation; either version 2 of the License, or *
13+
* (at your option) any later version. *
14+
* *
15+
***************************************************************************/
16+
17+
class QgsOrderByDialog : QDialog
18+
{
19+
%TypeHeaderCode
20+
#include "qgsorderbydialog.h"
21+
%End
22+
public:
23+
QgsOrderByDialog( QgsVectorLayer* layer, QWidget* parent = nullptr );
24+
25+
/**
26+
* Set the order by to manage
27+
*/
28+
void setOrderBys( const QList<QgsFeatureRequest::OrderByClause>& orderBys );
29+
30+
/**
31+
* Get the order by defined in the dialog
32+
*/
33+
QgsFeatureRequest::OrderBy orderBys();
34+
};

src/core/qgsfeatureiterator.cpp

+11-2
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ class QgsExpressionSorter
120120
QgsAbstractFeatureIterator::QgsAbstractFeatureIterator( const QgsFeatureRequest& request )
121121
: mRequest( request )
122122
, mClosed( false )
123+
, mZombie( false )
123124
, refs( 0 )
124125
, mFetchedCount( 0 )
125126
, mGeometrySimplifier( nullptr )
@@ -149,6 +150,12 @@ bool QgsAbstractFeatureIterator::nextFeature( QgsFeature& f )
149150
++mFeatureIterator;
150151
dataOk = true;
151152
}
153+
else
154+
{
155+
dataOk = false;
156+
// even the zombie dies at this point...
157+
mZombie = false;
158+
}
152159
}
153160
else
154161
{
@@ -213,7 +220,7 @@ void QgsAbstractFeatureIterator::ref()
213220
prepareSimplification( mRequest.simplifyMethod() );
214221

215222
// Should be called as last preparation step since it possibly will already fetch all features
216-
setupOrderBy( mRequest.orderBys() );
223+
setupOrderBy( mRequest.orderBy() );
217224
}
218225
refs++;
219226
}
@@ -279,10 +286,12 @@ void QgsAbstractFeatureIterator::setupOrderBy( const QList<QgsFeatureRequest::Or
279286
mCachedFeatures.append( indexedFeature );
280287
}
281288

282-
std::sort( mCachedFeatures.begin(), mCachedFeatures.end(), QgsExpressionSorter( preparedOrderBys ) );
289+
qSort( mCachedFeatures.begin(), mCachedFeatures.end(), QgsExpressionSorter( preparedOrderBys ) );
283290

284291
mFeatureIterator = mCachedFeatures.constBegin();
285292
mUseCachedFeatures = true;
293+
// The real iterator is closed, we are only serving cached features
294+
mZombie = true;
286295
}
287296
}
288297

src/core/qgsfeatureiterator.h

+11-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,17 @@ class CORE_EXPORT QgsAbstractFeatureIterator
8989
/** Set to true, as soon as the iterator is closed. */
9090
bool mClosed;
9191

92+
/**
93+
* A feature iterator may be closed already but still be serving features from the cache.
94+
* This is done when we serve features which have been pre-fetched and the order by has
95+
* been locally sorted.
96+
* In such a scenario, all resources have been released (mClosed is true) but the deads
97+
* are still alive.
98+
*/
99+
bool mZombie;
100+
92101
//! reference counting (to allow seamless copying of QgsFeatureIterator instances)
102+
//! TODO QGIS3: make this private
93103
int refs;
94104
void ref(); //!< add reference
95105
void deref(); //!< remove reference, delete if refs == 0
@@ -247,7 +257,7 @@ inline bool QgsFeatureIterator::close()
247257

248258
inline bool QgsFeatureIterator::isClosed() const
249259
{
250-
return mIter ? mIter->mClosed : true;
260+
return mIter ? mIter->mClosed && !mIter->mZombie : true;
251261
}
252262

253263
inline bool operator== ( const QgsFeatureIterator &fi1, const QgsFeatureIterator &fi2 )

src/core/qgsfeaturerequest.cpp

+91-7
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ QgsFeatureRequest& QgsFeatureRequest::operator=( const QgsFeatureRequest & rh )
8484
mAttrs = rh.mAttrs;
8585
mSimplifyMethod = rh.mSimplifyMethod;
8686
mLimit = rh.mLimit;
87-
mOrderBys = rh.mOrderBys;
87+
mOrderBy = rh.mOrderBy;
8888
return *this;
8989
}
9090

@@ -144,24 +144,25 @@ QgsFeatureRequest &QgsFeatureRequest::setExpressionContext( const QgsExpressionC
144144

145145
QgsFeatureRequest& QgsFeatureRequest::addOrderBy( const QString& expression, bool ascending )
146146
{
147-
mOrderBys.append( OrderByClause( expression, ascending ) );
147+
mOrderBy.append( OrderByClause( expression, ascending ) );
148148
return *this;
149149
}
150150

151151
QgsFeatureRequest& QgsFeatureRequest::addOrderBy( const QString& expression, bool ascending, bool nullsfirst )
152152
{
153-
mOrderBys.append( OrderByClause( expression, ascending, nullsfirst ) );
153+
mOrderBy.append( OrderByClause( expression, ascending, nullsfirst ) );
154154
return *this;
155155
}
156156

157-
QList<QgsFeatureRequest::OrderByClause> QgsFeatureRequest::orderBys() const
157+
QgsFeatureRequest::OrderBy QgsFeatureRequest::orderBy() const
158158
{
159-
return mOrderBys;
159+
return mOrderBy;
160160
}
161161

162-
void QgsFeatureRequest::setOrderBys( const QList<QgsFeatureRequest::OrderByClause>& orderBys )
162+
QgsFeatureRequest& QgsFeatureRequest::setOrderBy( const QgsFeatureRequest::OrderBy& orderBy )
163163
{
164-
mOrderBys = orderBys;
164+
mOrderBy = orderBy;
165+
return *this;
165166
}
166167

167168
QgsFeatureRequest& QgsFeatureRequest::setLimit( long limit )
@@ -307,7 +308,90 @@ void QgsFeatureRequest::OrderByClause::setNullsFirst( bool nullsFirst )
307308
mNullsFirst = nullsFirst;
308309
}
309310

311+
QString QgsFeatureRequest::OrderByClause::dump() const
312+
{
313+
return QString( "%1 %2 %3" )
314+
.arg( mExpression.expression() )
315+
.arg( mAscending ? "ASC" : "DESC" )
316+
.arg( mNullsFirst ? "NULLS FIRST" : "NULLS LAST" );
317+
}
318+
310319
QgsExpression QgsFeatureRequest::OrderByClause::expression() const
311320
{
312321
return mExpression;
313322
}
323+
324+
QgsFeatureRequest::OrderBy::OrderBy( const QList<QgsFeatureRequest::OrderByClause>& other )
325+
{
326+
Q_FOREACH ( const QgsFeatureRequest::OrderByClause& clause, other )
327+
{
328+
append( clause );
329+
}
330+
}
331+
332+
QList<QgsFeatureRequest::OrderByClause> QgsFeatureRequest::OrderBy::list() const
333+
{
334+
return *this;
335+
}
336+
337+
void QgsFeatureRequest::OrderBy::save( QDomElement& elem ) const
338+
{
339+
QDomDocument doc = elem.ownerDocument();
340+
QList<OrderByClause>::ConstIterator it;
341+
for ( it = constBegin(); it != constEnd(); ++it )
342+
{
343+
const OrderByClause& clause = *it;
344+
QDomElement clauseElem = doc.createElement( "orderByClause" );
345+
clauseElem.setAttribute( "asc", clause.ascending() );
346+
clauseElem.setAttribute( "nullsFirst", clause.nullsFirst() );
347+
clauseElem.appendChild( doc.createTextNode( clause.expression().expression() ) );
348+
349+
elem.appendChild( clauseElem );
350+
}
351+
}
352+
353+
void QgsFeatureRequest::OrderBy::load( const QDomElement& elem )
354+
{
355+
clear();
356+
357+
QDomNodeList clauses = elem.childNodes();
358+
359+
for ( int i = 0; i < clauses.size(); ++i )
360+
{
361+
QDomElement clauseElem = clauses.at( i ).toElement();
362+
QString expression = clauseElem.text();
363+
bool asc = clauseElem.attribute( "asc" ).toInt() != 0;
364+
bool nullsFirst = clauseElem.attribute( "nullsFirst" ).toInt() != 0;
365+
366+
append( OrderByClause( expression, asc, nullsFirst ) );
367+
}
368+
}
369+
370+
QSet<QString> QgsFeatureRequest::OrderBy::usedAttributes() const
371+
{
372+
QSet<QString> usedAttributes;
373+
374+
QList<OrderByClause>::ConstIterator it;
375+
for ( it = constBegin(); it != constEnd(); ++it )
376+
{
377+
const OrderByClause& clause = *it;
378+
379+
usedAttributes.unite( clause.expression().referencedColumns().toSet() );
380+
}
381+
382+
return usedAttributes;
383+
}
384+
QString QgsFeatureRequest::OrderBy::dump() const
385+
{
386+
QStringList results;
387+
388+
QList<OrderByClause>::ConstIterator it;
389+
for ( it = constBegin(); it != constEnd(); ++it )
390+
{
391+
const OrderByClause& clause = *it;
392+
393+
results << clause.dump();
394+
}
395+
396+
return results.join( ", " );
397+
}

0 commit comments

Comments
 (0)