Skip to content

Commit

Permalink
Model/view-based tree of rules. Added basic unit test, GUI renderer test
Browse files Browse the repository at this point in the history
  • Loading branch information
wonder-sk committed Jan 21, 2012
1 parent 83ec810 commit 245e76d
Show file tree
Hide file tree
Showing 13 changed files with 556 additions and 539 deletions.
18 changes: 4 additions & 14 deletions python/core/symbology-ng-core.sip
Expand Up @@ -430,8 +430,10 @@ class QgsRuleBasedRendererV2 : QgsFeatureRendererV2

static QgsFeatureRendererV2* create( QDomElement& element ) /Factory/;

//! Constructor. Takes ownership of the defult symbol.
QgsRuleBasedRendererV2( QgsSymbolV2* defaultSymbol /Transfer/ = NULL );
//! Constructs the renderer from given tree of rules
QgsRuleBasedRendererV2( QgsRuleBasedRendererV2::Rule* root /Transfer/ );
//! Constructor for convenience. Creates a root rule and adds a default rule with symbol
QgsRuleBasedRendererV2( QgsSymbolV2* defaultSymbol /Transfer/ );

//! return symbol for current feature. Should not be used individually: there could be more symbols for a feature
virtual QgsSymbolV2* symbolForFeature( QgsFeature& feature );
Expand All @@ -456,18 +458,6 @@ class QgsRuleBasedRendererV2 : QgsFeatureRendererV2

/////

//! return the total number of rules
int ruleCount();
//! get reference to rule at index (valid indexes: 0...count-1)
QgsRuleBasedRendererV2::Rule* ruleAt( int index );
//! add rule to the end of the list of rules
void addRule( QgsRuleBasedRendererV2::Rule* rule );
//! insert rule to a specific position of the list of rules
void insertRule( int index, QgsRuleBasedRendererV2::Rule* rule );
//! modify the rule at a specific position of the list of rules
void updateRuleAt( int index, QgsRuleBasedRendererV2::Rule* rule );
//! remove the rule at the specified index
void removeRuleAt( int index );

//////

Expand Down
96 changes: 24 additions & 72 deletions src/core/symbology-ng/qgsrulebasedrendererv2.cpp
Expand Up @@ -29,7 +29,7 @@


QgsRuleBasedRendererV2::Rule::Rule( QgsSymbolV2* symbol, int scaleMinDenom, int scaleMaxDenom, QString filterExp, QString label, QString description )
: mSymbol( symbol ),
: mParent( NULL ), mSymbol( symbol ),
mScaleMinDenom( scaleMinDenom ), mScaleMaxDenom( scaleMaxDenom ),
mFilterExp( filterExp ), mLabel( label ), mDescription( description ),
mFilter( NULL )
Expand All @@ -41,6 +41,8 @@ QgsRuleBasedRendererV2::Rule::~Rule()
{
delete mSymbol;
delete mFilter;
qDeleteAll( mChildren );
// do NOT delete parent
}

void QgsRuleBasedRendererV2::Rule::initFilter()
Expand Down Expand Up @@ -145,9 +147,7 @@ QgsRuleBasedRendererV2::Rule* QgsRuleBasedRendererV2::Rule::clone() const
Rule* newrule = new Rule( sym, mScaleMinDenom, mScaleMaxDenom, mFilterExp, mLabel, mDescription );
// clone children
foreach( Rule* rule, mChildren )
{
newrule->mChildren.append( rule->clone() );
}
newrule->appendChild( rule->clone() );
return newrule;
}

Expand Down Expand Up @@ -320,7 +320,7 @@ QgsRuleBasedRendererV2::Rule* QgsRuleBasedRendererV2::Rule::create( QDomElement&
{
Rule* childRule = create( childRuleElem, symbolMap );
if ( childRule )
rule->mChildren.append( childRule );
rule->appendChild( childRule );
else
QgsDebugMsg( "failed to init a child rule!" );
childRuleElem = childRuleElem.nextSiblingElement( "rule" );
Expand All @@ -332,16 +332,16 @@ QgsRuleBasedRendererV2::Rule* QgsRuleBasedRendererV2::Rule::create( QDomElement&

/////////////////////

QgsRuleBasedRendererV2::QgsRuleBasedRendererV2( QgsRuleBasedRendererV2::Rule* root )
: QgsFeatureRendererV2( "RuleRenderer" ), mRootRule( root )
{
}

QgsRuleBasedRendererV2::QgsRuleBasedRendererV2( QgsSymbolV2* defaultSymbol )
: QgsFeatureRendererV2( "RuleRenderer" )
{
mRootRule = new Rule( NULL );

if ( defaultSymbol )
{
// add the default rule
mRootRule->children().append( new Rule( defaultSymbol ) );
}
mRootRule = new Rule( NULL ); // root has no symbol, no filter etc - just a container
mRootRule->appendChild( new Rule( defaultSymbol ) );
}

QgsRuleBasedRendererV2::~QgsRuleBasedRendererV2()
Expand Down Expand Up @@ -454,9 +454,7 @@ QList<QString> QgsRuleBasedRendererV2::usedAttributes()

QgsFeatureRendererV2* QgsRuleBasedRendererV2::clone()
{
QgsRuleBasedRendererV2* r = new QgsRuleBasedRendererV2();
delete r->mRootRule;
r->mRootRule = mRootRule->clone();
QgsRuleBasedRendererV2* r = new QgsRuleBasedRendererV2( mRootRule->clone() );

r->setUsingSymbolLevels( usingSymbolLevels() );
setUsingSymbolLevels( usingSymbolLevels() );
Expand All @@ -477,8 +475,8 @@ QDomElement QgsRuleBasedRendererV2::save( QDomDocument& doc )

QgsSymbolV2Map symbols;

QDomElement rulesElem = doc.createElement( "rules" );
rulesElem.appendChild( mRootRule->save( doc, symbols ) );
QDomElement rulesElem = mRootRule->save( doc, symbols );
rulesElem.setTagName( "rules" ); // instead of just "rule"
rendererElem.appendChild( rulesElem );

QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
Expand Down Expand Up @@ -518,14 +516,11 @@ QgsFeatureRendererV2* QgsRuleBasedRendererV2::create( QDomElement& element )

QDomElement rulesElem = element.firstChildElement( "rules" );

QDomElement rootRuleElem = rulesElem.firstChildElement( "rule" );
Rule* root = Rule::create( rootRuleElem, symbolMap );
Rule* root = Rule::create( rulesElem, symbolMap );
if ( root == NULL )
return NULL;

QgsRuleBasedRendererV2* r = new QgsRuleBasedRendererV2();
delete r->mRootRule;
r->mRootRule = root;
QgsRuleBasedRendererV2* r = new QgsRuleBasedRendererV2( root );

// delete symbols if there are any more
QgsSymbolLayerV2Utils::clearSymbolMap( symbolMap );
Expand All @@ -534,48 +529,10 @@ QgsFeatureRendererV2* QgsRuleBasedRendererV2::create( QDomElement& element )
}


int QgsRuleBasedRendererV2::ruleCount()
{
return mRootRule->children().count();
}

QgsRuleBasedRendererV2::Rule* QgsRuleBasedRendererV2::ruleAt( int index )
{
return mRootRule->children()[index];
}

void QgsRuleBasedRendererV2::addRule( QgsRuleBasedRendererV2::Rule* rule )
{
mRootRule->children().append( rule );
}

void QgsRuleBasedRendererV2::insertRule( int index, QgsRuleBasedRendererV2::Rule* rule )
{
mRootRule->children().insert( index, rule );
}

void QgsRuleBasedRendererV2::updateRuleAt( int index, QgsRuleBasedRendererV2::Rule* rule )
{
RuleList& rules = mRootRule->children();
delete rules[index]; // delete previous
rules[index] = rule;
}

void QgsRuleBasedRendererV2::removeRuleAt( int index )
{
delete mRootRule->children().takeAt( index );
}

void QgsRuleBasedRendererV2::swapRules( int index1, int index2 )
{
mRootRule->children().swap( index1, index2 );
}


#include "qgscategorizedsymbolrendererv2.h"
#include "qgsgraduatedsymbolrendererv2.h"

QgsRuleBasedRendererV2::RuleList QgsRuleBasedRendererV2::refineRuleCategories( QgsRuleBasedRendererV2::Rule* initialRule, QgsCategorizedSymbolRendererV2* r )
void QgsRuleBasedRendererV2::refineRuleCategories( QgsRuleBasedRendererV2::Rule* initialRule, QgsCategorizedSymbolRendererV2* r )
{
RuleList rules;
foreach( const QgsRendererCategoryV2& cat, r->categories() )
Expand All @@ -588,14 +545,12 @@ QgsRuleBasedRendererV2::RuleList QgsRuleBasedRendererV2::refineRuleCategories( Q
filter = newfilter;
else
filter = QString( "(%1) AND (%2)" ).arg( filter ).arg( newfilter );
rules.append( new Rule( cat.symbol()->clone(), initialRule->scaleMinDenom(), initialRule->scaleMaxDenom(), filter, label, description ) );
initialRule->appendChild( new Rule( cat.symbol()->clone(), initialRule->scaleMinDenom(), initialRule->scaleMaxDenom(), filter, label, description ) );
}
return rules;
}

QgsRuleBasedRendererV2::RuleList QgsRuleBasedRendererV2::refineRuleRanges( QgsRuleBasedRendererV2::Rule* initialRule, QgsGraduatedSymbolRendererV2* r )
void QgsRuleBasedRendererV2::refineRuleRanges( QgsRuleBasedRendererV2::Rule* initialRule, QgsGraduatedSymbolRendererV2* r )
{
RuleList rules;
foreach( const QgsRendererRangeV2& rng, r->ranges() )
{
QString newfilter = QString( "%1 >= '%2' AND %1 <= '%3'" ).arg( r->classAttribute() ).arg( rng.lowerValue() ).arg( rng.upperValue() );
Expand All @@ -606,15 +561,13 @@ QgsRuleBasedRendererV2::RuleList QgsRuleBasedRendererV2::refineRuleRanges( QgsRu
filter = newfilter;
else
filter = QString( "(%1) AND (%2)" ).arg( filter ).arg( newfilter );
rules.append( new Rule( rng.symbol()->clone(), initialRule->scaleMinDenom(), initialRule->scaleMaxDenom(), filter, label, description ) );
initialRule->appendChild( new Rule( rng.symbol()->clone(), initialRule->scaleMinDenom(), initialRule->scaleMaxDenom(), filter, label, description ) );
}
return rules;
}

QgsRuleBasedRendererV2::RuleList QgsRuleBasedRendererV2::refineRuleScales( QgsRuleBasedRendererV2::Rule* initialRule, QList<int> scales )
void QgsRuleBasedRendererV2::refineRuleScales( QgsRuleBasedRendererV2::Rule* initialRule, QList<int> scales )
{
qSort( scales ); // make sure the scales are in ascending order
RuleList rules;
int oldScale = initialRule->scaleMinDenom();
int maxDenom = initialRule->scaleMaxDenom();
QString filter = initialRule->filterExpression();
Expand All @@ -627,12 +580,11 @@ QgsRuleBasedRendererV2::RuleList QgsRuleBasedRendererV2::refineRuleScales( QgsRu
continue; // jump over the first scales out of the interval
if ( maxDenom != 0 && maxDenom <= scale )
break; // ignore the latter scales out of the interval
rules.append( new Rule( symbol->clone(), oldScale, scale, filter, label, description ) );
initialRule->appendChild( new Rule( symbol->clone(), oldScale, scale, filter, label, description ) );
oldScale = scale;
}
// last rule
rules.append( new Rule( symbol->clone(), oldScale, maxDenom, filter, label, description ) );
return rules;
initialRule->appendChild( new Rule( symbol->clone(), oldScale, maxDenom, filter, label, description ) );
}

QString QgsRuleBasedRendererV2::dump()
Expand Down
40 changes: 18 additions & 22 deletions src/core/symbology-ng/qgsrulebasedrendererv2.h
Expand Up @@ -119,11 +119,22 @@ class CORE_EXPORT QgsRuleBasedRendererV2 : public QgsFeatureRendererV2
static Rule* create( QDomElement& ruleElem, QgsSymbolV2Map& symbolMap );

RuleList& children() { return mChildren; }
Rule* parent() { return mParent; }

//! add child rule, take ownership, sets this as parent
void appendChild( Rule* rule ) { mChildren.append( rule ); rule->mParent = this; }
//! add child rule, take ownership, sets this as parent
void insertChild( int i, Rule* rule ) { mChildren.insert( i, rule ); rule->mParent = this; }
//! delete child rule
void removeChild( Rule* rule ) { mChildren.removeAll( rule ); delete rule; }
//! take child rule out, set parent as null
void takeChild( Rule* rule ) { mChildren.removeAll( rule ); rule->mParent = NULL; }

protected:

void initFilter();

Rule* mParent; // parent rule (NULL only for root rule)
QgsSymbolV2* mSymbol;
int mScaleMinDenom, mScaleMaxDenom;
QString mFilterExp, mLabel, mDescription;
Expand All @@ -141,8 +152,10 @@ class CORE_EXPORT QgsRuleBasedRendererV2 : public QgsFeatureRendererV2

static QgsFeatureRendererV2* create( QDomElement& element );

//! Constructor. Adds default rule if the symbol is not null (and takes ownership of it)
QgsRuleBasedRendererV2( QgsSymbolV2* defaultSymbol = NULL );
//! Constructs the renderer from given tree of rules (takes ownership)
QgsRuleBasedRendererV2( QgsRuleBasedRendererV2::Rule* root );
//! Constructor for convenience. Creates a root rule and adds a default rule with symbol (takes ownership)
QgsRuleBasedRendererV2( QgsSymbolV2* defaultSymbol );

~QgsRuleBasedRendererV2();

Expand Down Expand Up @@ -178,31 +191,14 @@ class CORE_EXPORT QgsRuleBasedRendererV2 : public QgsFeatureRendererV2

Rule* rootRule() { return mRootRule; }


//! return the total number of rules
int ruleCount();
//! get reference to rule at index (valid indexes: 0...count-1)
Rule* ruleAt( int index );
//! add rule to the end of the list of rules. takes ownership
void addRule( Rule* rule );
//! insert rule to a specific position of the list of rules. takes ownership
void insertRule( int index, Rule* rule );
//! modify the rule at a specific position of the list of rules. takes ownership
void updateRuleAt( int index, Rule* rule );
//! remove the rule at the specified index
void removeRuleAt( int index );
//! swap the two rules specified by the indices
void swapRules( int index1, int index2 );


//////

//! take a rule and create a list of new rules based on the categories from categorized symbol renderer
static RuleList refineRuleCategories( Rule* initialRule, QgsCategorizedSymbolRendererV2* r );
static void refineRuleCategories( Rule* initialRule, QgsCategorizedSymbolRendererV2* r );
//! take a rule and create a list of new rules based on the ranges from graduated symbol renderer
static RuleList refineRuleRanges( Rule* initialRule, QgsGraduatedSymbolRendererV2* r );
static void refineRuleRanges( Rule* initialRule, QgsGraduatedSymbolRendererV2* r );
//! take a rule and create a list of new rules with intervals of scales given by the passed scale denominators
static RuleList refineRuleScales( Rule* initialRule, QList<int> scales );
static void refineRuleScales( Rule* initialRule, QList<int> scales );

protected:
//! the root node with hierarchical list of rules
Expand Down

0 comments on commit 245e76d

Please sign in to comment.