QGIS API Documentation 4.1.0-Master (376402f9aeb)
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"
28#include "qgsxmlutils.h"
29
30#include <QString>
31
32using namespace Qt::StringLiterals;
33
37
39{
40 QDomElement rulesElem = elem.firstChildElement( u"rules"_s );
41
43 if ( !root )
44 return nullptr;
45
47 r->readXml( elem, context );
48 return r;
49}
50
51
52// ---------
53
54
56
57QgsRuleBased3DRenderer::Rule::Rule( QgsAbstract3DSymbol *symbol, const QString &filterExp, const QString &description, bool elseRule )
58 : mSymbol( symbol )
59 , mFilterExp( filterExp )
60 , mDescription( description )
61 , mElseRule( elseRule )
62
63{
64 initFilter();
65}
66
68{
69 qDeleteAll( mChildren );
70 // do NOT delete parent
71}
72
74{
75 if ( mSymbol.get() == symbol )
76 return;
77
78 mSymbol.reset( symbol );
79}
80
82{
83 RuleList l;
84 for ( Rule *c : mChildren )
85 {
86 l += c;
87 l += c->descendants();
88 }
89 return l;
90}
91
92void QgsRuleBased3DRenderer::Rule::initFilter()
93{
94 if ( mElseRule || mFilterExp.compare( "ELSE"_L1, Qt::CaseInsensitive ) == 0 )
95 {
96 mElseRule = true;
97 mFilter.reset( nullptr );
98 }
99 else if ( !mFilterExp.isEmpty() )
100 {
101 mFilter = std::make_unique<QgsExpression>( mFilterExp );
102 }
103 else
104 {
105 mFilter.reset( nullptr );
106 }
107}
108
109void QgsRuleBased3DRenderer::Rule::updateElseRules()
110{
111 mElseRules.clear();
112 for ( Rule *rule : std::as_const( mChildren ) )
113 {
114 if ( rule->isElse() )
115 mElseRules << rule;
116 }
117}
118
119
121{
122 mChildren.append( rule );
123 rule->mParent = this;
124 updateElseRules();
125}
126
128{
129 mChildren.insert( i, rule );
130 rule->mParent = this;
131 updateElseRules();
132}
133
135{
136 delete mChildren.at( i );
137 mChildren.removeAt( i );
138 updateElseRules();
139}
140
142{
143 // we could use a hash / map for search if this will be slow...
144
145 if ( key == mRuleKey )
146 return this;
147
148 for ( Rule *rule : std::as_const( mChildren ) )
149 {
150 const Rule *r = rule->findRuleByKey( key );
151 if ( r )
152 return r;
153 }
154 return nullptr;
155}
156
158{
159 if ( key == mRuleKey )
160 return this;
161
162 for ( Rule *rule : std::as_const( mChildren ) )
163 {
164 Rule *r = rule->findRuleByKey( key );
165 if ( r )
166 return r;
167 }
168 return nullptr;
169}
170
172{
173 QgsAbstract3DSymbol *symbol = mSymbol.get() ? mSymbol->clone() : nullptr;
174 Rule *newrule = new Rule( symbol, mFilterExp, mDescription );
175 newrule->setActive( mIsActive );
176 // clone children
177 for ( Rule *rule : std::as_const( mChildren ) )
178 newrule->appendChild( rule->clone() );
179 return newrule;
180}
181
183{
184 QgsAbstract3DSymbol *symbol = nullptr;
185 QDomElement elemSymbol = ruleElem.firstChildElement( u"symbol"_s );
186 if ( !elemSymbol.isNull() )
187 {
188 QString symbolType = elemSymbol.attribute( u"type"_s );
190 if ( symbol )
191 symbol->readXml( elemSymbol, context );
192 }
193
194 QString filterExp = ruleElem.attribute( u"filter"_s );
195 QString description = ruleElem.attribute( u"description"_s );
196 QString ruleKey = ruleElem.attribute( u"key"_s );
197 Rule *rule = new Rule( symbol, filterExp, description );
198
199 if ( !ruleKey.isEmpty() )
200 rule->mRuleKey = ruleKey;
201
202 rule->setActive( ruleElem.attribute( u"active"_s, u"1"_s ).toInt() );
203
204 QDomElement childRuleElem = ruleElem.firstChildElement( u"rule"_s );
205 while ( !childRuleElem.isNull() )
206 {
207 Rule *childRule = create( childRuleElem, context );
208 if ( childRule )
209 {
210 rule->appendChild( childRule );
211 }
212 else
213 {
214 //QgsDebugError( u"failed to init a child rule!"_s );
215 }
216 childRuleElem = childRuleElem.nextSiblingElement( u"rule"_s );
217 }
218
219 return rule;
220}
221
222QDomElement QgsRuleBased3DRenderer::Rule::save( QDomDocument &doc, const QgsReadWriteContext &context ) const
223{
224 QDomElement ruleElem = doc.createElement( u"rule"_s );
225
226 if ( mSymbol )
227 {
228 QDomElement elemSymbol = doc.createElement( u"symbol"_s );
229 elemSymbol.setAttribute( u"type"_s, mSymbol->type() );
230 mSymbol->writeXml( elemSymbol, context );
231 ruleElem.appendChild( elemSymbol );
232 }
233
234 if ( !mFilterExp.isEmpty() )
235 ruleElem.setAttribute( u"filter"_s, mFilterExp );
236 if ( !mDescription.isEmpty() )
237 ruleElem.setAttribute( u"description"_s, mDescription );
238 if ( !mIsActive )
239 ruleElem.setAttribute( u"active"_s, 0 );
240 ruleElem.setAttribute( u"key"_s, mRuleKey );
241
242 for ( RuleList::const_iterator it = mChildren.constBegin(); it != mChildren.constEnd(); ++it )
243 {
244 Rule *rule = *it;
245 ruleElem.appendChild( rule->save( doc, context ) );
246 }
247 return ruleElem;
248}
249
250
252{
253 if ( mSymbol )
254 {
255 // add handler!
256 Q_ASSERT( !handlers.value( this ) );
257 QgsFeature3DHandler *handler = QgsApplication::symbol3DRegistry()->createHandlerForSymbol( layer, mSymbol.get() );
258 if ( handler )
259 handlers[this] = handler;
260 }
261
262 // call recursively
263 for ( Rule *rule : std::as_const( mChildren ) )
264 {
265 rule->createHandlers( layer, handlers );
266 }
267}
268
269
270void QgsRuleBased3DRenderer::Rule::prepare( const Qgs3DRenderContext &context, QSet<QString> &attributeNames, const QgsBox3D &chunkExtent, QgsRuleBased3DRenderer::RuleToHandlerMap &handlers ) const
271{
272 if ( mSymbol )
273 {
274 QgsFeature3DHandler *handler = handlers[this];
275 if ( !handler->prepare( context, attributeNames, chunkExtent ) )
276 {
277 handlers.remove( this );
278 delete handler;
279 }
280 }
281
282 if ( mFilter )
283 {
284 attributeNames.unite( mFilter->referencedColumns() );
285 mFilter->prepare( &context.expressionContext() );
286 }
287
288 // call recursively
289 for ( Rule *rule : std::as_const( mChildren ) )
290 {
291 rule->prepare( context, attributeNames, chunkExtent, handlers );
292 }
293}
294
296 const QgsFeature &feature, Qgs3DRenderContext &context, const QgsRuleBased3DRenderer::RuleToHandlerMap &handlers
297) const
298{
299 if ( !isFilterOK( feature, context ) )
300 return Filtered;
301
302 bool registered = false;
303
304 // do we have active handler for the rule?
305 if ( handlers.contains( this ) && mIsActive )
306 {
307 handlers[this]->processFeature( feature, context );
308 registered = true;
309 }
310
311 bool matchedAChild = false;
312
313 // call recursively
314 for ( Rule *rule : std::as_const( mChildren ) )
315 {
316 // Don't process else rules yet
317 if ( !rule->isElse() )
318 {
319 const RegisterResult res = rule->registerFeature( feature, context, handlers );
320 // consider inactive items as "matched" so the else rule will ignore them
321 matchedAChild |= ( res == Registered || res == Inactive );
322 registered |= matchedAChild;
323 }
324 }
325
326 // If none of the rules passed then we jump into the else rules and process them.
327 if ( !matchedAChild )
328 {
329 for ( Rule *rule : std::as_const( mElseRules ) )
330 {
331 const RegisterResult res = rule->registerFeature( feature, context, handlers );
332 matchedAChild |= ( res == Registered || res == Inactive );
333 registered |= res != Filtered;
334 }
335 }
336
337 if ( !mIsActive || ( matchedAChild && !registered ) )
338 return Inactive;
339 else if ( registered )
340 return Registered;
341 else
342 return Filtered;
343}
344
345
346bool QgsRuleBased3DRenderer::Rule::isFilterOK( const QgsFeature &f, Qgs3DRenderContext &context ) const
347{
348 if ( !mFilter || mElseRule )
349 return true;
350
351 context.expressionContext().setFeature( f );
352 QVariant res = mFilter->evaluate( &context.expressionContext() );
353 return res.toInt() != 0;
354}
355
356
358
359
363
365{
366 delete mRootRule;
367}
368
370{
371 Rule *rootRule = mRootRule->clone();
372
373 // normally with clone() the individual rules get new keys (UUID), but here we want to keep
374 // the tree of rules intact, so that other components that may use the rule keys work nicely (e.g. map themes)
375 rootRule->setRuleKey( mRootRule->ruleKey() );
376 RuleList origDescendants = mRootRule->descendants();
377 RuleList clonedDescendants = rootRule->descendants();
378 Q_ASSERT( origDescendants.count() == clonedDescendants.count() );
379 for ( int i = 0; i < origDescendants.count(); ++i )
380 clonedDescendants[i]->setRuleKey( origDescendants[i]->ruleKey() );
381
384 return r;
385}
386
388{
389 QgsVectorLayer *vl = layer();
390
391 if ( !vl )
392 return nullptr;
393
394 return new QgsRuleBasedChunkedEntity( map, vl, Qgs3DUtils::MINIMUM_VECTOR_Z_ESTIMATE, Qgs3DUtils::MAXIMUM_VECTOR_Z_ESTIMATE, tilingSettings(), mRootRule );
395}
396
397void QgsRuleBased3DRenderer::writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const
398{
399 QDomDocument doc = elem.ownerDocument();
400
401 writeXmlBaseProperties( elem, context );
402
403 QDomElement rulesElem = mRootRule->save( doc, context );
404 rulesElem.setTagName( u"rules"_s ); // instead of just "rule"
405 elem.appendChild( rulesElem );
406}
407
408void QgsRuleBased3DRenderer::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
409{
410 readXmlBaseProperties( elem, context );
411
412 // root rule is read before class constructed
413}
414
415std::unique_ptr<QgsRuleBased3DRenderer> QgsRuleBased3DRenderer::convertFromRenderer( const QgsAbstractVectorLayer3DRenderer *renderer, QgsVectorLayer * )
416{
417 std::unique_ptr< QgsRuleBased3DRenderer > r;
418 if ( renderer->type() == "rulebased"_L1 )
419 {
420 r.reset( dynamic_cast<const QgsRuleBased3DRenderer *>( renderer )->clone() );
421 }
422 else if ( renderer->type() == "vector"_L1 )
423 {
424 const QgsVectorLayer3DRenderer *singleSymbolRenderer = dynamic_cast<const QgsVectorLayer3DRenderer *>( renderer );
425 if ( !singleSymbolRenderer )
426 return nullptr;
427
428 std::unique_ptr< QgsAbstract3DSymbol > origSymbol( singleSymbolRenderer->symbol()->clone() );
429 auto rootRule = std::make_unique< Rule >( nullptr );
430 rootRule->appendChild( new Rule( origSymbol.release() ) );
431
432 r = std::make_unique< QgsRuleBased3DRenderer >( rootRule.release() );
433 }
434
435 if ( r )
436 {
437 renderer->copyBaseProperties( r.get() );
438 }
439
440 return r;
441}
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.
static constexpr double MINIMUM_VECTOR_Z_ESTIMATE
Definition qgs3dutils.h:455
static constexpr double MAXIMUM_VECTOR_Z_ESTIMATE
Definition qgs3dutils.h:456
Base class for all renderers that participate in 3D views.
virtual QString type() const =0
Returns unique identifier of the renderer class (used to identify subclass).
Abstract base class for 3D symbols that are used by VectorLayer3DRenderer objects.
virtual QgsAbstract3DSymbol * clone() const =0
Returns a new instance of the symbol with the same settings.
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.
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:45
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:60
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 prepare(const Qgs3DRenderContext &context, QSet< QString > &attributeNames, const QgsBox3D &chunkExtent, RuleToHandlerMap &handlers) const
call prepare() on handlers and populate attributeNames
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).
RegisterResult
The result of registering a rule.
@ Registered
Something was registered.
@ Filtered
The rule does not apply.
RegisterResult registerFeature(const QgsFeature &feature, Qgs3DRenderContext &context, const 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
static std::unique_ptr< QgsRuleBased3DRenderer > convertFromRenderer(const QgsAbstractVectorLayer3DRenderer *renderer, QgsVectorLayer *layer=nullptr)
Creates a new QgsRuleBased3DRenderer from an existing renderer.
3D renderer that renders all features of a vector layer with the same 3D symbol.
const QgsAbstract3DSymbol * symbol() const
Returns 3D symbol associated with the renderer.
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