QGIS API Documentation  2.14.11-Essen
qgsrulebasedlabeling.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrulebasedlabeling.cpp
3  ---------------------
4  begin : September 2015
5  copyright : (C) 2015 by Martin Dobias
6  email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 #include "qgsrulebasedlabeling.h"
16 
17 
19  : QgsVectorLayerLabelProvider( layer, withFeatureLoop )
20  , mRules( rules )
21 {
23 }
24 
26 {
27  // sub-providers owned by labeling engine
28 }
29 
31 {
32  return new QgsVectorLayerLabelProvider( layer, withFeatureLoop, settings );
33 }
34 
35 bool QgsRuleBasedLabelProvider::prepare( const QgsRenderContext& context, QStringList& attributeNames )
36 {
37  Q_FOREACH ( QgsVectorLayerLabelProvider* provider, mSubProviders )
38  provider->setEngine( mEngine );
39 
40  // populate sub-providers
41  mRules.rootRule()->prepare( context, attributeNames, mSubProviders );
42  return true;
43 }
44 
46 {
47  // will register the feature to relevant sub-providers
48  mRules.rootRule()->registerFeature( feature, context, mSubProviders, obstacleGeometry );
49 }
50 
52 {
54  Q_FOREACH ( QgsVectorLayerLabelProvider* subprovider, mSubProviders )
55  lst << subprovider;
56  return lst;
57 }
58 
59 
61 
62 QgsRuleBasedLabeling::Rule::Rule( QgsPalLayerSettings* settings, int scaleMinDenom, int scaleMaxDenom, const QString& filterExp, const QString& description, bool elseRule )
63  : mParent( nullptr ), mSettings( settings )
64  , mScaleMinDenom( scaleMinDenom ), mScaleMaxDenom( scaleMaxDenom )
65  , mFilterExp( filterExp ), mDescription( description )
66  , mElseRule( elseRule )
67  , mIsActive( true )
68  , mFilter( nullptr )
69 {
70  initFilter();
71 }
72 
74 {
75  delete mSettings;
76  delete mFilter;
77  qDeleteAll( mChildren );
78  // do NOT delete parent
79 }
80 
82 {
83  if ( mSettings == settings )
84  return;
85 
86  delete mSettings;
88 }
89 
91 {
92  if ( mElseRule || mFilterExp.compare( "ELSE", Qt::CaseInsensitive ) == 0 )
93  {
94  mElseRule = true;
95  mFilter = nullptr;
96  }
97  else if ( !mFilterExp.isEmpty() )
98  {
99  delete mFilter;
101  }
102  else
103  {
104  mFilter = nullptr;
105  }
106 }
107 
109 {
110  mElseRules.clear();
111  Q_FOREACH ( Rule* rule, mChildren )
112  {
113  if ( rule->isElse() )
114  mElseRules << rule;
115  }
116 }
117 
118 
120 {
121  mChildren.append( rule );
122  rule->mParent = this;
123  updateElseRules();
124 }
125 
127 {
128  mChildren.insert( i, rule );
129  rule->mParent = this;
130  updateElseRules();
131 }
132 
134 {
135  delete mChildren.at( i );
136  mChildren.removeAt( i );
137  updateElseRules();
138 }
139 
141 {
144  newrule->setActive( mIsActive );
145  // clone children
146  Q_FOREACH ( Rule* rule, mChildren )
147  newrule->appendChild( rule->clone() );
148  return newrule;
149 }
150 
152 {
153  QgsPalLayerSettings* settings = nullptr;
154  QDomElement settingsElem = ruleElem.firstChildElement( "settings" );
155  if ( !settingsElem.isNull() )
156  {
157  settings = new QgsPalLayerSettings;
158  settings->readXml( settingsElem );
159  }
160 
161  QString filterExp = ruleElem.attribute( "filter" );
162  QString description = ruleElem.attribute( "description" );
163  int scaleMinDenom = ruleElem.attribute( "scalemindenom", "0" ).toInt();
164  int scaleMaxDenom = ruleElem.attribute( "scalemaxdenom", "0" ).toInt();
165  //QString ruleKey = ruleElem.attribute( "key" );
166  Rule* rule = new Rule( settings, scaleMinDenom, scaleMaxDenom, filterExp, description );
167 
168  //if ( !ruleKey.isEmpty() )
169  // rule->mRuleKey = ruleKey;
170 
171  rule->setActive( ruleElem.attribute( "active", "1" ).toInt() );
172 
173  QDomElement childRuleElem = ruleElem.firstChildElement( "rule" );
174  while ( !childRuleElem.isNull() )
175  {
176  Rule* childRule = create( childRuleElem );
177  if ( childRule )
178  {
179  rule->appendChild( childRule );
180  }
181  else
182  {
183  //QgsDebugMsg( "failed to init a child rule!" );
184  }
185  childRuleElem = childRuleElem.nextSiblingElement( "rule" );
186  }
187 
188  return rule;
189 }
190 
192 {
193  QDomElement ruleElem = doc.createElement( "rule" );
194 
195  if ( mSettings )
196  {
197  ruleElem.appendChild( mSettings->writeXml( doc ) );
198  }
199  if ( !mFilterExp.isEmpty() )
200  ruleElem.setAttribute( "filter", mFilterExp );
201  if ( mScaleMinDenom != 0 )
202  ruleElem.setAttribute( "scalemindenom", mScaleMinDenom );
203  if ( mScaleMaxDenom != 0 )
204  ruleElem.setAttribute( "scalemaxdenom", mScaleMaxDenom );
205  if ( !mDescription.isEmpty() )
206  ruleElem.setAttribute( "description", mDescription );
207  if ( !mIsActive )
208  ruleElem.setAttribute( "active", 0 );
209  //ruleElem.setAttribute( "key", mRuleKey );
210 
211  for ( RuleList::const_iterator it = mChildren.constBegin(); it != mChildren.constEnd(); ++it )
212  {
213  Rule* rule = *it;
214  ruleElem.appendChild( rule->save( doc ) );
215  }
216  return ruleElem;
217 }
218 
220 {
221  if ( mSettings )
222  {
223  // add provider!
224  QgsVectorLayerLabelProvider *p = provider->createProvider( layer, false, mSettings );
225  delete subProviders.value( this, nullptr );
226  subProviders[this] = p;
227  }
228 
229  // call recursively
230  Q_FOREACH ( Rule* rule, mChildren )
231  {
232  rule->createSubProviders( layer, subProviders, provider );
233  }
234 }
235 
237 {
238  if ( mSettings )
239  {
240  QgsVectorLayerLabelProvider* p = subProviders[this];
241  if ( !p->prepare( context, attributeNames ) )
242  {
243  subProviders.remove( this );
244  delete p;
245  }
246  }
247 
248  if ( mFilter )
249  {
250  attributeNames << mFilter->referencedColumns();
251  mFilter->prepare( &context.expressionContext() );
252  }
253 
254  // call recursively
255  Q_FOREACH ( Rule* rule, mChildren )
256  {
257  rule->prepare( context, attributeNames, subProviders );
258  }
259 }
260 
262 {
263  if ( !isFilterOK( feature, context )
264  || !isScaleOK( context.rendererScale() ) )
265  return Filtered;
266 
267  bool registered = false;
268 
269  // do we have active subprovider for the rule?
270  if ( subProviders.contains( this ) && mIsActive )
271  {
272  subProviders[this]->registerFeature( feature, context, obstacleGeometry );
273  registered = true;
274  }
275 
276  bool willRegisterSomething = false;
277 
278  // call recursively
279  Q_FOREACH ( Rule* rule, mChildren )
280  {
281  // Don't process else rules yet
282  if ( !rule->isElse() )
283  {
284  RegisterResult res = rule->registerFeature( feature, context, subProviders, obstacleGeometry );
285  // consider inactive items as "registered" so the else rule will ignore them
286  willRegisterSomething |= ( res == Registered || res == Inactive );
287  registered |= willRegisterSomething;
288  }
289  }
290 
291  // If none of the rules passed then we jump into the else rules and process them.
292  if ( !willRegisterSomething )
293  {
294  Q_FOREACH ( Rule* rule, mElseRules )
295  {
296  registered |= rule->registerFeature( feature, context, subProviders, obstacleGeometry ) != Filtered;
297  }
298  }
299 
300  if ( !mIsActive )
301  return Inactive;
302  else if ( registered )
303  return Registered;
304  else
305  return Filtered;
306 }
307 
309 {
310  if ( ! mFilter || mElseRule )
311  return true;
312 
313  context.expressionContext().setFeature( f );
314  QVariant res = mFilter->evaluate( &context.expressionContext() );
315  return res.toInt() != 0;
316 }
317 
318 bool QgsRuleBasedLabeling::Rule::isScaleOK( double scale ) const
319 {
320  if ( qgsDoubleNear( scale, 0.0 ) ) // so that we can count features in classes without scale context
321  return true;
322  if ( mScaleMinDenom == 0 && mScaleMaxDenom == 0 )
323  return true;
324  if ( mScaleMinDenom != 0 && mScaleMinDenom > scale )
325  return false;
326  if ( mScaleMaxDenom != 0 && mScaleMaxDenom < scale )
327  return false;
328  return true;
329 }
330 
332 
334  : mRootRule( root )
335 {
336 
337 }
338 
340 {
341  mRootRule = other.mRootRule->clone();
342 }
343 
345 {
346  delete mRootRule;
347 }
348 
350 {
351  QDomElement rulesElem = element.firstChildElement( "rules" );
352 
353  Rule* root = Rule::create( rulesElem );
354  if ( !root )
355  return nullptr;
356 
357  QgsRuleBasedLabeling* rl = new QgsRuleBasedLabeling( root );
358  return rl;
359 }
360 
362 {
363  return "rule-based";
364 }
365 
367 {
368  QDomElement elem = doc.createElement( "labeling" );
369  elem.setAttribute( "type", "rule-based" );
370 
371  QDomElement rulesElem = mRootRule->save( doc );
372  rulesElem.setTagName( "rules" ); // instead of just "rule"
373  elem.appendChild( rulesElem );
374 
375  return elem;
376 }
377 
379 {
380  return new QgsRuleBasedLabelProvider( *this, layer, false );
381 }
Class for parsing and evaluation of expressions (formerly called "search strings").
void clear()
QgsPalLayerSettings * settings() const
Get the labeling settings.
double rendererScale() const
virtual QgsVectorLayerLabelProvider * provider(QgsVectorLayer *layer) const override
Factory for label provider implementation.
bool isScaleOK(double scale) const
Check if this rule applies for a given scale.
bool contains(const Key &key) const
Q_DECL_DEPRECATED QVariant evaluate(const QgsFeature *f)
Evaluate the feature and return the result.
void updateElseRules()
Check which child rules are else rules and update the internal list of else rules.
QgsRuleBasedLabeling::RuleToProviderMap mSubProviders
label providers are owned by labeling engine
virtual bool prepare(const QgsRenderContext &context, QStringList &attributeNames) override
Prepare for registration of features.
QDomNode appendChild(const QDomNode &newChild)
int scaleMinDenom() const
The minimum scale at which this label rule should be applied.
QString attribute(const QString &name, const QString &defValue) const
void setTagName(const QString &name)
Q_DECL_DEPRECATED bool prepare(const QgsFields &fields)
Get the expression ready for evaluation - find out column indexes.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
bool isElse() const
Check if this rule is an ELSE rule.
RegisterResult
The result of registering a rule.
QStringList referencedColumns() const
Get list of columns referenced by the expression.
Rule * clone() const
clone this rule, return new instance
const T & at(int i) const
void removeAt(int i)
QgsVectorLayerLabelProvider(QgsVectorLayer *layer, bool withFeatureLoop=true, const QgsPalLayerSettings *settings=nullptr, const QString &layerName=QString())
Convenience constructor to initialize the provider from given vector layer.
void prepare(const QgsRenderContext &context, QStringList &attributeNames, RuleToProviderMap &subProviders)
call prepare() on sub-providers and populate attributeNames
QDomElement nextSiblingElement(const QString &tagName) const
QString description() const
A human readable description for this rule.
The QgsVectorLayerLabelProvider class implements a label provider for vector layers.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:76
virtual QList< QgsAbstractLabelProvider * > subProviders() override
Return list of child providers - useful if the provider needs to put labels into more layers with dif...
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:187
bool isFilterOK(QgsFeature &f, QgsRenderContext &context) const
Check if a given feature shall be labelled by this rule.
virtual bool prepare(const QgsRenderContext &context, QStringList &attributeNames)
Prepare for registration of features.
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:285
virtual QDomElement save(QDomDocument &doc) const override
Return labeling configuration as XML element.
static Rule * create(const QDomElement &ruleElem)
Create a rule from an XML definition.
virtual QString type() const override
Unique type string of the labeling configuration implementation.
void append(const T &value)
void initFilter()
Initialize filters.
int toInt(bool *ok) const
virtual QgsVectorLayerLabelProvider * createProvider(QgsVectorLayer *layer, bool withFeatureLoop, const QgsPalLayerSettings *settings)
void setAttribute(const QString &name, const QString &value)
int toInt(bool *ok, int base) const
void setSettings(QgsPalLayerSettings *settings)
set new settings (or NULL). Deletes old settings if any.
QgsPalLayerSettings mSettings
Layer&#39;s labeling configuration.
bool isEmpty() const
void readXml(QDomElement &elem)
Read settings from a DOM element.
const QgsLabelingEngineV2 * mEngine
Associated labeling engine.
void setEngine(const QgsLabelingEngineV2 *engine)
Associate provider with a labeling engine (should be only called internally from QgsLabelingEngineV2)...
QgsRuleBasedLabeling(QgsRuleBasedLabeling::Rule *root)
Constructs the labeling from given tree of rules (takes ownership)
QDomElement save(QDomDocument &doc) const
store labeling info to XML element
static QgsRuleBasedLabeling * create(const QDomElement &element)
Create the instance from a DOM element with saved configuration.
void setActive(bool state)
Sets if this rule is active.
QgsExpressionContext & expressionContext()
Gets the expression context.
bool isNull() const
QgsPalLayerSettings * mSettings
Contains information about the context of a rendering operation.
Rule(QgsPalLayerSettings *settings, int scaleMinDenom=0, int scaleMaxDenom=0, const QString &filterExp=QString(), const QString &description=QString(), bool elseRule=false)
takes ownership of settings
void insert(int i, const T &value)
QgsRuleBasedLabeling mRules
owned copy
QDomElement firstChildElement(const QString &tagName) const
virtual void registerFeature(QgsFeature &feature, QgsRenderContext &context, QgsGeometry *obstacleGeometry=nullptr) override
Register a feature for labeling as one or more QgsLabelFeature objects stored into mLabels...
void insertChild(int i, Rule *rule)
add child rule, take ownership, sets this as parent
QDomElement writeXml(QDomDocument &doc)
Write settings into a DOM element.
void appendChild(Rule *rule)
add child rule, take ownership, sets this as parent
RegisterResult registerFeature(QgsFeature &feature, QgsRenderContext &context, RuleToProviderMap &subProviders, QgsGeometry *obstacleGeometry=nullptr)
register individual features
const_iterator constEnd() const
int scaleMaxDenom() const
The maximum scale denominator at which this label rule should be applied.
QDomElement createElement(const QString &tagName)
const_iterator constBegin() const
void removeChildAt(int i)
delete child rule
Represents a vector layer which manages a vector based data sets.
void createSubProviders(QgsVectorLayer *layer, RuleToProviderMap &subProviders, QgsRuleBasedLabelProvider *provider)
add providers
int compare(const QString &other) const
QgsRuleBasedLabelProvider(const QgsRuleBasedLabeling &rules, QgsVectorLayer *layer, bool withFeatureLoop=true)
const T value(const Key &key) const
int remove(const Key &key)