QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
qgsrulebased3drenderer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrulebased3drenderer.cpp
3  --------------------------------------
4  Date : January 2019
5  Copyright : (C) 2019 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 
16 #include "qgsrulebased3drenderer.h"
17 
18 #include "qgsvectorlayer.h"
19 #include "qgsxmlutils.h"
20 
21 #include "qgs3dmapsettings.h"
22 #include "qgs3dutils.h"
23 #include "qgsline3dsymbol.h"
24 #include "qgspoint3dsymbol.h"
25 #include "qgspolygon3dsymbol.h"
26 #include "qgsline3dsymbol_p.h"
27 #include "qgspoint3dsymbol_p.h"
28 #include "qgspolygon3dsymbol_p.h"
29 
31 
33  : Qgs3DRendererAbstractMetadata( QStringLiteral( "rulebased" ) )
34 {
35 }
36 
38 {
39  QDomElement rulesElem = elem.firstChildElement( QStringLiteral( "rules" ) );
40 
42  if ( !root )
43  return nullptr;
44 
46  r->readXml( elem, context );
47  return r;
48 }
49 
50 
51 // ---------
52 
53 
55 
56 QgsRuleBased3DRenderer::Rule::Rule( QgsAbstract3DSymbol *symbol, const QString &filterExp, const QString &description, bool elseRule )
57  : mSymbol( symbol )
58  , mFilterExp( filterExp )
59  , mDescription( description )
60  , mElseRule( elseRule )
61 
62 {
63  initFilter();
64 }
65 
67 {
68  qDeleteAll( mChildren );
69  // do NOT delete parent
70 }
71 
73 {
74  if ( mSymbol.get() == symbol )
75  return;
76 
77  mSymbol.reset( symbol );
78 }
79 
81 {
82  RuleList l;
83  for ( Rule *c : mChildren )
84  {
85  l += c;
86  l += c->descendants();
87  }
88  return l;
89 }
90 
91 void QgsRuleBased3DRenderer::Rule::initFilter()
92 {
93  if ( mElseRule || mFilterExp.compare( QLatin1String( "ELSE" ), Qt::CaseInsensitive ) == 0 )
94  {
95  mElseRule = true;
96  mFilter.reset( nullptr );
97  }
98  else if ( !mFilterExp.isEmpty() )
99  {
100  mFilter.reset( new QgsExpression( mFilterExp ) );
101  }
102  else
103  {
104  mFilter.reset( nullptr );
105  }
106 }
107 
108 void QgsRuleBased3DRenderer::Rule::updateElseRules()
109 {
110  mElseRules.clear();
111  for ( Rule *rule : qgis::as_const( 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 {
142  // we could use a hash / map for search if this will be slow...
143 
144  if ( key == mRuleKey )
145  return this;
146 
147  for ( Rule *rule : qgis::as_const( mChildren ) )
148  {
149  const Rule *r = rule->findRuleByKey( key );
150  if ( r )
151  return r;
152  }
153  return nullptr;
154 }
155 
157 {
158  if ( key == mRuleKey )
159  return this;
160 
161  for ( Rule *rule : qgis::as_const( mChildren ) )
162  {
163  Rule *r = rule->findRuleByKey( key );
164  if ( r )
165  return r;
166  }
167  return nullptr;
168 }
169 
171 {
172  QgsAbstract3DSymbol *symbol = mSymbol.get() ? mSymbol->clone() : nullptr;
173  Rule *newrule = new Rule( symbol, mFilterExp, mDescription );
174  newrule->setActive( mIsActive );
175  // clone children
176  for ( Rule *rule : qgis::as_const( mChildren ) )
177  newrule->appendChild( rule->clone() );
178  return newrule;
179 }
180 
182 {
183  QgsAbstract3DSymbol *symbol = nullptr;
184  QDomElement elemSymbol = ruleElem.firstChildElement( QStringLiteral( "symbol" ) );
185  if ( !elemSymbol.isNull() )
186  {
187  QString symbolType = elemSymbol.attribute( QStringLiteral( "type" ) );
188  if ( symbolType == QLatin1String( "polygon" ) )
189  symbol = new QgsPolygon3DSymbol;
190  else if ( symbolType == QLatin1String( "point" ) )
191  symbol = new QgsPoint3DSymbol;
192  else if ( symbolType == QLatin1String( "line" ) )
193  symbol = new QgsLine3DSymbol;
194 
195  if ( symbol )
196  symbol->readXml( elemSymbol, context );
197  }
198 
199  QString filterExp = ruleElem.attribute( QStringLiteral( "filter" ) );
200  QString description = ruleElem.attribute( QStringLiteral( "description" ) );
201  QString ruleKey = ruleElem.attribute( QStringLiteral( "key" ) );
202  Rule *rule = new Rule( symbol, filterExp, description );
203 
204  if ( !ruleKey.isEmpty() )
205  rule->mRuleKey = ruleKey;
206 
207  rule->setActive( ruleElem.attribute( QStringLiteral( "active" ), QStringLiteral( "1" ) ).toInt() );
208 
209  QDomElement childRuleElem = ruleElem.firstChildElement( QStringLiteral( "rule" ) );
210  while ( !childRuleElem.isNull() )
211  {
212  Rule *childRule = create( childRuleElem, context );
213  if ( childRule )
214  {
215  rule->appendChild( childRule );
216  }
217  else
218  {
219  //QgsDebugMsg( QStringLiteral( "failed to init a child rule!" ) );
220  }
221  childRuleElem = childRuleElem.nextSiblingElement( QStringLiteral( "rule" ) );
222  }
223 
224  return rule;
225 }
226 
227 QDomElement QgsRuleBased3DRenderer::Rule::save( QDomDocument &doc, const QgsReadWriteContext &context ) const
228 {
229  QDomElement ruleElem = doc.createElement( QStringLiteral( "rule" ) );
230 
231  if ( mSymbol )
232  {
233  QDomElement elemSymbol = doc.createElement( QStringLiteral( "symbol" ) );
234  elemSymbol.setAttribute( QStringLiteral( "type" ), mSymbol->type() );
235  mSymbol->writeXml( elemSymbol, context );
236  ruleElem.appendChild( elemSymbol );
237  }
238 
239  if ( !mFilterExp.isEmpty() )
240  ruleElem.setAttribute( QStringLiteral( "filter" ), mFilterExp );
241  if ( !mDescription.isEmpty() )
242  ruleElem.setAttribute( QStringLiteral( "description" ), mDescription );
243  if ( !mIsActive )
244  ruleElem.setAttribute( QStringLiteral( "active" ), 0 );
245  ruleElem.setAttribute( QStringLiteral( "key" ), mRuleKey );
246 
247  for ( RuleList::const_iterator it = mChildren.constBegin(); it != mChildren.constEnd(); ++it )
248  {
249  Rule *rule = *it;
250  ruleElem.appendChild( rule->save( doc, context ) );
251  }
252  return ruleElem;
253 }
254 
255 
257 {
258  if ( mSymbol )
259  {
260  // add handler!
261  Q_ASSERT( !handlers.value( this ) );
262  QgsFeature3DHandler *handler = nullptr;
263  if ( mSymbol->type() == QLatin1String( "polygon" ) )
264  {
265  handler = Qgs3DSymbolImpl::handlerForPolygon3DSymbol( layer, *static_cast<QgsPolygon3DSymbol *>( mSymbol.get() ) );
266  }
267  else if ( mSymbol->type() == QLatin1String( "point" ) )
268  {
269  handler = Qgs3DSymbolImpl::handlerForPoint3DSymbol( layer, *static_cast<QgsPoint3DSymbol *>( mSymbol.get() ) );
270  }
271  else if ( mSymbol->type() == QLatin1String( "line" ) )
272  {
273  handler = Qgs3DSymbolImpl::handlerForLine3DSymbol( layer, *static_cast<QgsLine3DSymbol *>( mSymbol.get() ) );
274  }
275 
276  if ( handler )
277  handlers[this] = handler;
278  }
279 
280  // call recursively
281  for ( Rule *rule : qgis::as_const( mChildren ) )
282  {
283  rule->createHandlers( layer, handlers );
284  }
285 }
286 
287 
288 void QgsRuleBased3DRenderer::Rule::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames, QgsRuleBased3DRenderer::RuleToHandlerMap &handlers ) const
289 {
290  if ( mSymbol )
291  {
292  QgsFeature3DHandler *handler = handlers[this];
293  if ( !handler->prepare( context, attributeNames ) )
294  {
295  handlers.remove( this );
296  delete handler;
297  }
298  }
299 
300  if ( mFilter )
301  {
302  attributeNames.unite( mFilter->referencedColumns() );
303  mFilter->prepare( &context.expressionContext() );
304  }
305 
306  // call recursively
307  for ( Rule *rule : qgis::as_const( mChildren ) )
308  {
309  rule->prepare( context, attributeNames, handlers );
310  }
311 }
312 
314 {
315  if ( !isFilterOK( feature, context ) )
316  return Filtered;
317 
318  bool registered = false;
319 
320  // do we have active handler for the rule?
321  if ( handlers.contains( this ) && mIsActive )
322  {
323  handlers[this]->processFeature( feature, context );
324  registered = true;
325  }
326 
327  bool willRegisterSomething = false;
328 
329  // call recursively
330  for ( Rule *rule : qgis::as_const( mChildren ) )
331  {
332  // Don't process else rules yet
333  if ( !rule->isElse() )
334  {
335  RegisterResult res = rule->registerFeature( feature, context, handlers );
336  // consider inactive items as "registered" so the else rule will ignore them
337  willRegisterSomething |= ( res == Registered || res == Inactive );
338  registered |= willRegisterSomething;
339  }
340  }
341 
342  // If none of the rules passed then we jump into the else rules and process them.
343  if ( !willRegisterSomething )
344  {
345  for ( Rule *rule : qgis::as_const( mElseRules ) )
346  {
347  registered |= rule->registerFeature( feature, context, handlers ) != Filtered;
348  }
349  }
350 
351  if ( !mIsActive )
352  return Inactive;
353  else if ( registered )
354  return Registered;
355  else
356  return Filtered;
357 }
358 
359 
360 bool QgsRuleBased3DRenderer::Rule::isFilterOK( QgsFeature &f, Qgs3DRenderContext &context ) const
361 {
362  if ( ! mFilter || mElseRule )
363  return true;
364 
365  context.expressionContext().setFeature( f );
366  QVariant res = mFilter->evaluate( &context.expressionContext() );
367  return res.toInt() != 0;
368 }
369 
370 
372 
373 
375  : mRootRule( root )
376 {
377 }
378 
380 {
381  delete mRootRule;
382 }
383 
385 {
386  Rule *rootRule = mRootRule->clone();
387 
388  // normally with clone() the individual rules get new keys (UUID), but here we want to keep
389  // the tree of rules intact, so that other components that may use the rule keys work nicely (e.g. map themes)
390  rootRule->setRuleKey( mRootRule->ruleKey() );
391  RuleList origDescendants = mRootRule->descendants();
392  RuleList clonedDescendants = rootRule->descendants();
393  Q_ASSERT( origDescendants.count() == clonedDescendants.count() );
394  for ( int i = 0; i < origDescendants.count(); ++i )
395  clonedDescendants[i]->setRuleKey( origDescendants[i]->ruleKey() );
396 
397  QgsRuleBased3DRenderer *r = new QgsRuleBased3DRenderer( rootRule );
398  copyBaseProperties( r );
399  return r;
400 }
401 
402 Qt3DCore::QEntity *QgsRuleBased3DRenderer::createEntity( const Qgs3DMapSettings &map ) const
403 {
404  QgsVectorLayer *vl = layer();
405 
406  if ( !vl )
407  return nullptr;
408 
409  double zMin, zMax;
410  Qgs3DUtils::estimateVectorLayerZRange( vl, zMin, zMax );
411 
412  return new QgsRuleBasedChunkedEntity( vl, zMin, zMax, tilingSettings(), mRootRule, map );
413 }
414 
415 void QgsRuleBased3DRenderer::writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const
416 {
417  QDomDocument doc = elem.ownerDocument();
418 
419  writeXmlBaseProperties( elem, context );
420 
421  QDomElement rulesElem = mRootRule->save( doc, context );
422  rulesElem.setTagName( QStringLiteral( "rules" ) ); // instead of just "rule"
423  elem.appendChild( rulesElem );
424 }
425 
426 void QgsRuleBased3DRenderer::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
427 {
428  readXmlBaseProperties( elem, context );
429 
430  // root rule is read before class constructed
431 }
void setSymbol(QgsAbstract3DSymbol *symbol)
Sets new symbol (or nullptr). Deletes old symbol if any.
Class for parsing and evaluation of expressions (formerly called "search strings").
The class is used as a container of context for various read/write operations on other objects...
RegisterResult registerFeature(QgsFeature &feature, Qgs3DRenderContext &context, RuleToHandlerMap &handlers) const
register individual features
static void estimateVectorLayerZRange(QgsVectorLayer *layer, double &zMin, double &zMax)
Try to estimate range of Z values used in the given vector layer and store that in zMin and zMax...
Definition: qgs3dutils.cpp:519
Base class for all renderers that may to participate in 3D view.
QList< QgsRuleBased3DRenderer::Rule * > RuleList
QgsVectorLayer3DTilingSettings tilingSettings() const
Returns tiling settings of the renderer.
const QgsRuleBased3DRenderer::Rule * findRuleByKey(const QString &key) const
Try to find a rule given its unique key.
void appendChild(QgsRuleBased3DRenderer::Rule *rule)
add child rule, take ownership, sets this as parent
QgsVectorLayer * layer() const
Returns vector layer associated with the renderer.
3 3D symbol that draws polygon geometries as planar polygons, optionally extruded (with added walls)...
QgsAbstract3DSymbol * symbol() const
Returns the labeling settings.
void writeXml(QDomElement &elem, const QgsReadWriteContext &context) const override
Writes renderer&#39;s properties to given XML element.
QString ruleKey() const
Unique rule identifier (for identification of rule within labeling, used as provider ID) ...
QString description() const
A human readable description for this rule.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
3 Definition of the world
3 Abstract base class for 3D symbols that are used by VectorLayer3DRenderer objects.
QgsRuleBased3DRenderer(QgsRuleBased3DRenderer::Rule *root)
Construct renderer with the given root rule (takes ownership)
void copyBaseProperties(QgsAbstractVectorLayer3DRenderer *r) const
Copies common properties of this object to another object.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
3 Rule-based 3D renderer.
QgsAbstract3DRenderer * createRenderer(QDomElement &elem, const QgsReadWriteContext &context) override
Creates an instance of a 3D renderer based on a DOM element with renderer configuration.
QDomElement save(QDomDocument &doc, const QgsReadWriteContext &context) const
store labeling info to XML element
3 3D symbol that draws point geometries as 3D objects using one of the predefined shapes...
void insertChild(int i, QgsRuleBased3DRenderer::Rule *rule)
add child rule, take ownership, sets this as parent
void createHandlers(QgsVectorLayer *layer, RuleToHandlerMap &handlers) const
add handlers
Qt3DCore::QEntity * createEntity(const Qgs3DMapSettings &map) const override
Returns a 3D entity that will be used to show renderer&#39;s data in 3D scene.
3 3D symbol that draws linestring geometries as planar polygons (created from lines using a buffer wi...
QgsRuleBased3DRenderer::RuleList descendants() const
Returns all children, grand-children, grand-grand-children, grand-gra...
QgsRuleBased3DRenderer::Rule * rootRule()
Returns pointer to the root rule.
void prepare(const Qgs3DRenderContext &context, QSet< QString > &attributeNames, RuleToHandlerMap &handlers) const
call prepare() on handlers and populate attributeNames
RegisterResult
The result of registering a rule.
void removeChildAt(int i)
delete child rule
void writeXmlBaseProperties(QDomElement &elem, const QgsReadWriteContext &context) const
Writes common properties of this object to DOM element.
void readXml(const QDomElement &elem, const QgsReadWriteContext &context) override
Reads renderer&#39;s properties from given XML element.
virtual void readXml(const QDomElement &elem, const QgsReadWriteContext &context)=0
Reads symbol configuration from the given DOM element.
QgsRuleBased3DRenderer * clone() const override
Returns a cloned instance.
Rule(QgsAbstract3DSymbol *symbol, const QString &filterExp=QString(), const QString &description=QString(), bool elseRule=false)
takes ownership of symbol, symbol may be nullptr
QHash< const QgsRuleBased3DRenderer::Rule *, QgsFeature3DHandler * > RuleToHandlerMap
void setActive(bool state)
Sets if this rule is active.
Base metadata class for 3D renderers.
Represents a vector layer which manages a vector based data sets.
void setRuleKey(const QString &key)
Override the assigned rule key (should be used just internally by rule-based renderer) ...
static QgsRuleBased3DRenderer::Rule * create(const QDomElement &ruleElem, const QgsReadWriteContext &context)
Create a rule from an XML definition.
QgsRuleBased3DRenderer::Rule * clone() const
clone this rule, return new instance
void readXmlBaseProperties(const QDomElement &elem, const QgsReadWriteContext &context)
Reads common properties of this object from DOM element.