QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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 
28 #include "qgsapplication.h"
29 #include "qgs3dsymbolregistry.h"
30 
32  : Qgs3DRendererAbstractMetadata( QStringLiteral( "rulebased" ) )
33 {
34 }
35 
37 {
38  QDomElement rulesElem = elem.firstChildElement( QStringLiteral( "rules" ) );
39 
41  if ( !root )
42  return nullptr;
43 
45  r->readXml( elem, context );
46  return r;
47 }
48 
49 
50 // ---------
51 
52 
54 
55 QgsRuleBased3DRenderer::Rule::Rule( QgsAbstract3DSymbol *symbol, const QString &filterExp, const QString &description, bool elseRule )
56  : mSymbol( symbol )
57  , mFilterExp( filterExp )
58  , mDescription( description )
59  , mElseRule( elseRule )
60 
61 {
62  initFilter();
63 }
64 
66 {
67  qDeleteAll( mChildren );
68  // do NOT delete parent
69 }
70 
72 {
73  if ( mSymbol.get() == symbol )
74  return;
75 
76  mSymbol.reset( symbol );
77 }
78 
80 {
81  RuleList l;
82  for ( Rule *c : mChildren )
83  {
84  l += c;
85  l += c->descendants();
86  }
87  return l;
88 }
89 
90 void QgsRuleBased3DRenderer::Rule::initFilter()
91 {
92  if ( mElseRule || mFilterExp.compare( QLatin1String( "ELSE" ), Qt::CaseInsensitive ) == 0 )
93  {
94  mElseRule = true;
95  mFilter.reset( nullptr );
96  }
97  else if ( !mFilterExp.isEmpty() )
98  {
99  mFilter.reset( new QgsExpression( mFilterExp ) );
100  }
101  else
102  {
103  mFilter.reset( nullptr );
104  }
105 }
106 
107 void QgsRuleBased3DRenderer::Rule::updateElseRules()
108 {
109  mElseRules.clear();
110  for ( Rule *rule : std::as_const( mChildren ) )
111  {
112  if ( rule->isElse() )
113  mElseRules << rule;
114  }
115 }
116 
117 
119 {
120  mChildren.append( rule );
121  rule->mParent = this;
122  updateElseRules();
123 }
124 
126 {
127  mChildren.insert( i, rule );
128  rule->mParent = this;
129  updateElseRules();
130 }
131 
133 {
134  delete mChildren.at( i );
135  mChildren.removeAt( i );
136  updateElseRules();
137 }
138 
140 {
141  // we could use a hash / map for search if this will be slow...
142 
143  if ( key == mRuleKey )
144  return this;
145 
146  for ( Rule *rule : std::as_const( mChildren ) )
147  {
148  const Rule *r = rule->findRuleByKey( key );
149  if ( r )
150  return r;
151  }
152  return nullptr;
153 }
154 
156 {
157  if ( key == mRuleKey )
158  return this;
159 
160  for ( Rule *rule : std::as_const( mChildren ) )
161  {
162  Rule *r = rule->findRuleByKey( key );
163  if ( r )
164  return r;
165  }
166  return nullptr;
167 }
168 
170 {
171  QgsAbstract3DSymbol *symbol = mSymbol.get() ? mSymbol->clone() : nullptr;
172  Rule *newrule = new Rule( symbol, mFilterExp, mDescription );
173  newrule->setActive( mIsActive );
174  // clone children
175  for ( Rule *rule : std::as_const( mChildren ) )
176  newrule->appendChild( rule->clone() );
177  return newrule;
178 }
179 
181 {
182  QgsAbstract3DSymbol *symbol = nullptr;
183  QDomElement elemSymbol = ruleElem.firstChildElement( QStringLiteral( "symbol" ) );
184  if ( !elemSymbol.isNull() )
185  {
186  QString symbolType = elemSymbol.attribute( QStringLiteral( "type" ) );
187  symbol = QgsApplication::symbol3DRegistry()->createSymbol( symbolType );
188  if ( symbol )
189  symbol->readXml( elemSymbol, context );
190  }
191 
192  QString filterExp = ruleElem.attribute( QStringLiteral( "filter" ) );
193  QString description = ruleElem.attribute( QStringLiteral( "description" ) );
194  QString ruleKey = ruleElem.attribute( QStringLiteral( "key" ) );
195  Rule *rule = new Rule( symbol, filterExp, description );
196 
197  if ( !ruleKey.isEmpty() )
198  rule->mRuleKey = ruleKey;
199 
200  rule->setActive( ruleElem.attribute( QStringLiteral( "active" ), QStringLiteral( "1" ) ).toInt() );
201 
202  QDomElement childRuleElem = ruleElem.firstChildElement( QStringLiteral( "rule" ) );
203  while ( !childRuleElem.isNull() )
204  {
205  Rule *childRule = create( childRuleElem, context );
206  if ( childRule )
207  {
208  rule->appendChild( childRule );
209  }
210  else
211  {
212  //QgsDebugMsg( QStringLiteral( "failed to init a child rule!" ) );
213  }
214  childRuleElem = childRuleElem.nextSiblingElement( QStringLiteral( "rule" ) );
215  }
216 
217  return rule;
218 }
219 
220 QDomElement QgsRuleBased3DRenderer::Rule::save( QDomDocument &doc, const QgsReadWriteContext &context ) const
221 {
222  QDomElement ruleElem = doc.createElement( QStringLiteral( "rule" ) );
223 
224  if ( mSymbol )
225  {
226  QDomElement elemSymbol = doc.createElement( QStringLiteral( "symbol" ) );
227  elemSymbol.setAttribute( QStringLiteral( "type" ), mSymbol->type() );
228  mSymbol->writeXml( elemSymbol, context );
229  ruleElem.appendChild( elemSymbol );
230  }
231 
232  if ( !mFilterExp.isEmpty() )
233  ruleElem.setAttribute( QStringLiteral( "filter" ), mFilterExp );
234  if ( !mDescription.isEmpty() )
235  ruleElem.setAttribute( QStringLiteral( "description" ), mDescription );
236  if ( !mIsActive )
237  ruleElem.setAttribute( QStringLiteral( "active" ), 0 );
238  ruleElem.setAttribute( QStringLiteral( "key" ), mRuleKey );
239 
240  for ( RuleList::const_iterator it = mChildren.constBegin(); it != mChildren.constEnd(); ++it )
241  {
242  Rule *rule = *it;
243  ruleElem.appendChild( rule->save( doc, context ) );
244  }
245  return ruleElem;
246 }
247 
248 
250 {
251  if ( mSymbol )
252  {
253  // add handler!
254  Q_ASSERT( !handlers.value( this ) );
255  QgsFeature3DHandler *handler = QgsApplication::symbol3DRegistry()->createHandlerForSymbol( layer, mSymbol.get() );
256  if ( handler )
257  handlers[this] = handler;
258  }
259 
260  // call recursively
261  for ( Rule *rule : std::as_const( mChildren ) )
262  {
263  rule->createHandlers( layer, handlers );
264  }
265 }
266 
267 
268 void QgsRuleBased3DRenderer::Rule::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames, QgsRuleBased3DRenderer::RuleToHandlerMap &handlers ) const
269 {
270  if ( mSymbol )
271  {
272  QgsFeature3DHandler *handler = handlers[this];
273  if ( !handler->prepare( context, attributeNames ) )
274  {
275  handlers.remove( this );
276  delete handler;
277  }
278  }
279 
280  if ( mFilter )
281  {
282  attributeNames.unite( mFilter->referencedColumns() );
283  mFilter->prepare( &context.expressionContext() );
284  }
285 
286  // call recursively
287  for ( Rule *rule : std::as_const( mChildren ) )
288  {
289  rule->prepare( context, attributeNames, handlers );
290  }
291 }
292 
294 {
295  if ( !isFilterOK( feature, context ) )
296  return Filtered;
297 
298  bool registered = false;
299 
300  // do we have active handler for the rule?
301  if ( handlers.contains( this ) && mIsActive )
302  {
303  handlers[this]->processFeature( feature, context );
304  registered = true;
305  }
306 
307  bool matchedAChild = false;
308 
309  // call recursively
310  for ( Rule *rule : std::as_const( mChildren ) )
311  {
312  // Don't process else rules yet
313  if ( !rule->isElse() )
314  {
315  const RegisterResult res = rule->registerFeature( feature, context, handlers );
316  // consider inactive items as "matched" so the else rule will ignore them
317  matchedAChild |= ( res == Registered || res == Inactive );
318  registered |= matchedAChild;
319  }
320  }
321 
322  // If none of the rules passed then we jump into the else rules and process them.
323  if ( !matchedAChild )
324  {
325  for ( Rule *rule : std::as_const( mElseRules ) )
326  {
327  const RegisterResult res = rule->registerFeature( feature, context, handlers );
328  matchedAChild |= ( res == Registered || res == Inactive );
329  registered |= res != Filtered;
330  }
331  }
332 
333  if ( !mIsActive || ( matchedAChild && !registered ) )
334  return Inactive;
335  else if ( registered )
336  return Registered;
337  else
338  return Filtered;
339 }
340 
341 
342 bool QgsRuleBased3DRenderer::Rule::isFilterOK( QgsFeature &f, Qgs3DRenderContext &context ) const
343 {
344  if ( ! mFilter || mElseRule )
345  return true;
346 
347  context.expressionContext().setFeature( f );
348  QVariant res = mFilter->evaluate( &context.expressionContext() );
349  return res.toInt() != 0;
350 }
351 
352 
354 
355 
357  : mRootRule( root )
358 {
359 }
360 
362 {
363  delete mRootRule;
364 }
365 
367 {
368  Rule *rootRule = mRootRule->clone();
369 
370  // normally with clone() the individual rules get new keys (UUID), but here we want to keep
371  // the tree of rules intact, so that other components that may use the rule keys work nicely (e.g. map themes)
372  rootRule->setRuleKey( mRootRule->ruleKey() );
373  RuleList origDescendants = mRootRule->descendants();
374  RuleList clonedDescendants = rootRule->descendants();
375  Q_ASSERT( origDescendants.count() == clonedDescendants.count() );
376  for ( int i = 0; i < origDescendants.count(); ++i )
377  clonedDescendants[i]->setRuleKey( origDescendants[i]->ruleKey() );
378 
380  copyBaseProperties( r );
381  return r;
382 }
383 
384 Qt3DCore::QEntity *QgsRuleBased3DRenderer::createEntity( const Qgs3DMapSettings &map ) const
385 {
386  QgsVectorLayer *vl = layer();
387 
388  if ( !vl )
389  return nullptr;
390 
391  // we start with a maximal z range (based on a number similar to the radius of the Earth),
392  // because we can't know this upfront. There's too many
393  // factors to consider eg vertex z data, terrain heights, data defined offsets and extrusion heights,...
394  // This range will be refined after populating the nodes to the actual z range of the generated chunks nodes.
395  // Assuming the vertical height is in meter, then it's extremely unlikely that a real vertical
396  // height will exceed this amount!
397  constexpr double MINIMUM_VECTOR_Z_ESTIMATE = -5000000;
398  constexpr double MAXIMUM_VECTOR_Z_ESTIMATE = 5000000;
399 
400  return new QgsRuleBasedChunkedEntity( vl, MINIMUM_VECTOR_Z_ESTIMATE, MAXIMUM_VECTOR_Z_ESTIMATE, tilingSettings(), mRootRule, map );
401 }
402 
403 void QgsRuleBased3DRenderer::writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const
404 {
405  QDomDocument doc = elem.ownerDocument();
406 
407  writeXmlBaseProperties( elem, context );
408 
409  QDomElement rulesElem = mRootRule->save( doc, context );
410  rulesElem.setTagName( QStringLiteral( "rules" ) ); // instead of just "rule"
411  elem.appendChild( rulesElem );
412 }
413 
414 void QgsRuleBased3DRenderer::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
415 {
416  readXmlBaseProperties( elem, context );
417 
418  // root rule is read before class constructed
419 }
QgsRuleBased3DRenderer::Rule::create
static QgsRuleBased3DRenderer::Rule * create(const QDomElement &ruleElem, const QgsReadWriteContext &context)
Create a rule from an XML definition.
Definition: qgsrulebased3drenderer.cpp:180
QgsRuleBased3DRenderer::Rule::createHandlers
void createHandlers(QgsVectorLayer *layer, RuleToHandlerMap &handlers) const
add handlers
Definition: qgsrulebased3drenderer.cpp:249
QgsRuleBased3DRenderer::Rule::clone
QgsRuleBased3DRenderer::Rule * clone() const
clone this rule, return new instance
Definition: qgsrulebased3drenderer.cpp:169
QgsRuleBased3DRenderer::rootRule
QgsRuleBased3DRenderer::Rule * rootRule()
Returns pointer to the root rule.
Definition: qgsrulebased3drenderer.h:300
QgsRuleBased3DRenderer::Rule::findRuleByKey
const QgsRuleBased3DRenderer::Rule * findRuleByKey(const QString &key) const
Try to find a rule given its unique key.
Definition: qgsrulebased3drenderer.cpp:139
QgsRuleBased3DRenderer::Rule::descendants
QgsRuleBased3DRenderer::RuleList descendants() const
Returns all children, grand-children, grand-grand-children, grand-gra...
Definition: qgsrulebased3drenderer.cpp:79
QgsReadWriteContext
The class is used as a container of context for various read/write operations on other objects.
Definition: qgsreadwritecontext.h:34
qgsline3dsymbol.h
Qgs3DSymbolRegistry::createSymbol
QgsAbstract3DSymbol * createSymbol(const QString &type) const
Creates a new instance of a symbol of the specified type.
Definition: qgs3dsymbolregistry.cpp:37
QgsRuleBased3DRenderer::Rule::setSymbol
void setSymbol(QgsAbstract3DSymbol *symbol)
Sets new symbol (or nullptr). Deletes old symbol if any.
Definition: qgsrulebased3drenderer.cpp:71
QgsAbstract3DRenderer
Base class for all renderers that may to participate in 3D view.
Definition: qgsabstract3drenderer.h:48
QgsRuleBased3DRenderer::Rule::~Rule
~Rule()
Definition: qgsrulebased3drenderer.cpp:65
QgsAbstract3DSymbol::readXml
virtual void readXml(const QDomElement &elem, const QgsReadWriteContext &context)=0
Reads symbol configuration from the given DOM element.
QgsRuleBased3DRenderer::Rule::ruleKey
QString ruleKey() const
Unique rule identifier (for identification of rule within labeling, used as provider ID)
Definition: qgsrulebased3drenderer.h:127
QgsRuleBased3DRenderer::~QgsRuleBased3DRenderer
~QgsRuleBased3DRenderer() override
Definition: qgsrulebased3drenderer.cpp:361
QgsApplication::symbol3DRegistry
static Qgs3DSymbolRegistry * symbol3DRegistry()
Returns registry of available 3D symbols.
Definition: qgsapplication.cpp:2490
QgsRuleBased3DRenderer::clone
QgsRuleBased3DRenderer * clone() const override
Returns a cloned instance.
Definition: qgsrulebased3drenderer.cpp:366
Qgs3DRendererAbstractMetadata
Base metadata class for 3D renderers.
Definition: qgs3drendererregistry.h:34
QgsRuleBased3DRenderer::Rule::prepare
void prepare(const Qgs3DRenderContext &context, QSet< QString > &attributeNames, RuleToHandlerMap &handlers) const
call prepare() on handlers and populate attributeNames
Definition: qgsrulebased3drenderer.cpp:268
QgsRuleBased3DRendererMetadata::createRenderer
QgsAbstract3DRenderer * createRenderer(QDomElement &elem, const QgsReadWriteContext &context) override
Creates an instance of a 3D renderer based on a DOM element with renderer configuration.
Definition: qgsrulebased3drenderer.cpp:36
QgsRuleBased3DRenderer::Rule::removeChildAt
void removeChildAt(int i)
delete child rule
Definition: qgsrulebased3drenderer.cpp:132
QgsRuleBased3DRenderer::Rule::appendChild
void appendChild(QgsRuleBased3DRenderer::Rule *rule)
add child rule, take ownership, sets this as parent
Definition: qgsrulebased3drenderer.cpp:118
QgsAbstractVectorLayer3DRenderer::copyBaseProperties
void copyBaseProperties(QgsAbstractVectorLayer3DRenderer *r) const
Copies common properties of this object to another object.
Definition: qgsabstractvectorlayer3drenderer.cpp:58
QgsRuleBased3DRenderer::Rule::save
QDomElement save(QDomDocument &doc, const QgsReadWriteContext &context) const
store labeling info to XML element
Definition: qgsrulebased3drenderer.cpp:220
qgs3dsymbolregistry.h
QgsRuleBased3DRenderer::RuleToHandlerMap
QHash< const QgsRuleBased3DRenderer::Rule *, QgsFeature3DHandler * > RuleToHandlerMap
Definition: qgsrulebased3drenderer.h:67
QgsRuleBased3DRenderer::readXml
void readXml(const QDomElement &elem, const QgsReadWriteContext &context) override
Reads renderer's properties from given XML element.
Definition: qgsrulebased3drenderer.cpp:414
QgsRuleBased3DRenderer::Rule::insertChild
void insertChild(int i, QgsRuleBased3DRenderer::Rule *rule)
add child rule, take ownership, sets this as parent
Definition: qgsrulebased3drenderer.cpp:125
qgsapplication.h
QgsAbstractVectorLayer3DRenderer::readXmlBaseProperties
void readXmlBaseProperties(const QDomElement &elem, const QgsReadWriteContext &context)
Reads common properties of this object from DOM element.
Definition: qgsabstractvectorlayer3drenderer.cpp:71
QgsAbstract3DSymbol
Abstract base class for 3D symbols that are used by VectorLayer3DRenderer objects.
Definition: qgsabstract3dsymbol.h:46
QgsAbstractVectorLayer3DRenderer::tilingSettings
QgsVectorLayer3DTilingSettings tilingSettings() const
Returns tiling settings of the renderer.
Definition: qgsabstractvectorlayer3drenderer.h:90
QgsRuleBased3DRenderer::Rule::Rule
Rule(QgsAbstract3DSymbol *symbol, const QString &filterExp=QString(), const QString &description=QString(), bool elseRule=false)
takes ownership of symbol, symbol may be nullptr
Definition: qgsrulebased3drenderer.cpp:55
QgsRuleBased3DRenderer::Rule::setActive
void setActive(bool state)
Sets if this rule is active.
Definition: qgsrulebased3drenderer.h:150
qgs3dutils.h
QgsAbstractVectorLayer3DRenderer::layer
QgsVectorLayer * layer() const
Returns vector layer associated with the renderer.
Definition: qgsabstractvectorlayer3drenderer.cpp:53
QgsRuleBased3DRenderer::Rule::setRuleKey
void setRuleKey(const QString &key)
Override the assigned rule key (should be used just internally by rule-based renderer)
Definition: qgsrulebased3drenderer.h:161
QgsAbstract3DSymbol::clone
virtual QgsAbstract3DSymbol * clone() const =0
Returns a new instance of the symbol with the same settings.
Qgs3DMapSettings
Definition of the world.
Definition: qgs3dmapsettings.h:57
QgsRuleBased3DRenderer::writeXml
void writeXml(QDomElement &elem, const QgsReadWriteContext &context) const override
Writes renderer's properties to given XML element.
Definition: qgsrulebased3drenderer.cpp:403
qgsrulebasedchunkloader_p.h
QgsAbstractVectorLayer3DRenderer::writeXmlBaseProperties
void writeXmlBaseProperties(QDomElement &elem, const QgsReadWriteContext &context) const
Writes common properties of this object to DOM element.
Definition: qgsabstractvectorlayer3drenderer.cpp:64
qgsxmlutils.h
QgsRuleBased3DRenderer::RuleList
QList< QgsRuleBased3DRenderer::Rule * > RuleList
Definition: qgsrulebased3drenderer.h:65
qgsvectorlayer.h
QgsRuleBased3DRenderer::Rule::registerFeature
RegisterResult registerFeature(QgsFeature &feature, Qgs3DRenderContext &context, RuleToHandlerMap &handlers) const
register individual features
Definition: qgsrulebased3drenderer.cpp:293
QgsRuleBased3DRenderer::QgsRuleBased3DRenderer
QgsRuleBased3DRenderer(QgsRuleBased3DRenderer::Rule *root)
Construct renderer with the given root rule (takes ownership)
Definition: qgsrulebased3drenderer.cpp:356
qgs3dmapsettings.h
qgspolygon3dsymbol.h
qgsrulebased3drenderer.h
c
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
Definition: porting_processing.dox:1
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:391
QgsRuleBased3DRenderer::Rule
A child rule for a QgsRuleBased3DRenderer.
Definition: qgsrulebased3drenderer.h:74
qgspoint3dsymbol.h
QgsRuleBased3DRenderer::Rule::RegisterResult
RegisterResult
The result of registering a rule.
Definition: qgsrulebased3drenderer.h:87
QgsRuleBased3DRenderer
Rule-based 3D renderer.
Definition: qgsrulebased3drenderer.h:61
QgsFeature
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:55
Qgs3DSymbolRegistry::createHandlerForSymbol
QgsFeature3DHandler * createHandlerForSymbol(QgsVectorLayer *layer, const QgsAbstract3DSymbol *symbol)
Creates a feature handler for a symbol, for the specified vector layer.
Definition: qgs3dsymbolregistry.cpp:60
QgsExpression
Class for parsing and evaluation of expressions (formerly called "search strings")....
Definition: qgsexpression.h:102
QgsRuleBased3DRenderer::createEntity
Qt3DCore::QEntity * createEntity(const Qgs3DMapSettings &map) const override
Returns a 3D entity that will be used to show renderer's data in 3D scene.
Definition: qgsrulebased3drenderer.cpp:384
QgsRuleBased3DRendererMetadata::QgsRuleBased3DRendererMetadata
QgsRuleBased3DRendererMetadata()
Definition: qgsrulebased3drenderer.cpp:31