Skip to content

Commit

Permalink
creation of a convertToRuleBasedRenderer() function
Browse files Browse the repository at this point in the history
  • Loading branch information
leyan committed Sep 9, 2014
1 parent 5c5deec commit 8517249
Show file tree
Hide file tree
Showing 11 changed files with 278 additions and 9 deletions.
92 changes: 92 additions & 0 deletions src/core/symbology-ng/qgscategorizedsymbolrendererv2.cpp
Expand Up @@ -19,6 +19,7 @@
#include "qgssymbolv2.h"
#include "qgssymbollayerv2utils.h"
#include "qgsvectorcolorrampv2.h"
#include "qgsrulebasedrendererv2.h"

#include "qgsfeature.h"
#include "qgsvectorlayer.h"
Expand Down Expand Up @@ -758,3 +759,94 @@ void QgsCategorizedSymbolRendererV2::checkLegendSymbolItem( QString key, bool st
}

QgsMarkerSymbolV2 QgsCategorizedSymbolRendererV2::sSkipRender;

QgsRuleBasedRendererV2* QgsCategorizedSymbolRendererV2::convertToRuleBasedRenderer()
{
QgsRuleBasedRendererV2::Rule* rootrule = new QgsRuleBasedRendererV2::Rule( NULL );

QString expression;
QString sizeExpression;
QString value;
for ( int i = 0; i < mCategories.size();++i )
{
QgsRuleBasedRendererV2::Rule* rule = new QgsRuleBasedRendererV2::Rule( NULL );

rule->setLabel( mCategories[i].label() );

//We first define the rule corresponding to the category
//If the value is a number, we can use it directly, otherwise we need to quote it in the rule
if ( QVariant( mCategories[i].value() ).convert( QVariant::Double ) )
{
value = mCategories[i].value().toString();
}
else
{
value = "'" + mCategories[i].value().toString() + "'";
}

//An empty category is equivalent to the ELSE keyword
if ( value == "''" )
expression = "ELSE";
else
expression = classAttribute() + " = " + value;
rule->setFilterExpression( expression );

//Then we construct an equivalent symbol.
//Ideally we could simply copy the symbol, but the categorized renderer allows a separate interface to specify
//data dependent area and rotation, so we need to convert these to obtain the same rendering

QgsSymbolV2* origSymbol = mCategories[i].symbol()->clone();

switch ( origSymbol->type() )
{
case QgsSymbolV2::Marker:
for ( int j = 0; j < origSymbol->symbolLayerCount();++j )
{
QgsMarkerSymbolLayerV2* msl = static_cast<QgsMarkerSymbolLayerV2*>( origSymbol->symbolLayer( j ) );
if ( mSizeScale.data() )
{
sizeExpression = QString( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField() );
msl->setDataDefinedProperty( "size", sizeExpression );
}
if ( mRotation.data() )
{
msl->setDataDefinedProperty( "angle", rotationField() );
}
}
break;
case QgsSymbolV2::Line:
if ( mSizeScale.data() )
{
for ( int j = 0; j < origSymbol->symbolLayerCount();++j )
{
if ( origSymbol->symbolLayer( j )->layerType() == "SimpleLine" )
{
QgsLineSymbolLayerV2* lsl = static_cast<QgsLineSymbolLayerV2*>( origSymbol->symbolLayer( j ) );
sizeExpression = QString( "%1*(%2)" ).arg( lsl->width() ).arg( sizeScaleField() );
lsl->setDataDefinedProperty( "width", sizeExpression );
}
if ( origSymbol->symbolLayer( j )->layerType() == "MarkerLine" )
{
QgsSymbolV2* marker = origSymbol->symbolLayer( j )->subSymbol();
for ( int k = 0; k < marker->symbolLayerCount();++k )
{
QgsMarkerSymbolLayerV2* msl = static_cast<QgsMarkerSymbolLayerV2*>( marker->symbolLayer( k ) );
sizeExpression = QString( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField() );
msl->setDataDefinedProperty( "size", sizeExpression );
}
}
}
}
break;
default:
break;
}

rule->setSymbol( origSymbol );

rootrule->appendChild( rule );
}

return new QgsRuleBasedRendererV2( rootrule );

}
6 changes: 6 additions & 0 deletions src/core/symbology-ng/qgscategorizedsymbolrendererv2.h
Expand Up @@ -24,6 +24,7 @@

class QgsVectorColorRampV2;
class QgsVectorLayer;
class QgsRuleBasedRendererV2;

/* \brief categorized renderer */
class CORE_EXPORT QgsRendererCategoryV2
Expand Down Expand Up @@ -165,6 +166,7 @@ class CORE_EXPORT QgsCategorizedSymbolRendererV2 : public QgsFeatureRendererV2
//! @note added in 2.0
QgsSymbolV2::ScaleMethod scaleMethod() const { return mScaleMethod; }


//! items of symbology items in legend should be checkable
// @note added in 2.5
virtual bool legendSymbolItemsCheckable() const;
Expand All @@ -181,6 +183,10 @@ class CORE_EXPORT QgsCategorizedSymbolRendererV2 : public QgsFeatureRendererV2
//! @note added in 2.6
virtual QString legendClassificationAttribute() const { return classAttribute(); }

//! convert the renderer to a rule based renderer with equivalent rules
//! @note added in 2.5
virtual QgsRuleBasedRendererV2* convertToRuleBasedRenderer();

protected:
QString mAttrName;
QgsCategoryList mCategories;
Expand Down
83 changes: 83 additions & 0 deletions src/core/symbology-ng/qgsgraduatedsymbolrendererv2.cpp
Expand Up @@ -17,6 +17,7 @@
#include "qgssymbolv2.h"
#include "qgssymbollayerv2utils.h"
#include "qgsvectorcolorrampv2.h"
#include "qgsrulebasedrendererv2.h"

#include "qgsfeature.h"
#include "qgsvectorlayer.h"
Expand Down Expand Up @@ -1311,3 +1312,85 @@ void QgsGraduatedSymbolRendererV2::sortByLabel( Qt::SortOrder order )
}
}

QgsRuleBasedRendererV2* QgsGraduatedSymbolRendererV2::convertToRuleBasedRenderer()
{
QgsRuleBasedRendererV2::Rule* rootrule = new QgsRuleBasedRendererV2::Rule( NULL );

QString sizeExpression;
QString expression;
for ( int i = 0; i < mRanges.size();++i )
{
QgsRuleBasedRendererV2::Rule* rule = new QgsRuleBasedRendererV2::Rule( NULL );
rule->setSymbol( mRanges[i].symbol() );
rule->setLabel( mRanges[i].label() );
if ( i == 0 )//The lower boundary of the first range is included, while it is excluded for the others
{
expression = classAttribute() + " >= " + QString::number( mRanges[i].lowerValue(), 'f' ) + " AND " + \
classAttribute() + " <= " + QString::number( mRanges[i].upperValue(), 'f' );
}
else
{
expression = classAttribute() + " > " + QString::number( mRanges[i].lowerValue(), 'f' ) + " AND " + \
classAttribute() + " <= " + QString::number( mRanges[i].upperValue(), 'f' );
}
rule->setFilterExpression( expression );

//Then we construct an equivalent symbol.
//Ideally we could simply copy the symbol, but the graduated renderer allows a separate interface to specify
//data dependent area and rotation, so we need to convert these to obtain the same rendering

QgsSymbolV2* origSymbol = mRanges[i].symbol()->clone();

switch ( origSymbol->type() )
{
case QgsSymbolV2::Marker:
for ( int j = 0; j < origSymbol->symbolLayerCount();++j )
{
QgsMarkerSymbolLayerV2* msl = static_cast<QgsMarkerSymbolLayerV2*>( origSymbol->symbolLayer( j ) );
if ( mSizeScale.data() )
{
sizeExpression = QString( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField() );
msl->setDataDefinedProperty( "size", sizeExpression );
if ( mRotation.data() )
{
msl->setDataDefinedProperty( "angle", rotationField() );
}
}
}
break;
case QgsSymbolV2::Line:
if ( mSizeScale.data() )
{
for ( int j = 0; j < origSymbol->symbolLayerCount();++j )
{
if ( origSymbol->symbolLayer( j )->layerType() == "SimpleLine" )
{
QgsLineSymbolLayerV2* lsl = static_cast<QgsLineSymbolLayerV2*>( origSymbol->symbolLayer( j ) );
sizeExpression = QString( "%1*(%2)" ).arg( lsl->width() ).arg( sizeScaleField() );
lsl->setDataDefinedProperty( "width", sizeExpression );
}
if ( origSymbol->symbolLayer( j )->layerType() == "MarkerLine" )
{
QgsSymbolV2* marker = origSymbol->symbolLayer( j )->subSymbol();
for ( int k = 0; k < marker->symbolLayerCount();++k )
{
QgsMarkerSymbolLayerV2* msl = static_cast<QgsMarkerSymbolLayerV2*>( marker->symbolLayer( k ) );
sizeExpression = QString( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField() );
msl->setDataDefinedProperty( "size", sizeExpression );
}
}
}
}
break;
default:
break;
}

rule->setSymbol( origSymbol );

rootrule->appendChild( rule );
}

return new QgsRuleBasedRendererV2( rootrule );

}
5 changes: 5 additions & 0 deletions src/core/symbology-ng/qgsgraduatedsymbolrendererv2.h
Expand Up @@ -64,6 +64,7 @@ typedef QList<QgsRendererRangeV2> QgsRangeList;

class QgsVectorLayer;
class QgsVectorColorRampV2;
class QgsRuleBasedRenderedV2;

class CORE_EXPORT QgsGraduatedSymbolRendererV2 : public QgsFeatureRendererV2
{
Expand Down Expand Up @@ -202,6 +203,10 @@ class CORE_EXPORT QgsGraduatedSymbolRendererV2 : public QgsFeatureRendererV2
//! @note added in 2.6
virtual QString legendClassificationAttribute() const { return classAttribute(); }

//! convert the renderer to a rule based renderer with equivalent rules
//! @note added in 2.5
virtual QgsRuleBasedRendererV2* convertToRuleBasedRenderer();

protected:
QString mAttrName;
QgsRangeList mRanges;
Expand Down
6 changes: 6 additions & 0 deletions src/core/symbology-ng/qgsrendererv2.cpp
Expand Up @@ -16,6 +16,7 @@
#include "qgsrendererv2.h"
#include "qgssymbolv2.h"
#include "qgssymbollayerv2utils.h"
#include "qgsrulebasedrendererv2.h"

#include "qgssinglesymbolrendererv2.h" // for default renderer

Expand Down Expand Up @@ -585,3 +586,8 @@ QgsSymbolV2List QgsFeatureRendererV2::symbolsForFeature( QgsFeature& feat )
if ( s ) lst.append( s );
return lst;
}

QgsRuleBasedRendererV2* QgsFeatureRendererV2::convertToRuleBasedRenderer()
{
return 0;
}
5 changes: 5 additions & 0 deletions src/core/symbology-ng/qgsrendererv2.h
Expand Up @@ -31,6 +31,7 @@ class QgsRenderContext;
class QgsFeature;
class QgsFields;
class QgsVectorLayer;
class QgsRuleBasedRendererV2;

typedef QMap<QString, QString> QgsStringMap;

Expand Down Expand Up @@ -201,6 +202,10 @@ class CORE_EXPORT QgsFeatureRendererV2
//! @note added in 1.9
virtual QgsSymbolV2List symbolsForFeature( QgsFeature& feat );

//! convert the renderer to a rule based renderer with equivalent rules, if possible
//! @note added in 2.5
virtual QgsRuleBasedRendererV2* convertToRuleBasedRenderer();

protected:
QgsFeatureRendererV2( QString type );

Expand Down
5 changes: 5 additions & 0 deletions src/core/symbology-ng/qgsrulebasedrendererv2.cpp
Expand Up @@ -1043,3 +1043,8 @@ QgsSymbolV2List QgsRuleBasedRendererV2::symbolsForFeature( QgsFeature& feat )
{
return mRootRule->symbolsForFeature( feat );
}

QgsRuleBasedRendererV2* QgsRuleBasedRendererV2::convertToRuleBasedRenderer()
{
return this ;
}
4 changes: 4 additions & 0 deletions src/core/symbology-ng/qgsrulebasedrendererv2.h
Expand Up @@ -293,6 +293,10 @@ class CORE_EXPORT QgsRuleBasedRendererV2 : public QgsFeatureRendererV2
//! take a rule and create a list of new rules with intervals of scales given by the passed scale denominators
static void refineRuleScales( Rule* initialRule, QList<int> scales );

//! convert the renderer to a rule based renderer with equivalent rules, if possible
//! @note added in 2.5
virtual QgsRuleBasedRendererV2* convertToRuleBasedRenderer();

protected:
//! the root node with hierarchical list of rules
Rule* mRootRule;
Expand Down
58 changes: 58 additions & 0 deletions src/core/symbology-ng/qgssinglesymbolrendererv2.cpp
Expand Up @@ -23,6 +23,7 @@
#include "qgsvectorlayer.h"
#include "qgssymbollayerv2.h"
#include "qgsogcutils.h"
#include "qgsrulebasedrendererv2.h"

#include <QDomDocument>
#include <QDomElement>
Expand Down Expand Up @@ -373,3 +374,60 @@ QgsLegendSymbolListV2 QgsSingleSymbolRendererV2::legendSymbolItemsV2() const
lst << QgsLegendSymbolItemV2( mSymbol.data(), QString(), 0 );
return lst;
}

QgsRuleBasedRendererV2* QgsSingleSymbolRendererV2::convertToRuleBasedRenderer()
{
//We construct an equivalent symbol for the rule based renderer.
//Ideally we could simply copy the symbol, but the single symbol renderer allows a separate interface to specify
//data dependent area and rotation, so we need to convert these to obtain the same rendering

QString sizeExpression;
QgsSymbolV2* origSymbol = symbol()->clone();

switch ( origSymbol->type() )
{
case QgsSymbolV2::Marker:
for ( int j = 0; j < origSymbol->symbolLayerCount();++j )
{
QgsMarkerSymbolLayerV2* msl = static_cast<QgsMarkerSymbolLayerV2*>( origSymbol->symbolLayer( j ) );
if ( mSizeScale.data() )
{
sizeExpression = QString( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField() );
msl->setDataDefinedProperty( "size", sizeExpression );
}
if ( mRotation.data() )
{
msl->setDataDefinedProperty( "angle", rotationField() );
}
}
break;
case QgsSymbolV2::Line:
if ( mSizeScale.data() )
{
for ( int j = 0; j < origSymbol->symbolLayerCount();++j )
{
if ( origSymbol->symbolLayer( j )->layerType() == "SimpleLine" )
{
QgsLineSymbolLayerV2* lsl = static_cast<QgsLineSymbolLayerV2*>( origSymbol->symbolLayer( j ) );
sizeExpression = QString( "%1*(%2)" ).arg( lsl->width() ).arg( sizeScaleField() );
lsl->setDataDefinedProperty( "width", sizeExpression );
}
if ( origSymbol->symbolLayer( j )->layerType() == "MarkerLine" )
{
QgsSymbolV2* marker = origSymbol->symbolLayer( j )->subSymbol();
for ( int k = 0; k < marker->symbolLayerCount();++k )
{
QgsMarkerSymbolLayerV2* msl = static_cast<QgsMarkerSymbolLayerV2*>( marker->symbolLayer( k ) );
sizeExpression = QString( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField() );
msl->setDataDefinedProperty( "size", sizeExpression );
}
}
}
}
break;
default:
break;
}

return new QgsRuleBasedRendererV2( origSymbol );
}
8 changes: 8 additions & 0 deletions src/core/symbology-ng/qgssinglesymbolrendererv2.h
Expand Up @@ -21,6 +21,8 @@
#include "qgsexpression.h"
#include <QScopedPointer>

class QgsRuleBasedRenderedV2;

class CORE_EXPORT QgsSingleSymbolRendererV2 : public QgsFeatureRendererV2
{
public:
Expand Down Expand Up @@ -82,10 +84,16 @@ class CORE_EXPORT QgsSingleSymbolRendererV2 : public QgsFeatureRendererV2
//! @note not available in python bindings
virtual QgsLegendSymbolList legendSymbolItems( double scaleDenominator = -1, QString rule = QString() );


//! Return a list of symbology items for the legend. Better choice than legendSymbolItems().
//! @note added in 2.6
virtual QgsLegendSymbolListV2 legendSymbolItemsV2() const;

//! convert the renderer to a rule based renderer with equivalent rules
//! @note added in 2.5
virtual QgsRuleBasedRendererV2* convertToRuleBasedRenderer();


protected:
QScopedPointer<QgsSymbolV2> mSymbol;
QScopedPointer<QgsExpression> mRotation;
Expand Down

0 comments on commit 8517249

Please sign in to comment.