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