QGIS API Documentation  2.14.5-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  Q_ASSERT( !mSettings == subProviders.contains( this ) );
270 
271  // do we have active subprovider for the rule?
272  if ( subProviders.contains( this ) && mIsActive )
273  {
274  subProviders[this]->registerFeature( feature, context, obstacleGeometry );
275  registered = true;
276  }
277 
278  bool willRegisterSomething = false;
279 
280  // call recursively
281  Q_FOREACH ( Rule* rule, mChildren )
282  {
283  // Don't process else rules yet
284  if ( !rule->isElse() )
285  {
286  RegisterResult res = rule->registerFeature( feature, context, subProviders, obstacleGeometry );
287  // consider inactive items as "registered" so the else rule will ignore them
288  willRegisterSomething |= ( res == Registered || res == Inactive );
289  registered |= willRegisterSomething;
290  }
291  }
292 
293  // If none of the rules passed then we jump into the else rules and process them.
294  if ( !willRegisterSomething )
295  {
296  Q_FOREACH ( Rule* rule, mElseRules )
297  {
298  registered |= rule->registerFeature( feature, context, subProviders, obstacleGeometry ) != Filtered;
299  }
300  }
301 
302  if ( !mIsActive )
303  return Inactive;
304  else if ( registered )
305  return Registered;
306  else
307  return Filtered;
308 }
309 
311 {
312  if ( ! mFilter || mElseRule )
313  return true;
314 
315  context.expressionContext().setFeature( f );
316  QVariant res = mFilter->evaluate( &context.expressionContext() );
317  return res.toInt() != 0;
318 }
319 
320 bool QgsRuleBasedLabeling::Rule::isScaleOK( double scale ) const
321 {
322  if ( qgsDoubleNear( scale, 0.0 ) ) // so that we can count features in classes without scale context
323  return true;
324  if ( mScaleMinDenom == 0 && mScaleMaxDenom == 0 )
325  return true;
326  if ( mScaleMinDenom != 0 && mScaleMinDenom > scale )
327  return false;
328  if ( mScaleMaxDenom != 0 && mScaleMaxDenom < scale )
329  return false;
330  return true;
331 }
332 
334 
336  : mRootRule( root )
337 {
338 
339 }
340 
342 {
343  mRootRule = other.mRootRule->clone();
344 }
345 
347 {
348  delete mRootRule;
349 }
350 
352 {
353  QDomElement rulesElem = element.firstChildElement( "rules" );
354 
355  Rule* root = Rule::create( rulesElem );
356  if ( !root )
357  return nullptr;
358 
359  QgsRuleBasedLabeling* rl = new QgsRuleBasedLabeling( root );
360  return rl;
361 }
362 
364 {
365  return "rule-based";
366 }
367 
369 {
370  QDomElement elem = doc.createElement( "labeling" );
371  elem.setAttribute( "type", "rule-based" );
372 
373  QDomElement rulesElem = mRootRule->save( doc );
374  rulesElem.setTagName( "rules" ); // instead of just "rule"
375  elem.appendChild( rulesElem );
376 
377  return elem;
378 }
379 
381 {
382  return new QgsRuleBasedLabelProvider( *this, layer, false );
383 }
Class for parsing and evaluation of expressions (formerly called "search strings").
void clear()
QgsPalLayerSettings * settings() const
Get the labeling settings.
virtual QgsVectorLayerLabelProvider * provider(QgsVectorLayer *layer) const override
Factory for label provider implementation.
bool isElse() const
Check if this rule is an ELSE rule.
QStringList referencedColumns() const
Get list of columns referenced by the expression.
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.
QString description() const
A human readable description for this rule.
QDomNode appendChild(const QDomNode &newChild)
bool isFilterOK(QgsFeature &f, QgsRenderContext &context) const
Check if a given feature shall be labelled by this rule.
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.
QDomElement save(QDomDocument &doc) const
store labeling info to XML element
RegisterResult
The result of registering a rule.
double rendererScale() const
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
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
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
int scaleMinDenom() const
The minimum scale at which this label rule should be applied.
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
bool isScaleOK(double scale) const
Check if this rule applies for a given scale.
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)
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
Rule * clone() const
clone this rule, return new instance
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
QDomElement createElement(const QString &tagName)
const_iterator constBegin() const
void removeChildAt(int i)
delete child rule
int scaleMaxDenom() const
The maximum scale denominator at which this label rule should be applied.
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)