QGIS API Documentation  2.12.0-Lyon
qgsrulebasedlabeling.cpp
Go to the documentation of this file.
1 #include "qgsrulebasedlabeling.h"
2 
3 
4 
6  : QgsVectorLayerLabelProvider( layer, withFeatureLoop )
7  , mRules( rules )
8 {
10 }
11 
13 {
14  // sub-providers owned by labeling engine
15 }
16 
17 
18 bool QgsRuleBasedLabelProvider::prepare( const QgsRenderContext& context, QStringList& attributeNames )
19 {
20  Q_FOREACH ( QgsVectorLayerLabelProvider* provider, mSubProviders.values() )
21  provider->setEngine( mEngine );
22 
23  // populate sub-providers
24  mRules.rootRule()->prepare( context, attributeNames, mSubProviders );
25  return true;
26 }
27 
29 {
30  // will register the feature to relevant sub-providers
31  mRules.rootRule()->registerFeature( feature, context, mSubProviders );
32 }
33 
35 {
37  Q_FOREACH ( QgsVectorLayerLabelProvider* subprovider, mSubProviders.values() )
38  lst << subprovider;
39  return lst;
40 }
41 
42 
44 
45 QgsRuleBasedLabeling::Rule::Rule( QgsPalLayerSettings* settings, int scaleMinDenom, int scaleMaxDenom, const QString& filterExp, const QString& description, bool elseRule )
46  : mParent( 0 ), mSettings( settings )
47  , mScaleMinDenom( scaleMinDenom ), mScaleMaxDenom( scaleMaxDenom )
48  , mFilterExp( filterExp ), mDescription( description )
49  , mElseRule( elseRule )
50  , mIsActive( true )
51  , mFilter( 0 )
52 {
53  initFilter();
54 }
55 
57 {
58  delete mSettings;
59  delete mFilter;
60  qDeleteAll( mChildren );
61  // do NOT delete parent
62 }
63 
65 {
66  if ( mSettings == settings )
67  return;
68 
69  delete mSettings;
70  mSettings = settings;
71 }
72 
74 {
75  if ( mElseRule || mFilterExp.compare( "ELSE", Qt::CaseInsensitive ) == 0 )
76  {
77  mElseRule = true;
78  mFilter = 0;
79  }
80  else if ( !mFilterExp.isEmpty() )
81  {
82  delete mFilter;
83  mFilter = new QgsExpression( mFilterExp );
84  }
85  else
86  {
87  mFilter = 0;
88  }
89 }
90 
92 {
93  mElseRules.clear();
94  Q_FOREACH ( Rule* rule, mChildren )
95  {
96  if ( rule->isElse() )
97  mElseRules << rule;
98  }
99 }
100 
101 
103 {
104  mChildren.append( rule );
105  rule->mParent = this;
106  updateElseRules();
107 }
108 
110 {
111  mChildren.insert( i, rule );
112  rule->mParent = this;
113  updateElseRules();
114 }
115 
117 {
118  Rule* rule = mChildren[i];
119  mChildren.removeAt( i );
120  delete rule;
121  updateElseRules();
122 }
123 
125 {
126  QgsPalLayerSettings* s = mSettings ? new QgsPalLayerSettings( *mSettings ) : 0;
127  Rule* newrule = new Rule( s, mScaleMinDenom, mScaleMaxDenom, mFilterExp, mDescription );
128  newrule->setActive( mIsActive );
129  // clone children
130  Q_FOREACH ( Rule* rule, mChildren )
131  newrule->appendChild( rule->clone() );
132  return newrule;
133 }
134 
136 {
137  QgsPalLayerSettings* settings = 0;
138  QDomElement settingsElem = ruleElem.firstChildElement( "settings" );
139  if ( !settingsElem.isNull() )
140  {
141  settings = new QgsPalLayerSettings;
142  settings->readXml( settingsElem );
143  }
144 
145  QString filterExp = ruleElem.attribute( "filter" );
146  QString description = ruleElem.attribute( "description" );
147  int scaleMinDenom = ruleElem.attribute( "scalemindenom", "0" ).toInt();
148  int scaleMaxDenom = ruleElem.attribute( "scalemaxdenom", "0" ).toInt();
149  //QString ruleKey = ruleElem.attribute( "key" );
150  Rule* rule = new Rule( settings, scaleMinDenom, scaleMaxDenom, filterExp, description );
151 
152  //if ( !ruleKey.isEmpty() )
153  // rule->mRuleKey = ruleKey;
154 
155  rule->setActive( ruleElem.attribute( "active", "1" ).toInt() );
156 
157  QDomElement childRuleElem = ruleElem.firstChildElement( "rule" );
158  while ( !childRuleElem.isNull() )
159  {
160  Rule* childRule = create( childRuleElem );
161  if ( childRule )
162  {
163  rule->appendChild( childRule );
164  }
165  else
166  {
167  //QgsDebugMsg( "failed to init a child rule!" );
168  }
169  childRuleElem = childRuleElem.nextSiblingElement( "rule" );
170  }
171 
172  return rule;
173 }
174 
176 {
177  QDomElement ruleElem = doc.createElement( "rule" );
178 
179  if ( mSettings )
180  {
181  ruleElem.appendChild( mSettings->writeXml( doc ) );
182  }
183  if ( !mFilterExp.isEmpty() )
184  ruleElem.setAttribute( "filter", mFilterExp );
185  if ( mScaleMinDenom != 0 )
186  ruleElem.setAttribute( "scalemindenom", mScaleMinDenom );
187  if ( mScaleMaxDenom != 0 )
188  ruleElem.setAttribute( "scalemaxdenom", mScaleMaxDenom );
189  if ( !mDescription.isEmpty() )
190  ruleElem.setAttribute( "description", mDescription );
191  if ( !mIsActive )
192  ruleElem.setAttribute( "active", 0 );
193  //ruleElem.setAttribute( "key", mRuleKey );
194 
195  for ( RuleList::const_iterator it = mChildren.constBegin(); it != mChildren.constEnd(); ++it )
196  {
197  Rule* rule = *it;
198  ruleElem.appendChild( rule->save( doc ) );
199  }
200  return ruleElem;
201 }
202 
204 {
205  if ( mSettings )
206  {
207  // add provider!
208  QgsVectorLayerLabelProvider* p = new QgsVectorLayerLabelProvider( layer, false, mSettings );
209  subProviders[this] = p;
210  }
211 
212  // call recursively
213  Q_FOREACH ( Rule* rule, mChildren )
214  {
215  rule->createSubProviders( layer, subProviders );
216  }
217 }
218 
220 {
221  if ( mSettings )
222  {
223  QgsVectorLayerLabelProvider* p = subProviders[this];
224  if ( !p->prepare( context, attributeNames ) )
225  {
226  subProviders.remove( this );
227  delete p;
228  }
229  }
230 
231  if ( mFilter )
232  {
233  attributeNames << mFilter->referencedColumns();
234  mFilter->prepare( &context.expressionContext() );
235  }
236 
237  // call recursively
238  Q_FOREACH ( Rule* rule, mChildren )
239  {
240  rule->prepare( context, attributeNames, subProviders );
241  }
242 }
243 
245 {
246  if ( !isFilterOK( feature, context )
247  || !isScaleOK( context.rendererScale() ) )
248  return Filtered;
249 
250  bool registered = false;
251 
252  // do we have active subprovider for the rule?
253  if ( subProviders.contains( this ) && mIsActive )
254  {
255  subProviders[this]->registerFeature( feature, context );
256  registered = true;
257  }
258 
259  bool willRegisterSomething = false;
260 
261  // call recursively
262  Q_FOREACH ( Rule* rule, mChildren )
263  {
264  // Don't process else rules yet
265  if ( !rule->isElse() )
266  {
267  RegisterResult res = rule->registerFeature( feature, context, subProviders );
268  // consider inactive items as "registered" so the else rule will ignore them
269  willRegisterSomething |= ( res == Registered || res == Inactive );
270  registered |= willRegisterSomething;
271  }
272  }
273 
274  // If none of the rules passed then we jump into the else rules and process them.
275  if ( !willRegisterSomething )
276  {
277  Q_FOREACH ( Rule* rule, mElseRules )
278  {
279  registered |= rule->registerFeature( feature, context, subProviders ) != Filtered;
280  }
281  }
282 
283  if ( !mIsActive )
284  return Inactive;
285  else if ( registered )
286  return Registered;
287  else
288  return Filtered;
289 }
290 
292 {
293  if ( ! mFilter || mElseRule )
294  return true;
295 
296  context.expressionContext().setFeature( f );
297  QVariant res = mFilter->evaluate( &context.expressionContext() );
298  return res.toInt() != 0;
299 }
300 
301 bool QgsRuleBasedLabeling::Rule::isScaleOK( double scale ) const
302 {
303  if ( scale == 0 ) // so that we can count features in classes without scale context
304  return true;
305  if ( mScaleMinDenom == 0 && mScaleMaxDenom == 0 )
306  return true;
307  if ( mScaleMinDenom != 0 && mScaleMinDenom > scale )
308  return false;
309  if ( mScaleMaxDenom != 0 && mScaleMaxDenom < scale )
310  return false;
311  return true;
312 }
313 
315 
317  : mRootRule( root )
318 {
319 
320 }
321 
323 {
324  mRootRule = other.mRootRule->clone();
325 }
326 
328 {
329  delete mRootRule;
330 }
331 
333 {
334  QDomElement rulesElem = element.firstChildElement( "rules" );
335 
336  Rule* root = Rule::create( rulesElem );
337  if ( !root )
338  return 0;
339 
340  QgsRuleBasedLabeling* rl = new QgsRuleBasedLabeling( root );
341  return rl;
342 }
343 
345 {
346  return "rule-based";
347 }
348 
350 {
351  QDomElement elem = doc.createElement( "labeling" );
352  elem.setAttribute( "type", "rule-based" );
353 
354  QDomElement rulesElem = mRootRule->save( doc );
355  rulesElem.setTagName( "rules" ); // instead of just "rule"
356  elem.appendChild( rulesElem );
357 
358  return elem;
359 }
360 
362 {
363  return new QgsRuleBasedLabelProvider( *this, layer, false );
364 }
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:92
virtual QgsVectorLayerLabelProvider * provider(QgsVectorLayer *layer) const override
Factory for label provider implementation.
bool isElse() const
Check if this rule is an ELSE rule.
bool contains(const Key &key) const
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.
QList< T > values() const
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)
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
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.
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:176
virtual bool prepare(const QgsRenderContext &context, QStringList &attributeNames)
Prepare for registration of features.
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 createSubProviders(QgsVectorLayer *layer, RuleToProviderMap &subProviders)
add providers
int toInt(bool *ok) const
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.
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)
virtual void registerFeature(QgsFeature &feature, QgsRenderContext &context) override
Register a feature for labeling as one or more QgsLabelFeature objects stored into mLabels...
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
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
QgsRuleBasedLabeling mRules
owned copy
QDomElement firstChildElement(const QString &tagName) const
RegisterResult registerFeature(QgsFeature &feature, QgsRenderContext &context, RuleToProviderMap &subProviders)
register individual features
void insertChild(int i, Rule *rule)
add child rule, take ownership, sets this as parent
void appendChild(Rule *rule)
add child rule, take ownership, sets this as parent
QDomElement createElement(const QString &tagName)
void removeChildAt(int i)
delete child rule
Represents a vector layer which manages a vector based data sets.
QgsRuleBasedLabelProvider(const QgsRuleBasedLabeling &rules, QgsVectorLayer *layer, bool withFeatureLoop=true)
int remove(const Key &key)