QGIS API Documentation 4.1.0-Master (9af12b5a203)
Loading...
Searching...
No Matches
qgscategorized3drenderer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgscategorized3drenderer.cpp
3 --------------------------------------
4 Date : November 2025
5 Copyright : (C) 2025 by Jean Felder
6 Email : jean dot felder at oslandia 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 "qgs3dsymbolutils.h"
23#include "qgs3dutils.h"
25#include "qgsabstract3dsymbol.h"
26#include "qgsapplication.h"
28#include "qgscolorrampimpl.h"
29#include "qgssymbollayerutils.h"
30#include "qgsvectorlayer.h"
31#include "qgsxmlutils.h"
32
33#include <QString>
34
35using namespace Qt::StringLiterals;
36
40
42{
44 renderer->readXml( elem, context );
45 return renderer;
46}
47
48
49// ---------
50
51
53
55 : mValue( value )
56 , mSymbol( symbol )
57 , mRender( render )
58{}
59
61 : Qgs3DRendererCategory( other.mValue, other.mSymbol ? other.mSymbol->clone() : nullptr, other.mRender )
62{}
63
65{
66 if ( this != &other )
67 {
68 mValue = other.mValue;
69 mSymbol = other.mSymbol ? std::unique_ptr<QgsAbstract3DSymbol>( other.mSymbol->clone() ) : nullptr;
70 mRender = other.mRender;
71 }
72
73 return *this;
74}
75
77{
78 mRender = render;
79}
80
82{
83 mValue = value;
84}
85
87{
88 if ( mSymbol.get() != symbol )
89 {
90 mSymbol.reset( symbol );
91 }
92}
93
94// ---------
95
96
98
99
101 : mAttributeName( attributeName )
102 , mCategories( categories )
103{}
104
106{
107 QgsCategorized3DRenderer *renderer = new QgsCategorized3DRenderer( mAttributeName, mCategories );
108
109 if ( mSourceSymbol )
110 {
111 renderer->setSourceSymbol( mSourceSymbol->clone() );
112 }
113
114 if ( mSourceColorRamp )
115 {
116 renderer->setSourceColorRamp( mSourceColorRamp->clone() );
117 }
118
119 copyBaseProperties( renderer );
120 return renderer;
121}
122
123Qt3DCore::QEntity *QgsCategorized3DRenderer::createEntity( Qgs3DMapSettings *mapSettings ) const
124{
125 QgsVectorLayer *vectorLayer = layer();
126
127 if ( !vectorLayer )
128 {
129 return nullptr;
130 }
131
132 return new QgsCategorizedChunkedEntity( mapSettings, vectorLayer, Qgs3DUtils::MINIMUM_VECTOR_Z_ESTIMATE, Qgs3DUtils::MAXIMUM_VECTOR_Z_ESTIMATE, tilingSettings(), this );
133}
134
136{
137 int catIndex = 0;
138 for ( const Qgs3DRendererCategory &cat : mCategories )
139 {
140 std::unique_ptr<QgsAbstract3DSymbol> newSymbol( symbol->clone() );
141 Qgs3DSymbolUtils::copyVectorSymbolMaterial( cat.symbol(), newSymbol.get() );
142 updateCategorySymbol( catIndex, newSymbol.release() );
143 ++catIndex;
144 }
145 setSourceSymbol( symbol->clone() );
146}
147
149{
150 mAttributeName = attributeName;
151}
152
154{
155 return mSourceSymbol.get();
156}
157
159{
160 return mSourceSymbol.get();
161}
162
164{
165 mSourceSymbol.reset( symbol );
166}
167
169{
170 return mSourceColorRamp.get();
171}
172
174{
175 return mSourceColorRamp.get();
176}
177
179{
180 mSourceColorRamp.reset( ramp );
181}
182
184{
185 setSourceColorRamp( ramp );
186 const double num = static_cast<double>( mCategories.count() - 1 );
187 double count = 0.;
188
189 QgsRandomColorRamp *randomRamp = dynamic_cast<QgsRandomColorRamp *>( ramp );
190 if ( randomRamp )
191 {
192 //ramp is a random colors ramp, so inform it of the total number of required colors
193 //this allows the ramp to pregenerate a set of visually distinctive colors
194 randomRamp->setTotalColorCount( static_cast<int>( mCategories.count() ) );
195 }
196
197 for ( const Qgs3DRendererCategory &cat : mCategories )
198 {
199 Qgs3DSymbolUtils::setVectorSymbolBaseColor( cat.symbol(), mSourceColorRamp->color( count / num ) );
200 count += 1.;
201 }
202}
203
204bool QgsCategorized3DRenderer::updateCategoryValue( int catIndex, const QVariant &value )
205{
206 if ( catIndex < 0 || catIndex >= mCategories.size() )
207 {
208 return false;
209 }
210
211 mCategories[catIndex].setValue( value );
212 return true;
213}
214
216{
217 if ( catIndex < 0 || catIndex >= mCategories.size() )
218 {
219 return false;
220 }
221
222 mCategories[catIndex].setSymbol( symbol );
223 return true;
224}
225
227{
228 if ( catIndex < 0 || catIndex >= mCategories.size() )
229 {
230 return false;
231 }
232
233 mCategories[catIndex].setRenderState( render );
234 return true;
235}
236
238{
239 mCategories.append( category );
240}
241
243{
244 if ( catIndex < 0 || catIndex >= mCategories.size() )
245 {
246 return false;
247 }
248
249 mCategories.removeAt( catIndex );
250 return true;
251}
252
254{
255 mCategories.clear();
256}
257
259{
260 if ( from < 0 || from >= mCategories.size() || to < 0 || to >= mCategories.size() )
261 {
262 return false;
263 }
264
265 mCategories.move( from, to );
266 return true;
267}
268
269void QgsCategorized3DRenderer::sortByValue( Qt::SortOrder order )
270{
271 std::sort( mCategories.begin(), mCategories.end(), [order]( const Qgs3DRendererCategory &cat1, const Qgs3DRendererCategory &cat2 ) {
272 bool less = qgsVariantLessThan( cat1.value(), cat2.value() );
273 return ( order == Qt::AscendingOrder ) ? less : !less;
274 } );
275}
276
277void QgsCategorized3DRenderer::writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const
278{
279 QDomDocument doc = elem.ownerDocument();
280
281 writeXmlBaseProperties( elem, context );
282
283 elem.setAttribute( u"attribute"_s, mAttributeName );
284
285 // save categories
286 if ( !mCategories.isEmpty() )
287 {
288 QDomElement catsElem = doc.createElement( u"categories"_s );
289 for ( const Qgs3DRendererCategory &category : mCategories )
290 {
291 QDomElement catElem = doc.createElement( u"category"_s );
292 QDomElement valueElem = QgsXmlUtils::writeVariant( category.value(), doc );
293 catElem.appendChild( valueElem );
294 catElem.setAttribute( u"render"_s, category.renderState() ? 1 : 0 );
295
296 QDomElement symbolElem = doc.createElement( u"symbol"_s );
297 symbolElem.setAttribute( u"type"_s, category.symbol()->type() );
298 category.symbol()->writeXml( symbolElem, context );
299 catElem.appendChild( symbolElem );
300
301 catsElem.appendChild( catElem );
302 }
303 elem.appendChild( catsElem );
304 }
305
306 // save source symbol
307 if ( mSourceSymbol )
308 {
309 QDomElement sourceSymbolElem = doc.createElement( u"source-symbol"_s );
310 sourceSymbolElem.setAttribute( u"type"_s, mSourceSymbol->type() );
311 mSourceSymbol->writeXml( sourceSymbolElem, context );
312 elem.appendChild( sourceSymbolElem );
313 }
314
315 // save source color ramp
316 if ( mSourceColorRamp )
317 {
318 QDomElement colorRampElem = QgsSymbolLayerUtils::saveColorRamp( u"[source]"_s, mSourceColorRamp.get(), doc );
319 elem.appendChild( colorRampElem );
320 }
321}
322
323void QgsCategorized3DRenderer::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
324{
325 readXmlBaseProperties( elem, context );
326
327 mAttributeName = elem.attribute( u"attribute"_s );
328
329 // load categories
330 QDomElement catsElem = elem.firstChildElement( u"categories"_s );
331 if ( !catsElem.isNull() )
332 {
333 QDomElement catElem = catsElem.firstChildElement();
334 while ( !catElem.isNull() )
335 {
336 if ( catElem.tagName() == "category"_L1 )
337 {
338 const QVariant value = QgsXmlUtils::readVariant( catElem.firstChildElement( u"Option"_s ) );
339 const bool render = static_cast<bool>( catElem.attribute( u"render"_s, u"0"_s ).toInt() );
340
341 QgsAbstract3DSymbol *symbol = nullptr;
342 QDomElement symbolElem = catElem.firstChildElement( u"symbol"_s );
343 if ( !symbolElem.isNull() )
344 {
345 QString symbolType = symbolElem.attribute( u"type"_s );
346 symbol = QgsApplication::symbol3DRegistry()->createSymbol( symbolType );
347 if ( symbol )
348 {
349 symbol->readXml( symbolElem, context );
350 }
351 }
352
353 mCategories.append( Qgs3DRendererCategory( value, symbol, render ) );
354 }
355 catElem = catElem.nextSiblingElement();
356 }
357 }
358
359 // load source symbol
360 QDomElement sourceSymbolElem = elem.firstChildElement( u"source-symbol"_s );
361 if ( !sourceSymbolElem.isNull() )
362 {
363 QString symbolType = sourceSymbolElem.attribute( u"type"_s );
365 if ( sourceSymbol )
366 {
367 sourceSymbol->readXml( sourceSymbolElem, context );
368 mSourceSymbol.reset( sourceSymbol );
369 }
370 }
371
372 // load source color ramp
373 QDomElement sourceColorRampElem = elem.firstChildElement( u"colorramp"_s );
374 if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( u"name"_s ) == "[source]"_L1 )
375 {
376 mSourceColorRamp = QgsSymbolLayerUtils::loadColorRamp( sourceColorRampElem );
377 }
378}
379
380Qgs3DCategoryList QgsCategorized3DRenderer::createCategories( const QList<QVariant> &values, const QgsAbstract3DSymbol *symbol, QgsVectorLayer *layer, const QString &attributeName )
381{
383 QVariantList vals = values;
384 // sort the categories first
385 QgsSymbolLayerUtils::sortVariantList( vals, Qt::AscendingOrder );
386
387 if ( layer && !attributeName.isNull() )
388 {
389 const QgsFields fields = layer->fields();
390 for ( const QVariant &value : vals )
391 {
392 if ( !QgsVariantUtils::isNull( value ) )
393 {
394 std::unique_ptr<QgsAbstract3DSymbol> newSymbol( symbol->clone() );
395 cats.append( Qgs3DRendererCategory( value, newSymbol.release(), true ) );
396 }
397 }
398 }
399
400 // add null (default) value
401 std::unique_ptr<QgsAbstract3DSymbol> newSymbol( symbol->clone() );
402 cats.append( Qgs3DRendererCategory( QVariant(), newSymbol.release(), true ) );
403
404 return cats;
405}
Definition of the world.
Qgs3DRendererAbstractMetadata(const QString &type)
Constructor of the base class.
Represents an individual category (class) from a QgsCategorized3DRenderer.
Qgs3DRendererCategory()=default
const QVariant value() const
Returns the value corresponding to this category.
void setRenderState(bool render)
Sets whether the category is currently enabled and should be rendered.
void setValue(const QVariant &value)
Sets the value corresponding to this category.
Qgs3DRendererCategory & operator=(const Qgs3DRendererCategory &other)
void setSymbol(QgsAbstract3DSymbol *symbol)
Sets the symbol which will be used to render this category.
QgsAbstract3DSymbol * symbol() const
Returns the symbol which will be used to render this category.
QgsAbstract3DSymbol * createSymbol(const QString &type) const
Creates a new instance of a symbol of the specified type.
static bool copyVectorSymbolMaterial(const QgsAbstract3DSymbol *fromSymbol, QgsAbstract3DSymbol *toSymbol)
Copies the material properties of a vector 3D symbol from one symbol to another.
static bool setVectorSymbolBaseColor(QgsAbstract3DSymbol *symbol, const QColor &baseColor)
Sets the base color of the material settings associated with a 3D vector symbol.
static constexpr double MINIMUM_VECTOR_Z_ESTIMATE
Definition qgs3dutils.h:522
static constexpr double MAXIMUM_VECTOR_Z_ESTIMATE
Definition qgs3dutils.h:523
Base class for all renderers that participate in 3D views.
Abstract base class for 3D symbols that are used by VectorLayer3DRenderer objects.
virtual void readXml(const QDomElement &elem, const QgsReadWriteContext &context)=0
Reads symbol configuration from the given DOM element.
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.
QgsAbstract3DRenderer * createRenderer(QDomElement &elem, const QgsReadWriteContext &context) override
Creates an instance of a 3D renderer Categorized on a DOM element with renderer configuration.
bool updateCategorySymbol(int catIndex, QgsAbstract3DSymbol *symbol)
Changes the symbol for the category with the specified index.
QgsAbstract3DSymbol * sourceSymbol()
Returns the renderer's source symbol, which is the base symbol used for the each categories' symbol b...
bool updateCategoryValue(int catIndex, const QVariant &value)
Changes the value for the category with the specified index.
static Qgs3DCategoryList createCategories(const QVariantList &values, const QgsAbstract3DSymbol *symbol, QgsVectorLayer *layer=nullptr, const QString &attributeName=QString())
Create categories for a list of values.
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.
void updateColorRamp(QgsColorRamp *ramp)
Update the color ramp used and all symbols colors.
QgsCategorized3DRenderer(const QString &attributeName=QString(), const Qgs3DCategoryList &categories=Qgs3DCategoryList())
Construct renderer with the given categories.
QgsColorRamp * sourceColorRamp()
Returns the source color ramp, from which each categories' color is derived.
void addCategory(const Qgs3DRendererCategory &category)
Adds a new category to the renderer.
void setSourceSymbol(QgsAbstract3DSymbol *symbol)
Sets the source symbol for the renderer, which is the base symbol used for the each categories' symbo...
void deleteAllCategories()
Deletes all existing categories from the renderer.
const Qgs3DCategoryList & categories() const
Returns a list of all categories recognized by the renderer.
void setClassAttribute(QString attributeName)
Sets the class attribute for the renderer, which is the field name or expression string from the laye...
Qt3DCore::QEntity * createEntity(Qgs3DMapSettings *mapSettings) const override
Returns a 3D entity that will be used to show renderer's data in 3D scene.
void setSourceColorRamp(QgsColorRamp *ramp)
Sets the source color ramp.
bool moveCategory(int from, int to)
Moves an existing category at index position from to index position to.
void sortByValue(Qt::SortOrder order=Qt::AscendingOrder)
Sorts the existing categories by their value.
bool updateCategoryRenderState(int catIndex, bool render)
Changes the render state for the category with the specified index.
QgsCategorized3DRenderer * clone() const override
Returns a cloned instance.
void updateSymbols(QgsAbstract3DSymbol *symbol)
Update all the symbols but leave categories and colors.
bool deleteCategory(int catIndex)
Deletes the category with the specified index from the renderer.
Abstract base class for color ramps.
Container of fields for a vector layer.
Definition qgsfields.h:46
A color ramp consisting of random colors, constrained within component ranges.
virtual void setTotalColorCount(int colorCount)
Sets the desired total number of unique colors for the resultant ramp.
A container for the context for various read/write operations on objects.
static void sortVariantList(QList< QVariant > &list, Qt::SortOrder order)
Sorts the passed list in requested order.
static std::unique_ptr< QgsColorRamp > loadColorRamp(QDomElement &element)
Creates a color ramp from the settings encoded in an XML element.
static QDomElement saveColorRamp(const QString &name, const QgsColorRamp *ramp, QDomDocument &doc)
Encodes a color ramp's settings to an XML element.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
Represents a vector layer which manages a vector based dataset.
static QDomElement writeVariant(const QVariant &value, QDomDocument &doc)
Write a QVariant to a QDomElement.
static QVariant readVariant(const QDomElement &element)
Read a QVariant from a QDomElement.
QList< Qgs3DRendererCategory > Qgs3DCategoryList