Skip to content
Permalink
Browse files

Merge pull request #2600 from m-kuhn/orderby-renderer

Allow definition of feature rendering order
  • Loading branch information
m-kuhn committed Dec 22, 2015
2 parents 52a39fc + d5585ff commit e6a265c1030bae01aa8d0eca905693e371ff0bd7
Showing with 869 additions and 59 deletions.
  1. +38 −2 python/core/qgsfeaturerequest.sip
  2. +12 −0 python/core/symbology-ng/qgsrendererv2.sip
  3. +34 −0 python/gui/qgsorderbydialog.sip
  4. +11 −2 src/core/qgsfeatureiterator.cpp
  5. +11 −1 src/core/qgsfeatureiterator.h
  6. +91 −7 src/core/qgsfeaturerequest.cpp
  7. +62 −3 src/core/qgsfeaturerequest.h
  8. +17 −4 src/core/qgsvectorlayerfeatureiterator.cpp
  9. +4 −1 src/core/qgsvectorlayerrenderer.cpp
  10. +8 −1 src/core/symbology-ng/qgscategorizedsymbolrendererv2.cpp
  11. +8 −1 src/core/symbology-ng/qgsgraduatedsymbolrendererv2.cpp
  12. +8 −1 src/core/symbology-ng/qgsheatmaprenderer.cpp
  13. +1 −1 src/core/symbology-ng/qgsinvertedpolygonrenderer.cpp
  14. +1 −1 src/core/symbology-ng/qgspointdisplacementrenderer.cpp
  15. +29 −1 src/core/symbology-ng/qgsrendererv2.cpp
  16. +27 −1 src/core/symbology-ng/qgsrendererv2.h
  17. +1 −1 src/core/symbology-ng/qgsrulebasedrendererv2.cpp
  18. +7 −1 src/core/symbology-ng/qgssinglesymbolrendererv2.cpp
  19. +2 −0 src/gui/CMakeLists.txt
  20. +4 −0 src/gui/qgsfieldexpressionwidget.h
  21. +147 −0 src/gui/qgsorderbydialog.cpp
  22. +69 −0 src/gui/qgsorderbydialog.h
  23. +44 −14 src/gui/symbology-ng/qgsrendererv2propertiesdialog.cpp
  24. +7 −0 src/gui/symbology-ng/qgsrendererv2propertiesdialog.h
  25. +1 −1 src/providers/mssql/qgsmssqlfeatureiterator.cpp
  26. +4 −2 src/providers/ogr/qgsogrfeatureiterator.cpp
  27. +2 −2 src/providers/postgres/qgspostgresfeatureiterator.cpp
  28. +2 −2 src/providers/spatialite/qgsspatialitefeatureiterator.cpp
  29. +4 −0 src/providers/virtual/qgsvirtuallayersqlitemodule.cpp
  30. +83 −0 src/ui/qgsorderbydialogbase.ui
  31. +34 −9 src/ui/qgsrendererv2propsdialogbase.ui
  32. +1 −0 tests/src/python/CMakeLists.txt
  33. +5 −0 tests/src/python/providertestbase.py
  34. +90 −0 tests/src/python/test_qgssinglesymbolrenderer.py
  35. BIN tests/testdata/control_images/expected_singlesymbol_orderby/expected_singlesymbol_orderby.png
@@ -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
*/
@@ -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.
@@ -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 );

@@ -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();
};
@@ -120,6 +120,7 @@ class QgsExpressionSorter
QgsAbstractFeatureIterator::QgsAbstractFeatureIterator( const QgsFeatureRequest& request )
: mRequest( request )
, mClosed( false )
, mZombie( false )
, refs( 0 )
, mFetchedCount( 0 )
, mGeometrySimplifier( nullptr )
@@ -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
{
@@ -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++;
}
@@ -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;
}
}

@@ -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
@@ -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 )
@@ -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;
}

@@ -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 )
@@ -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.
You can’t perform that action at this time.