Skip to content

Commit

Permalink
Merge pull request #2600 from m-kuhn/orderby-renderer
Browse files Browse the repository at this point in the history
Allow definition of feature rendering order
  • Loading branch information
m-kuhn committed Dec 22, 2015
2 parents 52a39fc + d5585ff commit e6a265c
Show file tree
Hide file tree
Showing 35 changed files with 869 additions and 59 deletions.
40 changes: 38 additions & 2 deletions python/core/qgsfeaturerequest.sip
Expand Up @@ -78,6 +78,42 @@ class QgsFeatureRequest

};

class OrderBy
{
public:
OrderBy();

OrderBy( const QList<QgsFeatureRequest::OrderByClause>& other );

/**
* Get a copy as a list of OrderByClauses
*
* This is only required in python where the inheritance
* is not properly propagated and this makes it usable.
*/
QList<QgsFeatureRequest::OrderByClause> list() const;

/**
* Serialize to XML
*/
void save( QDomElement& elem ) const;

/**
* Deserialize from XML
*/
void load( const QDomElement& elem );

/**
* Returns a set of used attributes
*/
QSet<QString> usedAttributes() const;

/**
* Dumps the content to an SQL equivalent syntax
*/
QString dump() const;
};

/**
* A special attribute that if set matches all attributes
*/
Expand Down Expand Up @@ -175,12 +211,12 @@ class QgsFeatureRequest
/**
* Return a list of order by clauses specified for this feature request.
*/
QList<QgsFeatureRequest::OrderByClause> orderBys() const;
QgsFeatureRequest::OrderBy orderBy() const;

/**
* Set a list of order by clauses.
*/
void setOrderBys(const QList<QgsFeatureRequest::OrderByClause>& orderBys );
QgsFeatureRequest& setOrderBy(const QgsFeatureRequest::OrderBy& orderBy );

/** Set the maximum number of features to request.
* @param limit maximum number of features, or -1 to request all features.
Expand Down
12 changes: 12 additions & 0 deletions python/core/symbology-ng/qgsrendererv2.sip
Expand Up @@ -312,6 +312,18 @@ class QgsFeatureRendererV2
*/
void setForceRasterRender( bool forceRaster );

/**
* Get the order in which features shall be processed by this renderer.
* @note added in QGIS 2.14
*/
QgsFeatureRequest::OrderBy orderBy() const;

/**
* Define the order in which features shall be processed by this renderer.
* @note added in QGIS 2.14
*/
void setOrderBy( const QgsFeatureRequest::OrderBy& orderBy );

protected:
QgsFeatureRendererV2( const QString& type );

Expand Down
34 changes: 34 additions & 0 deletions python/gui/qgsorderbydialog.sip
@@ -0,0 +1,34 @@
/***************************************************************************
qgsorderbydialog.h

---------------------
begin : 20.12.2015
copyright : (C) 2015 by Matthias Kuhn
email : matthias@opengis.ch
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

class QgsOrderByDialog : QDialog
{
%TypeHeaderCode
#include "qgsorderbydialog.h"
%End
public:
QgsOrderByDialog( QgsVectorLayer* layer, QWidget* parent = nullptr );

/**
* Set the order by to manage
*/
void setOrderBys( const QList<QgsFeatureRequest::OrderByClause>& orderBys );

/**
* Get the order by defined in the dialog
*/
QgsFeatureRequest::OrderBy orderBys();
};
13 changes: 11 additions & 2 deletions src/core/qgsfeatureiterator.cpp
Expand Up @@ -120,6 +120,7 @@ class QgsExpressionSorter
QgsAbstractFeatureIterator::QgsAbstractFeatureIterator( const QgsFeatureRequest& request )
: mRequest( request )
, mClosed( false )
, mZombie( false )
, refs( 0 )
, mFetchedCount( 0 )
, mGeometrySimplifier( nullptr )
Expand Down Expand Up @@ -149,6 +150,12 @@ bool QgsAbstractFeatureIterator::nextFeature( QgsFeature& f )
++mFeatureIterator;
dataOk = true;
}
else
{
dataOk = false;
// even the zombie dies at this point...
mZombie = false;
}
}
else
{
Expand Down Expand Up @@ -213,7 +220,7 @@ void QgsAbstractFeatureIterator::ref()
prepareSimplification( mRequest.simplifyMethod() );

// Should be called as last preparation step since it possibly will already fetch all features
setupOrderBy( mRequest.orderBys() );
setupOrderBy( mRequest.orderBy() );
}
refs++;
}
Expand Down Expand Up @@ -279,10 +286,12 @@ void QgsAbstractFeatureIterator::setupOrderBy( const QList<QgsFeatureRequest::Or
mCachedFeatures.append( indexedFeature );
}

std::sort( mCachedFeatures.begin(), mCachedFeatures.end(), QgsExpressionSorter( preparedOrderBys ) );
qSort( mCachedFeatures.begin(), mCachedFeatures.end(), QgsExpressionSorter( preparedOrderBys ) );

mFeatureIterator = mCachedFeatures.constBegin();
mUseCachedFeatures = true;
// The real iterator is closed, we are only serving cached features
mZombie = true;
}
}

Expand Down
12 changes: 11 additions & 1 deletion src/core/qgsfeatureiterator.h
Expand Up @@ -89,7 +89,17 @@ class CORE_EXPORT QgsAbstractFeatureIterator
/** Set to true, as soon as the iterator is closed. */
bool mClosed;

/**
* A feature iterator may be closed already but still be serving features from the cache.
* This is done when we serve features which have been pre-fetched and the order by has
* been locally sorted.
* In such a scenario, all resources have been released (mClosed is true) but the deads
* are still alive.
*/
bool mZombie;

//! reference counting (to allow seamless copying of QgsFeatureIterator instances)
//! TODO QGIS3: make this private
int refs;
void ref(); //!< add reference
void deref(); //!< remove reference, delete if refs == 0
Expand Down Expand Up @@ -247,7 +257,7 @@ inline bool QgsFeatureIterator::close()

inline bool QgsFeatureIterator::isClosed() const
{
return mIter ? mIter->mClosed : true;
return mIter ? mIter->mClosed && !mIter->mZombie : true;
}

inline bool operator== ( const QgsFeatureIterator &fi1, const QgsFeatureIterator &fi2 )
Expand Down
98 changes: 91 additions & 7 deletions src/core/qgsfeaturerequest.cpp
Expand Up @@ -84,7 +84,7 @@ QgsFeatureRequest& QgsFeatureRequest::operator=( const QgsFeatureRequest & rh )
mAttrs = rh.mAttrs;
mSimplifyMethod = rh.mSimplifyMethod;
mLimit = rh.mLimit;
mOrderBys = rh.mOrderBys;
mOrderBy = rh.mOrderBy;
return *this;
}

Expand Down Expand Up @@ -144,24 +144,25 @@ QgsFeatureRequest &QgsFeatureRequest::setExpressionContext( const QgsExpressionC

QgsFeatureRequest& QgsFeatureRequest::addOrderBy( const QString& expression, bool ascending )
{
mOrderBys.append( OrderByClause( expression, ascending ) );
mOrderBy.append( OrderByClause( expression, ascending ) );
return *this;
}

QgsFeatureRequest& QgsFeatureRequest::addOrderBy( const QString& expression, bool ascending, bool nullsfirst )
{
mOrderBys.append( OrderByClause( expression, ascending, nullsfirst ) );
mOrderBy.append( OrderByClause( expression, ascending, nullsfirst ) );
return *this;
}

QList<QgsFeatureRequest::OrderByClause> QgsFeatureRequest::orderBys() const
QgsFeatureRequest::OrderBy QgsFeatureRequest::orderBy() const
{
return mOrderBys;
return mOrderBy;
}

void QgsFeatureRequest::setOrderBys( const QList<QgsFeatureRequest::OrderByClause>& orderBys )
QgsFeatureRequest& QgsFeatureRequest::setOrderBy( const QgsFeatureRequest::OrderBy& orderBy )
{
mOrderBys = orderBys;
mOrderBy = orderBy;
return *this;
}

QgsFeatureRequest& QgsFeatureRequest::setLimit( long limit )
Expand Down Expand Up @@ -307,7 +308,90 @@ void QgsFeatureRequest::OrderByClause::setNullsFirst( bool nullsFirst )
mNullsFirst = nullsFirst;
}

QString QgsFeatureRequest::OrderByClause::dump() const
{
return QString( "%1 %2 %3" )
.arg( mExpression.expression() )
.arg( mAscending ? "ASC" : "DESC" )
.arg( mNullsFirst ? "NULLS FIRST" : "NULLS LAST" );
}

QgsExpression QgsFeatureRequest::OrderByClause::expression() const
{
return mExpression;
}

QgsFeatureRequest::OrderBy::OrderBy( const QList<QgsFeatureRequest::OrderByClause>& other )
{
Q_FOREACH ( const QgsFeatureRequest::OrderByClause& clause, other )
{
append( clause );
}
}

QList<QgsFeatureRequest::OrderByClause> QgsFeatureRequest::OrderBy::list() const
{
return *this;
}

void QgsFeatureRequest::OrderBy::save( QDomElement& elem ) const
{
QDomDocument doc = elem.ownerDocument();
QList<OrderByClause>::ConstIterator it;
for ( it = constBegin(); it != constEnd(); ++it )
{
const OrderByClause& clause = *it;
QDomElement clauseElem = doc.createElement( "orderByClause" );
clauseElem.setAttribute( "asc", clause.ascending() );
clauseElem.setAttribute( "nullsFirst", clause.nullsFirst() );
clauseElem.appendChild( doc.createTextNode( clause.expression().expression() ) );

elem.appendChild( clauseElem );
}
}

void QgsFeatureRequest::OrderBy::load( const QDomElement& elem )
{
clear();

QDomNodeList clauses = elem.childNodes();

for ( int i = 0; i < clauses.size(); ++i )
{
QDomElement clauseElem = clauses.at( i ).toElement();
QString expression = clauseElem.text();
bool asc = clauseElem.attribute( "asc" ).toInt() != 0;
bool nullsFirst = clauseElem.attribute( "nullsFirst" ).toInt() != 0;

append( OrderByClause( expression, asc, nullsFirst ) );
}
}

QSet<QString> QgsFeatureRequest::OrderBy::usedAttributes() const
{
QSet<QString> usedAttributes;

QList<OrderByClause>::ConstIterator it;
for ( it = constBegin(); it != constEnd(); ++it )
{
const OrderByClause& clause = *it;

usedAttributes.unite( clause.expression().referencedColumns().toSet() );
}

return usedAttributes;
}
QString QgsFeatureRequest::OrderBy::dump() const
{
QStringList results;

QList<OrderByClause>::ConstIterator it;
for ( it = constBegin(); it != constEnd(); ++it )
{
const OrderByClause& clause = *it;

results << clause.dump();
}

return results.join( ", " );
}

0 comments on commit e6a265c

Please sign in to comment.