The following commit has been merged in the master branch: commit ad955e26bf9fe7281c286c18b3b41fde6846b3bd Author: Martin Dobias <wonder...@gmail.com> Date: Fri Mar 2 00:06:02 2012 +0100
Added willRenderFeature() and symbolsForFeature() utility methods. diff --git a/python/core/symbology-ng-core.sip b/python/core/symbology-ng-core.sip index 6e3784e..a1b25e8 100644 --- a/python/core/symbology-ng-core.sip +++ b/python/core/symbology-ng-core.sip @@ -77,6 +77,17 @@ public: virtual QString dump(); + enum Capabilities + { + SymbolLevels = 1, // rendering with symbol levels (i.e. implements symbols(), symbolForFeature()) + RotationField = 2, // rotate symbols by attribute value + MoreSymbolsPerFeature = 4 // may use more than one symbol to render a feature: symbolsForFeature() will return them + }; + + //! returns bitwise OR-ed capabilities of the renderer + //! \note added in 2.0 + virtual int capabilities(); + virtual QgsFeatureRendererV2* clone()=0 /Factory/; virtual QgsSymbolV2List symbols()=0; @@ -105,6 +116,18 @@ public: //! @note added in 1.9 virtual void setRotationField( QString fieldName ); + //! return whether the renderer will render a feature or not. + //! Must be called between startRender() and stopRender() calls. + //! Default implementation uses symbolForFeature(). + //! @note added in 1.9 + virtual bool willRenderFeature( QgsFeature& feat ); + + //! return list of symbols used for rendering the feature. + //! For renderers that do not support MoreSymbolsPerFeature it is more efficient + //! to use symbolForFeature() + //! @note added in 1.9 + virtual QgsSymbolV2List symbolsForFeature( QgsFeature& feat ); + protected: QgsFeatureRendererV2(QString type); @@ -154,6 +177,10 @@ public: virtual QString dump(); + //! returns bitwise OR-ed capabilities of the renderer + //! \note added in 2.0 + virtual int capabilities(); + virtual QgsFeatureRendererV2* clone() /Factory/; virtual QgsSymbolV2List symbols(); @@ -221,6 +248,10 @@ public: virtual QString dump(); + //! returns bitwise OR-ed capabilities of the renderer + //! \note added in 2.0 + virtual int capabilities(); + virtual QgsFeatureRendererV2* clone() /Factory/; virtual QgsSymbolV2List symbols(); @@ -320,6 +351,10 @@ public: virtual QString dump(); + //! returns bitwise OR-ed capabilities of the renderer + //! \note added in 2.0 + virtual int capabilities(); + virtual QgsFeatureRendererV2* clone() /Factory/; virtual QgsSymbolV2List symbols(); @@ -410,27 +445,76 @@ class QgsRuleBasedRendererV2 : QgsFeatureRendererV2 class Rule { public: - //! Constructor takes ownership of the symbol - Rule( QgsSymbolV2* symbol /Transfer/, int scaleMinDenom = 0, int scaleMaxDenom = 0, QString filterExp = QString() ); - //Rule( const QgsRuleBasedRendererV2::Rule& other ); + + Rule( QgsSymbolV2* symbol /Transfer/, int scaleMinDenom = 0, int scaleMaxDenom = 0, QString filterExp = QString(), + QString label = QString(), QString description = QString() ); ~Rule(); - QString dump() const; - //QStringList needsFields() const; + QString dump( int offset = 0 ) const; + QSet<QString> usedAttributes(); + QgsSymbolV2List symbols(); + // TODO QgsLegendSymbolList legendSymbolItems(); bool isFilterOK( QgsFeature& f ) const; bool isScaleOK( double scale ) const; QgsSymbolV2* symbol(); + QString label() const; bool dependsOnScale() const; int scaleMinDenom() const; int scaleMaxDenom() const; - QString filterExpression() const; QgsExpression* filter() const; + QString filterExpression() const; + QString description() const; + //! set a new symbol (or NULL). Deletes old symbol. + void setSymbol( QgsSymbolV2* sym /Transfer/ ); + void setLabel( QString label ); void setScaleMinDenom( int scaleMinDenom ); void setScaleMaxDenom( int scaleMaxDenom ); void setFilterExpression( QString filterExp ); + void setDescription( QString description ); + + //! clone this rule, return new instance + QgsRuleBasedRendererV2::Rule* clone() const /Factory/; + + QDomElement save( QDomDocument& doc, QgsSymbolV2Map& symbolMap ); + + //! prepare the rule for rendering and its children (build active children array) + bool startRender( QgsRenderContext& context, const QgsVectorLayer *vlayer ); + //! get all used z-levels from this rule and children + QSet<int> collectZLevels(); + //! assign normalized z-levels [0..N-1] for this rule's symbol for quick access during rendering + // TODO void setNormZLevels( const QMap<int, int>& zLevelsToNormLevels ); + + // TODO bool renderFeature( FeatureToRender& featToRender, QgsRenderContext& context, RenderQueue& renderQueue ); + + //! only tell whether a feature will be rendered without actually rendering it + //! @note added in 1.9 + bool willRenderFeature( QgsFeature& feat ); + + //! tell which symbols will be used to render the feature + //! @note added in 1.9 + QgsSymbolV2List symbolsForFeature( QgsFeature& feat ); + + void stopRender( QgsRenderContext& context ); + + static QgsRuleBasedRendererV2::Rule* create( QDomElement& ruleElem, QgsSymbolV2Map& symbolMap ) /Factory/; + + QList<QgsRuleBasedRendererV2::Rule*>& children(); + QgsRuleBasedRendererV2::Rule* parent(); + + //! add child rule, take ownership, sets this as parent + void appendChild( QgsRuleBasedRendererV2::Rule* rule /Transfer/ ); + //! add child rule, take ownership, sets this as parent + void insertChild( int i, QgsRuleBasedRendererV2::Rule* rule /Transfer/ ); + //! delete child rule + void removeChild( QgsRuleBasedRendererV2::Rule* rule ); + //! delete child rule + void removeChildAt( int i ); + //! take child rule out, set parent as null + void takeChild( QgsRuleBasedRendererV2::Rule* rule ); + //! take child rule out, set parent as null + QgsRuleBasedRendererV2::Rule* takeChildAt( int i ); - //Rule& operator=( const Rule& other ); }; ///// @@ -453,6 +537,12 @@ class QgsRuleBasedRendererV2 : QgsFeatureRendererV2 virtual QList<QString> usedAttributes(); + virtual QString dump(); + + //! returns bitwise OR-ed capabilities of the renderer + //! \note added in 2.0 + virtual int capabilities(); + virtual QgsFeatureRendererV2* clone() /Factory/; virtual QgsSymbolV2List symbols(); @@ -463,6 +553,17 @@ class QgsRuleBasedRendererV2 : QgsFeatureRendererV2 //! return a list of symbology items for the legend virtual QgsLegendSymbologyList legendSymbologyItems( QSize iconSize ); + //! return whether the renderer will render a feature or not. + //! Must be called between startRender() and stopRender() calls. + //! @note added in 1.9 + virtual bool willRenderFeature( QgsFeature& feat ); + + //! return list of symbols used for rendering the feature. + //! For renderers that do not support MoreSymbolsPerFeature it is more efficient + //! to use symbolForFeature() + //! @note added in 1.9 + virtual QgsSymbolV2List symbolsForFeature( QgsFeature& feat ); + ///// diff --git a/src/core/symbology-ng/qgsrendererv2.cpp b/src/core/symbology-ng/qgsrendererv2.cpp index 7f09f47..be5ab2b 100644 --- a/src/core/symbology-ng/qgsrendererv2.cpp +++ b/src/core/symbology-ng/qgsrendererv2.cpp @@ -417,3 +417,11 @@ void QgsFeatureRendererV2::renderVertexMarkerPolygon( QPolygonF& pts, QList<QPol } } } + +QgsSymbolV2List QgsFeatureRendererV2::symbolsForFeature( QgsFeature& feat ) +{ + QgsSymbolV2List lst; + QgsSymbolV2* s = symbolForFeature( feat ); + if ( s ) lst.append( s ); + return lst; +} diff --git a/src/core/symbology-ng/qgsrendererv2.h b/src/core/symbology-ng/qgsrendererv2.h index 7b3b45b..968e8e8 100644 --- a/src/core/symbology-ng/qgsrendererv2.h +++ b/src/core/symbology-ng/qgsrendererv2.h @@ -81,7 +81,8 @@ class CORE_EXPORT QgsFeatureRendererV2 enum Capabilities { SymbolLevels = 1, // rendering with symbol levels (i.e. implements symbols(), symbolForFeature()) - RotationField = 1 << 1 // rotate symbols by attribute value + RotationField = 1 << 1, // rotate symbols by attribute value + MoreSymbolsPerFeature = 1 << 2 // may use more than one symbol to render a feature: symbolsForFeature() will return them }; //! returns bitwise OR-ed capabilities of the renderer @@ -117,7 +118,17 @@ class CORE_EXPORT QgsFeatureRendererV2 //! @note added in 1.9 virtual void setRotationField( QString fieldName ) { Q_UNUSED( fieldName ); } + //! return whether the renderer will render a feature or not. + //! Must be called between startRender() and stopRender() calls. + //! Default implementation uses symbolForFeature(). + //! @note added in 1.9 + virtual bool willRenderFeature( QgsFeature& feat ) { return symbolForFeature( feat ) != NULL; } + //! return list of symbols used for rendering the feature. + //! For renderers that do not support MoreSymbolsPerFeature it is more efficient + //! to use symbolForFeature() + //! @note added in 1.9 + virtual QgsSymbolV2List symbolsForFeature( QgsFeature& feat ); protected: QgsFeatureRendererV2( QString type ); diff --git a/src/core/symbology-ng/qgsrulebasedrendererv2.cpp b/src/core/symbology-ng/qgsrulebasedrendererv2.cpp index 68ee914..665ec9d 100644 --- a/src/core/symbology-ng/qgsrulebasedrendererv2.cpp +++ b/src/core/symbology-ng/qgsrulebasedrendererv2.cpp @@ -288,6 +288,38 @@ bool QgsRuleBasedRendererV2::Rule::renderFeature( QgsRuleBasedRendererV2::Featur return rendered; } +bool QgsRuleBasedRendererV2::Rule::willRenderFeature( QgsFeature& feat ) +{ + if ( !isFilterOK( feat ) ) + return false; + if ( mSymbol ) + return true; + + for ( QList<Rule*>::iterator it = mActiveChildren.begin(); it != mActiveChildren.end(); ++it ) + { + Rule* rule = *it; + if ( rule->willRenderFeature( feat ) ) + return true; + } + return false; +} + +QgsSymbolV2List QgsRuleBasedRendererV2::Rule::symbolsForFeature( QgsFeature& feat ) +{ + QgsSymbolV2List lst; + if ( !isFilterOK( feat ) ) + return lst; + if ( mSymbol ) + lst.append( mSymbol ); + + for ( QList<Rule*>::iterator it = mActiveChildren.begin(); it != mActiveChildren.end(); ++it ) + { + Rule* rule = *it; + lst += rule->symbolsForFeature( feat ); + } + return lst; +} + void QgsRuleBasedRendererV2::Rule::stopRender( QgsRenderContext& context ) { @@ -581,3 +613,13 @@ QString QgsRuleBasedRendererV2::dump() msg += mRootRule->dump(); return msg; } + +bool QgsRuleBasedRendererV2::willRenderFeature( QgsFeature& feat ) +{ + return mRootRule->willRenderFeature( feat ); +} + +QgsSymbolV2List QgsRuleBasedRendererV2::symbolsForFeature( QgsFeature& feat ) +{ + return mRootRule->symbolsForFeature( feat ); +} diff --git a/src/core/symbology-ng/qgsrulebasedrendererv2.h b/src/core/symbology-ng/qgsrulebasedrendererv2.h index d3cab4e..6a773ed 100644 --- a/src/core/symbology-ng/qgsrulebasedrendererv2.h +++ b/src/core/symbology-ng/qgsrulebasedrendererv2.h @@ -127,6 +127,14 @@ class CORE_EXPORT QgsRuleBasedRendererV2 : public QgsFeatureRendererV2 bool renderFeature( FeatureToRender& featToRender, QgsRenderContext& context, RenderQueue& renderQueue ); + //! only tell whether a feature will be rendered without actually rendering it + //! @note added in 1.9 + bool willRenderFeature( QgsFeature& feat ); + + //! tell which symbols will be used to render the feature + //! @note added in 1.9 + QgsSymbolV2List symbolsForFeature( QgsFeature& feat ); + void stopRender( QgsRenderContext& context ); static Rule* create( QDomElement& ruleElem, QgsSymbolV2Map& symbolMap ); @@ -204,6 +212,21 @@ class CORE_EXPORT QgsRuleBasedRendererV2 : public QgsFeatureRendererV2 //! for debugging virtual QString dump(); + //! return whether the renderer will render a feature or not. + //! Must be called between startRender() and stopRender() calls. + //! @note added in 1.9 + virtual bool willRenderFeature( QgsFeature& feat ); + + //! return list of symbols used for rendering the feature. + //! For renderers that do not support MoreSymbolsPerFeature it is more efficient + //! to use symbolForFeature() + //! @note added in 1.9 + virtual QgsSymbolV2List symbolsForFeature( QgsFeature& feat ); + + //! returns bitwise OR-ed capabilities of the renderer + //! \note added in 2.0 + virtual int capabilities() { return MoreSymbolsPerFeature; } + ///// Rule* rootRule() { return mRootRule; } diff --git a/tests/src/core/testqgsrulebasedrenderer.cpp b/tests/src/core/testqgsrulebasedrenderer.cpp index 206bc72..e6ca922 100644 --- a/tests/src/core/testqgsrulebasedrenderer.cpp +++ b/tests/src/core/testqgsrulebasedrenderer.cpp @@ -18,75 +18,128 @@ //header for class being tested #include <qgsrulebasedrendererv2.h> +#include <qgsapplication.h> +#include <qgssymbolv2.h> +#include <qgsvectorlayer.h> + #if QT_VERSION < 0x40701 // See http://hub.qgis.org/issues/4284 Q_DECLARE_METATYPE( QVariant ) #endif +typedef QgsRuleBasedRendererV2::Rule RRule; class TestQgsRuleBasedRenderer: public QObject { Q_OBJECT private slots: - void test_load_xml() - { - QDomDocument doc; - xml2domElement( "rulebasedrenderer_simple.xml", doc ); - QDomElement elem = doc.documentElement(); - - QgsRuleBasedRendererV2* r = static_cast<QgsRuleBasedRendererV2*>( QgsRuleBasedRendererV2::create( elem ) ); - QVERIFY( r ); - check_tree_valid( r->rootRule() ); - delete r; - } - - void test_load_invalid_xml() - { - QDomDocument doc; - xml2domElement( "rulebasedrenderer_invalid.xml", doc ); - QDomElement elem = doc.documentElement(); - - QgsRuleBasedRendererV2* r = static_cast<QgsRuleBasedRendererV2*>( QgsRuleBasedRendererV2::create( elem ) ); - QVERIFY( r == NULL ); - } - -private: - void xml2domElement( QString testFile, QDomDocument& doc ) - { - QString fileName = QString( TEST_DATA_DIR ) + QDir::separator() + testFile; - QFile f(fileName); - bool fileOpen = f.open(QIODevice::ReadOnly); - QVERIFY( fileOpen ); - - QString msg; - int line, col; - bool parse = doc.setContent( &f, &msg, &line, &col ); - QVERIFY( parse ); - } - - void check_tree_valid( QgsRuleBasedRendererV2::Rule* root ) - { - // root must always exist (although it does not have children) - QVERIFY( root ); - // and does not have a parent - QVERIFY( root->parent() == NULL ); - - foreach ( QgsRuleBasedRendererV2::Rule* node, root->children() ) + void initTestCase() + { + // we need memory provider, so make sure to load providers + QgsApplication::init(); + QgsApplication::initQgis(); + } + + void test_load_xml() + { + QDomDocument doc; + xml2domElement( "rulebasedrenderer_simple.xml", doc ); + QDomElement elem = doc.documentElement(); + + QgsRuleBasedRendererV2* r = static_cast<QgsRuleBasedRendererV2*>( QgsRuleBasedRendererV2::create( elem ) ); + QVERIFY( r ); + check_tree_valid( r->rootRule() ); + delete r; + } + + void test_load_invalid_xml() + { + QDomDocument doc; + xml2domElement( "rulebasedrenderer_invalid.xml", doc ); + QDomElement elem = doc.documentElement(); + + QgsRuleBasedRendererV2* r = static_cast<QgsRuleBasedRendererV2*>( QgsRuleBasedRendererV2::create( elem ) ); + QVERIFY( r == NULL ); + } + + void test_willRenderFeature_symbolsForFeature() + { + // prepare features + QgsVectorLayer* layer = new QgsVectorLayer( "point?field=fld:int", "x", "memory" ); + int idx = layer->fieldNameIndex( "fld" ); + QVERIFY( idx != -1 ); + QgsFeature f1; f1.addAttribute( idx, QVariant( 2 ) ); + QgsFeature f2; f2.addAttribute( idx, QVariant( 8 ) ); + QgsFeature f3; f3.addAttribute( idx, QVariant( 100 ) ); + + // prepare renderer + QgsSymbolV2* s1 = QgsSymbolV2::defaultSymbol( QGis::Point ); + QgsSymbolV2* s2 = QgsSymbolV2::defaultSymbol( QGis::Point ); + RRule* rootRule = new RRule( NULL ); + rootRule->appendChild( new RRule( s1, 0, 0, "fld >= 5 and fld <= 20" ) ); + rootRule->appendChild( new RRule( s2, 0, 0, "fld <= 10" ) ); + QgsRuleBasedRendererV2 r( rootRule ); + + QVERIFY( r.capabilities() & QgsFeatureRendererV2::MoreSymbolsPerFeature ); + + QgsRenderContext ctx; // dummy render context + r.startRender( ctx, layer ); + + // test willRenderFeature + QVERIFY( r.willRenderFeature( f1 ) == true ); + QVERIFY( r.willRenderFeature( f2 ) == true ); + QVERIFY( r.willRenderFeature( f3 ) == false ); + + // test symbolsForFeature + QgsSymbolV2List lst1 = r.symbolsForFeature( f1 ); + QVERIFY( lst1.count() == 1 ); + QgsSymbolV2List lst2 = r.symbolsForFeature( f2 ); + QVERIFY( lst2.count() == 2 ); + QgsSymbolV2List lst3 = r.symbolsForFeature( f3 ); + QVERIFY( lst3.count() == 0 ); + + r.stopRender( ctx ); + + delete layer; + } + + private: + void xml2domElement( QString testFile, QDomDocument& doc ) + { + QString fileName = QString( TEST_DATA_DIR ) + QDir::separator() + testFile; + QFile f( fileName ); + bool fileOpen = f.open( QIODevice::ReadOnly ); + QVERIFY( fileOpen ); + + QString msg; + int line, col; + bool parse = doc.setContent( &f, &msg, &line, &col ); + QVERIFY( parse ); + } + + void check_tree_valid( QgsRuleBasedRendererV2::Rule* root ) + { + // root must always exist (although it does not have children) + QVERIFY( root ); + // and does not have a parent + QVERIFY( root->parent() == NULL ); + + foreach( QgsRuleBasedRendererV2::Rule* node, root->children() ) check_non_root_rule( node ); - } - - void check_non_root_rule( QgsRuleBasedRendererV2::Rule* node ) - { - qDebug() << node->dump(); - // children must not be NULL - QVERIFY( node ); - // and must have a parent - QVERIFY( node->parent() ); - // check that all children are okay - foreach ( QgsRuleBasedRendererV2::Rule* child, node->children() ) + } + + void check_non_root_rule( QgsRuleBasedRendererV2::Rule* node ) + { + qDebug() << node->dump(); + // children must not be NULL + QVERIFY( node ); + // and must have a parent + QVERIFY( node->parent() ); + // check that all children are okay + foreach( QgsRuleBasedRendererV2::Rule* child, node->children() ) check_non_root_rule( child ); - } + } }; -- The Quantum GIS in Debian project _______________________________________________ Pkg-grass-devel mailing list Pkg-grass-devel@lists.alioth.debian.org http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-grass-devel