QGIS API Documentation 3.27.0-Master (95e00c50d2)
qgssinglesymbolrenderer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgssinglesymbolrenderer.cpp
3 ---------------------
4 begin : November 2009
5 copyright : (C) 2009 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 "qgssymbol.h"
19#include "qgssymbollayerutils.h"
20
22#include "qgslogger.h"
23#include "qgsfeature.h"
24#include "qgsvectorlayer.h"
25#include "qgssymbollayer.h"
26#include "qgsogcutils.h"
29#include "qgspainteffect.h"
31#include "qgsproperty.h"
33#include "qgslinesymbol.h"
34#include "qgsmarkersymbol.h"
35#include "qgsfillsymbol.h"
36
37#include <QDomDocument>
38#include <QDomElement>
39
41 : QgsFeatureRenderer( QStringLiteral( "singleSymbol" ) )
42 , mSymbol( symbol )
43{
44 Q_ASSERT( symbol );
45}
46
48
50{
51 return mSymbol.get();
52}
53
55{
56 Q_UNUSED( context )
57 Q_UNUSED( feature )
58 return mSymbol.get();
59}
60
62{
63 QgsFeatureRenderer::startRender( context, fields );
64
65 if ( !mSymbol )
66 return;
67
68 mSymbol->startRender( context, fields );
69}
70
72{
74
75 if ( !mSymbol )
76 return;
77
78 mSymbol->stopRender( context );
79}
80
81QSet<QString> QgsSingleSymbolRenderer::usedAttributes( const QgsRenderContext &context ) const
82{
83 QSet<QString> attributes;
84 if ( mSymbol )
85 attributes.unite( mSymbol->usedAttributes( context ) );
86 return attributes;
87}
88
90{
91 if ( mSymbol )
92 {
93 QgsStyleSymbolEntity entity( mSymbol.get() );
94 return visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity ) );
95 }
96 return true;
97}
98
100{
101 return mSymbol.get();
102}
103
105{
106 Q_ASSERT( s );
107 mSymbol.reset( s );
108}
109
111{
112 return mSymbol ? QStringLiteral( "SINGLE: %1" ).arg( mSymbol->dump() ) : QString();
113}
114
116{
119 copyRendererData( r );
120 return r;
121}
122
123void QgsSingleSymbolRenderer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
124{
125 QVariantMap newProps = props;
126
127 QDomElement ruleElem = doc.createElement( QStringLiteral( "se:Rule" ) );
128 element.appendChild( ruleElem );
129
130 QDomElement nameElem = doc.createElement( QStringLiteral( "se:Name" ) );
131 nameElem.appendChild( doc.createTextNode( QStringLiteral( "Single symbol" ) ) );
132 ruleElem.appendChild( nameElem );
133
134 QgsSymbolLayerUtils::applyScaleDependency( doc, ruleElem, newProps );
135
136 if ( mSymbol ) mSymbol->toSld( doc, ruleElem, newProps );
137}
138
140{
141 Q_UNUSED( context )
142 QgsSymbolList lst;
143 lst.append( mSymbol.get() );
144 return lst;
145}
146
147
149{
150 QDomElement symbolsElem = element.firstChildElement( QStringLiteral( "symbols" ) );
151 if ( symbolsElem.isNull() )
152 return nullptr;
153
154 QgsSymbolMap symbolMap = QgsSymbolLayerUtils::loadSymbols( symbolsElem, context );
155
156 if ( !symbolMap.contains( QStringLiteral( "0" ) ) )
157 return nullptr;
158
159 QgsSingleSymbolRenderer *r = new QgsSingleSymbolRenderer( symbolMap.take( QStringLiteral( "0" ) ) );
160
161 // delete symbols if there are any more
163
164 const QDomElement rotationElem = element.firstChildElement( QStringLiteral( "rotation" ) );
165 if ( !rotationElem.isNull() && !rotationElem.attribute( QStringLiteral( "field" ) ).isEmpty() )
166 {
167 convertSymbolRotation( r->mSymbol.get(), rotationElem.attribute( QStringLiteral( "field" ) ) );
168 }
169
170 const QDomElement sizeScaleElem = element.firstChildElement( QStringLiteral( "sizescale" ) );
171 if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( QStringLiteral( "field" ) ).isEmpty() )
172 {
174 QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( QStringLiteral( "scalemethod" ) ) ),
175 sizeScaleElem.attribute( QStringLiteral( "field" ) ) );
176 }
177
178 const QDomElement ddsLegendSizeElem = element.firstChildElement( QStringLiteral( "data-defined-size-legend" ) );
179 if ( !ddsLegendSizeElem.isNull() )
180 {
181 r->mDataDefinedSizeLegend.reset( QgsDataDefinedSizeLegend::readXml( ddsLegendSizeElem, context ) );
182 }
183
184 // TODO: symbol levels
185 return r;
186}
187
189{
190 // XXX this renderer can handle only one Rule!
191
192 // get the first Rule element
193 const QDomElement ruleElem = element.firstChildElement( QStringLiteral( "Rule" ) );
194 if ( ruleElem.isNull() )
195 {
196 QgsDebugMsg( QStringLiteral( "no Rule elements found!" ) );
197 return nullptr;
198 }
199
200 QString label, description;
201 QgsSymbolLayerList layers;
202
203 // retrieve the Rule element child nodes
204 QDomElement childElem = ruleElem.firstChildElement();
205 while ( !childElem.isNull() )
206 {
207 if ( childElem.localName() == QLatin1String( "Name" ) )
208 {
209 // <se:Name> tag contains the rule identifier,
210 // so prefer title tag for the label property value
211 if ( label.isEmpty() )
212 label = childElem.firstChild().nodeValue();
213 }
214 else if ( childElem.localName() == QLatin1String( "Description" ) )
215 {
216 // <se:Description> can contains a title and an abstract
217 const QDomElement titleElem = childElem.firstChildElement( QStringLiteral( "Title" ) );
218 if ( !titleElem.isNull() )
219 {
220 label = titleElem.firstChild().nodeValue();
221 }
222
223 const QDomElement abstractElem = childElem.firstChildElement( QStringLiteral( "Abstract" ) );
224 if ( !abstractElem.isNull() )
225 {
226 description = abstractElem.firstChild().nodeValue();
227 }
228 }
229 else if ( childElem.localName() == QLatin1String( "Abstract" ) )
230 {
231 // <sld:Abstract> (v1.0)
232 description = childElem.firstChild().nodeValue();
233 }
234 else if ( childElem.localName() == QLatin1String( "Title" ) )
235 {
236 // <sld:Title> (v1.0)
237 label = childElem.firstChild().nodeValue();
238 }
239 else if ( childElem.localName().endsWith( QLatin1String( "Symbolizer" ) ) )
240 {
241 // create symbol layers for this symbolizer
242 QgsSymbolLayerUtils::createSymbolLayerListFromSld( childElem, geomType, layers );
243 }
244
245 childElem = childElem.nextSiblingElement();
246 }
247
248 if ( layers.isEmpty() )
249 return nullptr;
250
251 // now create the symbol
252 std::unique_ptr< QgsSymbol > symbol;
253 switch ( geomType )
254 {
256 symbol = std::make_unique< QgsLineSymbol >( layers );
257 break;
258
260 symbol = std::make_unique< QgsFillSymbol >( layers );
261 break;
262
264 symbol = std::make_unique< QgsMarkerSymbol >( layers );
265 break;
266
267 default:
268 QgsDebugMsg( QStringLiteral( "invalid geometry type: found %1" ).arg( geomType ) );
269 return nullptr;
270 }
271
272 // and finally return the new renderer
273 return new QgsSingleSymbolRenderer( symbol.release() );
274}
275
276QDomElement QgsSingleSymbolRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
277{
278 QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
279 rendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "singleSymbol" ) );
280
282 symbols[QStringLiteral( "0" )] = mSymbol.get();
283 const QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, context );
284 rendererElem.appendChild( symbolsElem );
285
286 const QDomElement rotationElem = doc.createElement( QStringLiteral( "rotation" ) );
287 rendererElem.appendChild( rotationElem );
288
289 const QDomElement sizeScaleElem = doc.createElement( QStringLiteral( "sizescale" ) );
290 rendererElem.appendChild( sizeScaleElem );
291
293 {
294 QDomElement ddsLegendElem = doc.createElement( QStringLiteral( "data-defined-size-legend" ) );
295 mDataDefinedSizeLegend->writeXml( ddsLegendElem, context );
296 rendererElem.appendChild( ddsLegendElem );
297 }
298
299 saveRendererData( doc, rendererElem, context );
300
301 return rendererElem;
302}
303
305{
307 {
308 const QgsMarkerSymbol *symbol = static_cast<const QgsMarkerSymbol *>( mSymbol.get() );
309 const QgsProperty sizeDD( symbol->dataDefinedSize() );
310 if ( sizeDD && sizeDD.isActive() )
311 {
313 ddSizeLegend.updateFromSymbolAndProperty( static_cast<const QgsMarkerSymbol *>( mSymbol.get() ), sizeDD );
314 return ddSizeLegend.legendSymbolList();
315 }
316 }
317
319 lst << QgsLegendSymbolItem( mSymbol.get(), QString(), QStringLiteral( "0" ) );
320 return lst;
321}
322
323QSet< QString > QgsSingleSymbolRenderer::legendKeysForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
324{
325 Q_UNUSED( feature )
326 Q_UNUSED( context )
327 return QSet< QString >() << QStringLiteral( "0" );
328}
329
330QString QgsSingleSymbolRenderer::legendKeyToExpression( const QString &key, QgsVectorLayer *, bool &ok ) const
331{
332 if ( key == QLatin1String( "0" ) )
333 {
334 ok = true;
335 return QStringLiteral( "TRUE" );
336 }
337 else
338 {
339 ok = false;
340 return QString();
341 }
342}
343
345{
346 Q_UNUSED( key )
347 setSymbol( symbol );
348}
349
351{
352 QgsSingleSymbolRenderer *r = nullptr;
353 if ( renderer->type() == QLatin1String( "singleSymbol" ) )
354 {
355 r = dynamic_cast<QgsSingleSymbolRenderer *>( renderer->clone() );
356 }
357 else if ( renderer->type() == QLatin1String( "pointDisplacement" ) || renderer->type() == QLatin1String( "pointCluster" ) )
358 {
359 const QgsPointDistanceRenderer *pointDistanceRenderer = dynamic_cast<const QgsPointDistanceRenderer *>( renderer );
360 if ( pointDistanceRenderer )
361 r = convertFromRenderer( pointDistanceRenderer->embeddedRenderer() );
362 }
363 else if ( renderer->type() == QLatin1String( "invertedPolygonRenderer" ) )
364 {
365 const QgsInvertedPolygonRenderer *invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer *>( renderer );
366 if ( invertedPolygonRenderer )
367 r = convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
368 }
369
370 if ( !r )
371 {
372 QgsRenderContext context;
373 const QgsSymbolList symbols = const_cast<QgsFeatureRenderer *>( renderer )->symbols( context );
374 if ( !symbols.isEmpty() )
375 {
376 r = new QgsSingleSymbolRenderer( symbols.at( 0 )->clone() );
377 }
378 }
379
380 if ( r )
381 {
382 renderer->copyRendererData( r );
383 }
384
385 return r;
386}
387
389{
390 mDataDefinedSizeLegend.reset( settings );
391}
392
394{
395 return mDataDefinedSizeLegend.get();
396}
@ Marker
Marker symbol.
Object that keeps configuration of appearance of marker symbol's data-defined size in legend.
static QgsDataDefinedSizeLegend * readXml(const QDomElement &elem, const QgsReadWriteContext &context) SIP_FACTORY
Creates instance from given element and returns it (caller takes ownership). Returns nullptr on error...
void updateFromSymbolAndProperty(const QgsMarkerSymbol *symbol, const QgsProperty &ddSize)
Updates the list of classes, source symbol and title label from given symbol and property.
QgsLegendSymbolList legendSymbolList() const
Generates legend symbol items according to the configuration.
virtual void stopRender(QgsRenderContext &context)
Must be called when a render cycle has finished, to allow the renderer to clean up.
QString type() const
Definition: qgsrenderer.h:142
void copyRendererData(QgsFeatureRenderer *destRenderer) const
Clones generic renderer data to another renderer.
Definition: qgsrenderer.cpp:52
static void convertSymbolRotation(QgsSymbol *symbol, const QString &field)
void saveRendererData(QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context)
Saves generic renderer data into the specified element.
virtual void startRender(QgsRenderContext &context, const QgsFields &fields)
Must be called when a new render cycle is started.
Definition: qgsrenderer.cpp:96
static void convertSymbolSizeScale(QgsSymbol *symbol, Qgis::ScaleMethod method, const QString &field)
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
Container of fields for a vector layer.
Definition: qgsfields.h:45
QgsInvertedPolygonRenderer is a polygon-only feature renderer used to display features inverted,...
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
A marker symbol type, for rendering Point and MultiPoint geometries.
const QgsFeatureRenderer * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
An abstract base class for distance based point renderers (e.g., clusterer and displacement renderers...
const QgsFeatureRenderer * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
A store for object properties.
Definition: qgsproperty.h:231
bool isActive() const
Returns whether the property is currently active.
The class is used as a container of context for various read/write operations on other objects.
Contains information about the context of a rendering operation.
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns a list of attributes required by this renderer.
QDomElement save(QDomDocument &doc, const QgsReadWriteContext &context) override
Stores renderer properties to an XML element.
QgsSymbol * symbol() const
Returns the symbol which will be rendered for every feature.
QString legendKeyToExpression(const QString &key, QgsVectorLayer *layer, bool &ok) const override
Attempts to convert the specified legend rule key to a QGIS expression matching the features displaye...
void setSymbol(QgsSymbol *s)
Sets the symbol which will be rendered for every feature.
std::unique_ptr< QgsSymbol > mSymbol
bool accept(QgsStyleEntityVisitorInterface *visitor) const override
Accepts the specified symbology visitor, causing it to visit all symbols associated with the renderer...
void setLegendSymbolItem(const QString &key, QgsSymbol *symbol) override
Sets the symbol to be used for a legend symbol item.
static QgsFeatureRenderer * create(QDomElement &element, const QgsReadWriteContext &context)
Creates a new single symbol renderer from an XML element, using the supplied read/write context.
~QgsSingleSymbolRenderer() override
QSet< QString > legendKeysForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Returns legend keys matching a specified feature.
void setDataDefinedSizeLegend(QgsDataDefinedSizeLegend *settings)
Configures appearance of legend when renderer is configured to use data-defined size for marker symbo...
static QgsFeatureRenderer * createFromSld(QDomElement &element, QgsWkbTypes::GeometryType geomType)
Creates a new single symbol renderer from an SLD element.
void stopRender(QgsRenderContext &context) override
Must be called when a render cycle has finished, to allow the renderer to clean up.
QgsLegendSymbolList legendSymbolItems() const override
Returns a list of symbology items for the legend.
void startRender(QgsRenderContext &context, const QgsFields &fields) override
Must be called when a new render cycle is started.
QgsDataDefinedSizeLegend * dataDefinedSizeLegend() const
Returns configuration of appearance of legend when using data-defined size for marker symbols.
std::unique_ptr< QgsDataDefinedSizeLegend > mDataDefinedSizeLegend
QgsSymbol * originalSymbolForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Returns symbol for feature.
static QgsSingleSymbolRenderer * convertFromRenderer(const QgsFeatureRenderer *renderer)
Creates a new single symbol renderer from an existing renderer.
QgsSymbol * symbolForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
To be overridden.
void toSld(QDomDocument &doc, QDomElement &element, const QVariantMap &props=QVariantMap()) const override
used from subclasses to create SLD Rule elements following SLD v1.1 specs
QgsSingleSymbolRenderer * clone() const override
Create a deep copy of this renderer.
QgsSymbolList symbols(QgsRenderContext &context) const override
Returns list of symbols used by the renderer.
QString dump() const override
Returns debug information about this renderer.
QgsSingleSymbolRenderer(QgsSymbol *symbol)
Constructor for QgsSingleSymbolRenderer.
An interface for classes which can visit style entity (e.g.
virtual bool visit(const QgsStyleEntityVisitorInterface::StyleLeaf &entity)
Called when the visitor will visit a style entity.
A symbol entity for QgsStyle databases.
Definition: qgsstyle.h:1342
static void applyScaleDependency(QDomDocument &doc, QDomElement &ruleElem, QVariantMap &props)
Checks if the properties contain scaleMinDenom and scaleMaxDenom, if available, they are added into t...
static Qgis::ScaleMethod decodeScaleMethod(const QString &str)
Decodes a symbol scale method from a string.
static bool createSymbolLayerListFromSld(QDomElement &element, QgsWkbTypes::GeometryType geomType, QList< QgsSymbolLayer * > &layers)
Creates a symbol layer list from a DOM element.
static void clearSymbolMap(QgsSymbolMap &symbols)
static QgsSymbolMap loadSymbols(QDomElement &element, const QgsReadWriteContext &context)
Reads a collection of symbols from XML and returns them in a map. Caller is responsible for deleting ...
static QDomElement saveSymbols(QgsSymbolMap &symbols, const QString &tagName, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a collection of symbols to XML with specified tagName for the top-level element.
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:93
Represents a vector layer which manages a vector based data sets.
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:141
QList< QgsLegendSymbolItem > QgsLegendSymbolList
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
#define RENDERER_TAG_NAME
Definition: qgsrenderer.h:50
QMap< QString, QgsSymbol * > QgsSymbolMap
Definition: qgsrenderer.h:45
QList< QgsSymbol * > QgsSymbolList
Definition: qgsrenderer.h:44
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition: qgssymbol.h:29
Contains information relating to the style entity currently being visited.