QGIS API Documentation  2.99.0-Master (9caa722)
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"
30 #include "qgspainteffectregistry.h"
31 #include "qgsproperty.h"
32 
33 #include <QDomDocument>
34 #include <QDomElement>
35 
37  : QgsFeatureRenderer( QStringLiteral( "singleSymbol" ) )
38  , mSymbol( symbol )
39 {
40  Q_ASSERT( symbol );
41 }
42 
44 {
45  return mSymbol.get();
46 }
47 
49 {
50  Q_UNUSED( context );
51  Q_UNUSED( feature );
52  return mSymbol.get();
53 }
54 
56 {
57  if ( !mSymbol )
58  return;
59 
60  mSymbol->startRender( context, fields );
61 }
62 
64 {
65  if ( !mSymbol )
66  return;
67 
68  mSymbol->stopRender( context );
69 }
70 
71 QSet<QString> QgsSingleSymbolRenderer::usedAttributes( const QgsRenderContext &context ) const
72 {
73  QSet<QString> attributes;
74  if ( mSymbol )
75  attributes.unite( mSymbol->usedAttributes( context ) );
76  return attributes;
77 }
78 
80 {
81  return mSymbol.get();
82 }
83 
85 {
86  Q_ASSERT( s );
87  mSymbol.reset( s );
88 }
89 
91 {
92  return mSymbol ? QStringLiteral( "SINGLE: %1" ).arg( mSymbol->dump() ) : QLatin1String( "" );
93 }
94 
96 {
100  copyRendererData( r );
101  return r;
102 }
103 
104 void QgsSingleSymbolRenderer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
105 {
106  QgsStringMap newProps = props;
107 
108  QDomElement ruleElem = doc.createElement( QStringLiteral( "se:Rule" ) );
109  element.appendChild( ruleElem );
110 
111  QDomElement nameElem = doc.createElement( QStringLiteral( "se:Name" ) );
112  nameElem.appendChild( doc.createTextNode( QStringLiteral( "Single symbol" ) ) );
113  ruleElem.appendChild( nameElem );
114 
115  QgsSymbolLayerUtils::applyScaleDependency( doc, ruleElem, newProps );
116 
117  if ( mSymbol ) mSymbol->toSld( doc, ruleElem, newProps );
118 }
119 
121 {
122  Q_UNUSED( context );
123  QgsSymbolList lst;
124  lst.append( mSymbol.get() );
125  return lst;
126 }
127 
128 
130 {
131  QDomElement symbolsElem = element.firstChildElement( QStringLiteral( "symbols" ) );
132  if ( symbolsElem.isNull() )
133  return nullptr;
134 
135  QgsSymbolMap symbolMap = QgsSymbolLayerUtils::loadSymbols( symbolsElem, context );
136 
137  if ( !symbolMap.contains( QStringLiteral( "0" ) ) )
138  return nullptr;
139 
140  QgsSingleSymbolRenderer *r = new QgsSingleSymbolRenderer( symbolMap.take( QStringLiteral( "0" ) ) );
141 
142  // delete symbols if there are any more
144 
145  QDomElement rotationElem = element.firstChildElement( QStringLiteral( "rotation" ) );
146  if ( !rotationElem.isNull() && !rotationElem.attribute( QStringLiteral( "field" ) ).isEmpty() )
147  {
148  convertSymbolRotation( r->mSymbol.get(), rotationElem.attribute( QStringLiteral( "field" ) ) );
149  }
150 
151  QDomElement sizeScaleElem = element.firstChildElement( QStringLiteral( "sizescale" ) );
152  if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( QStringLiteral( "field" ) ).isEmpty() )
153  {
155  QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( QStringLiteral( "scalemethod" ) ) ),
156  sizeScaleElem.attribute( QStringLiteral( "field" ) ) );
157  }
158 
159  QDomElement ddsLegendSizeElem = element.firstChildElement( "data-defined-size-legend" );
160  if ( !ddsLegendSizeElem.isNull() )
161  {
162  r->mDataDefinedSizeLegend.reset( QgsDataDefinedSizeLegend::readXml( ddsLegendSizeElem, context ) );
163  }
164 
165  // TODO: symbol levels
166  return r;
167 }
168 
170 {
171  // XXX this renderer can handle only one Rule!
172 
173  // get the first Rule element
174  QDomElement ruleElem = element.firstChildElement( QStringLiteral( "Rule" ) );
175  if ( ruleElem.isNull() )
176  {
177  QgsDebugMsg( "no Rule elements found!" );
178  return nullptr;
179  }
180 
181  QString label, description;
182  QgsSymbolLayerList layers;
183 
184  // retrieve the Rule element child nodes
185  QDomElement childElem = ruleElem.firstChildElement();
186  while ( !childElem.isNull() )
187  {
188  if ( childElem.localName() == QLatin1String( "Name" ) )
189  {
190  // <se:Name> tag contains the rule identifier,
191  // so prefer title tag for the label property value
192  if ( label.isEmpty() )
193  label = childElem.firstChild().nodeValue();
194  }
195  else if ( childElem.localName() == QLatin1String( "Description" ) )
196  {
197  // <se:Description> can contains a title and an abstract
198  QDomElement titleElem = childElem.firstChildElement( QStringLiteral( "Title" ) );
199  if ( !titleElem.isNull() )
200  {
201  label = titleElem.firstChild().nodeValue();
202  }
203 
204  QDomElement abstractElem = childElem.firstChildElement( QStringLiteral( "Abstract" ) );
205  if ( !abstractElem.isNull() )
206  {
207  description = abstractElem.firstChild().nodeValue();
208  }
209  }
210  else if ( childElem.localName() == QLatin1String( "Abstract" ) )
211  {
212  // <sld:Abstract> (v1.0)
213  description = childElem.firstChild().nodeValue();
214  }
215  else if ( childElem.localName() == QLatin1String( "Title" ) )
216  {
217  // <sld:Title> (v1.0)
218  label = childElem.firstChild().nodeValue();
219  }
220  else if ( childElem.localName().endsWith( QLatin1String( "Symbolizer" ) ) )
221  {
222  // create symbol layers for this symbolizer
223  QgsSymbolLayerUtils::createSymbolLayerListFromSld( childElem, geomType, layers );
224  }
225 
226  childElem = childElem.nextSiblingElement();
227  }
228 
229  if ( layers.isEmpty() )
230  return nullptr;
231 
232  // now create the symbol
233  QgsSymbol *symbol = nullptr;
234  switch ( geomType )
235  {
237  symbol = new QgsLineSymbol( layers );
238  break;
239 
241  symbol = new QgsFillSymbol( layers );
242  break;
243 
245  symbol = new QgsMarkerSymbol( layers );
246  break;
247 
248  default:
249  QgsDebugMsg( QString( "invalid geometry type: found %1" ).arg( geomType ) );
250  return nullptr;
251  }
252 
253  // and finally return the new renderer
254  return new QgsSingleSymbolRenderer( symbol );
255 }
256 
257 QDomElement QgsSingleSymbolRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
258 {
259  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
260  rendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "singleSymbol" ) );
261  rendererElem.setAttribute( QStringLiteral( "symbollevels" ), ( mUsingSymbolLevels ? "1" : "0" ) );
262  rendererElem.setAttribute( QStringLiteral( "forceraster" ), ( mForceRaster ? "1" : "0" ) );
263 
265  symbols[QStringLiteral( "0" )] = mSymbol.get();
266  QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, context );
267  rendererElem.appendChild( symbolsElem );
268 
269  QDomElement rotationElem = doc.createElement( QStringLiteral( "rotation" ) );
270  rendererElem.appendChild( rotationElem );
271 
272  QDomElement sizeScaleElem = doc.createElement( QStringLiteral( "sizescale" ) );
273  rendererElem.appendChild( sizeScaleElem );
274 
276  mPaintEffect->saveProperties( doc, rendererElem );
277 
278  if ( !mOrderBy.isEmpty() )
279  {
280  QDomElement orderBy = doc.createElement( QStringLiteral( "orderby" ) );
281  mOrderBy.save( orderBy );
282  rendererElem.appendChild( orderBy );
283  }
284  rendererElem.setAttribute( QStringLiteral( "enableorderby" ), ( mOrderByEnabled ? "1" : "0" ) );
285 
287  {
288  QDomElement ddsLegendElem = doc.createElement( QStringLiteral( "data-defined-size-legend" ) );
289  mDataDefinedSizeLegend->writeXml( ddsLegendElem, context );
290  rendererElem.appendChild( ddsLegendElem );
291  }
292 
293  return rendererElem;
294 }
295 
297 {
299  {
300  const QgsMarkerSymbol *symbol = static_cast<const QgsMarkerSymbol *>( mSymbol.get() );
301  QgsProperty sizeDD( symbol->dataDefinedSize() );
302  if ( sizeDD && sizeDD.isActive() )
303  {
305  ddSizeLegend.updateFromSymbolAndProperty( static_cast<const QgsMarkerSymbol *>( mSymbol.get() ), sizeDD );
306  return ddSizeLegend.legendSymbolList();
307  }
308  }
309 
311  lst << QgsLegendSymbolItem( mSymbol.get(), QString(), QString() );
312  return lst;
313 }
314 
316 {
317  Q_UNUSED( feature );
318  Q_UNUSED( context );
319  return QSet< QString >() << QString();
320 }
321 
323 {
324  Q_UNUSED( key );
325  setSymbol( symbol );
326 }
327 
329 {
330  QgsSingleSymbolRenderer *r = nullptr;
331  if ( renderer->type() == QLatin1String( "singleSymbol" ) )
332  {
333  r = dynamic_cast<QgsSingleSymbolRenderer *>( renderer->clone() );
334  }
335  else if ( renderer->type() == QLatin1String( "pointDisplacement" ) || renderer->type() == QLatin1String( "pointCluster" ) )
336  {
337  const QgsPointDistanceRenderer *pointDistanceRenderer = dynamic_cast<const QgsPointDistanceRenderer *>( renderer );
338  if ( pointDistanceRenderer )
339  r = convertFromRenderer( pointDistanceRenderer->embeddedRenderer() );
340  }
341  else if ( renderer->type() == QLatin1String( "invertedPolygonRenderer" ) )
342  {
343  const QgsInvertedPolygonRenderer *invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer *>( renderer );
344  if ( invertedPolygonRenderer )
345  r = convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
346  }
347 
348  if ( !r )
349  {
350  QgsRenderContext context;
351  QgsSymbolList symbols = const_cast<QgsFeatureRenderer *>( renderer )->symbols( context );
352  if ( !symbols.isEmpty() )
353  {
354  r = new QgsSingleSymbolRenderer( symbols.at( 0 )->clone() );
355  }
356  }
357 
358  if ( r )
359  {
360  r->setOrderBy( renderer->orderBy() );
361  r->setOrderByEnabled( renderer->orderByEnabled() );
362  }
363 
364  return r;
365 }
366 
368 {
369  mDataDefinedSizeLegend.reset( settings );
370 }
371 
373 {
374  return mDataDefinedSizeLegend.get();
375 }
An abstract base class for distance based point renderers (e.g., clusterer and displacement renderers...
static QgsFeatureRenderer * create(QDomElement &element, const QgsReadWriteContext &context)
create renderer from XML element
virtual void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props=QgsStringMap()) const override
used from subclasses to create SLD Rule elements following SLD v1.1 specs
The class is used as a container of context for various read/write operations on other objects...
static QgsSymbol::ScaleMethod decodeScaleMethod(const QString &str)
virtual QgsSymbolList symbols(QgsRenderContext &context) override
Returns list of symbols used by the renderer.
QList< QgsLegendSymbolItem > QgsLegendSymbolList
QgsFeatureRequest::OrderBy mOrderBy
Definition: qgsrenderer.h:472
static void applyScaleDependency(QDomDocument &doc, QDomElement &ruleElem, QgsStringMap &props)
Checks if the properties contain scaleMinDenom and scaleMaxDenom, if available, they are added into t...
#define QgsDebugMsg(str)
Definition: qgslogger.h:37
QgsFeatureRequest::OrderBy orderBy() const
Get the order in which features shall be processed by this renderer.
virtual void stopRender(QgsRenderContext &context) override
Needs to be called when a render cycle has finished to clean up.
void setDataDefinedSizeLegend(QgsDataDefinedSizeLegend *settings)
Configures appearance of legend when renderer is configured to use data-defined size for marker symbo...
static bool isDefaultStack(QgsPaintEffect *effect)
Tests whether a paint effect matches the default effects stack.
static QgsSingleSymbolRenderer * convertFromRenderer(const QgsFeatureRenderer *renderer)
creates a QgsSingleSymbolRenderer from an existing renderer.
Container of fields for a vector layer.
Definition: qgsfields.h:41
virtual QgsLegendSymbolList legendSymbolItems() const override
Returns a list of symbology items for the legend.
#define RENDERER_TAG_NAME
Definition: qgsrenderer.h:49
void setUsingSymbolLevels(bool usingSymbolLevels)
Definition: qgsrenderer.h:250
static void clearSymbolMap(QgsSymbolMap &symbols)
virtual QgsSymbol * symbolForFeature(QgsFeature &feature, QgsRenderContext &context) override
To be overridden.
QgsPaintEffect * mPaintEffect
Definition: qgsrenderer.h:458
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:61
QgsLegendSymbolList legendSymbolList() const
Generates legend symbol items according to the configuration.
std::unique_ptr< QgsDataDefinedSizeLegend > mDataDefinedSizeLegend
QMap< QString, QString > QgsStringMap
Definition: qgis.h:366
static QgsFeatureRenderer * createFromSld(QDomElement &element, QgsWkbTypes::GeometryType geomType)
QList< QgsSymbol * > QgsSymbolList
Definition: qgsrenderer.h:43
QString type() const
Definition: qgsrenderer.h:124
virtual QgsSymbol * originalSymbolForFeature(QgsFeature &feature, QgsRenderContext &context) override
Return symbol for feature.
QgsInvertedPolygonRenderer is a polygon-only feature renderer used to display features inverted...
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.
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition: qgssymbol.h:53
void updateFromSymbolAndProperty(const QgsMarkerSymbol *symbol, const QgsProperty &ddSize)
Updates the list of classes, source symbol and title label from given symbol and property.
static void convertSymbolSizeScale(QgsSymbol *symbol, QgsSymbol::ScaleMethod method, const QString &field)
virtual void setLegendSymbolItem(const QString &key, QgsSymbol *symbol) override
Sets the symbol to be used for a legend symbol item.
A store for object properties.
Definition: qgsproperty.h:189
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 ...
bool orderByEnabled() const
Returns whether custom ordering will be applied before features are processed by this renderer...
void setOrderBy(const QgsFeatureRequest::OrderBy &orderBy)
Define the order in which features shall be processed by this renderer.
QgsProperty dataDefinedSize() const
Returns data defined size for whole symbol (including all symbol layers).
Definition: qgssymbol.cpp:1346
virtual void startRender(QgsRenderContext &context, const QgsFields &fields) override
Needs to be called when a new render cycle is started.
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:136
static void convertSymbolRotation(QgsSymbol *symbol, const QString &field)
Marker symbol.
Definition: qgssymbol.h:84
const QgsFeatureRenderer * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
virtual QString dump() const override
Returns debug information about this renderer.
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
Contains information about the context of a rendering operation.
bool usingSymbolLevels() const
Definition: qgsrenderer.h:249
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Return a list of attributes required by this renderer.
void CORE_EXPORT save(QDomElement &elem) const
Serialize to XML.
QMap< QString, QgsSymbol *> QgsSymbolMap
Definition: qgsrenderer.h:44
void copyRendererData(QgsFeatureRenderer *destRenderer) const
Clones generic renderer data to another renderer.
Definition: qgsrenderer.cpp:48
virtual QDomElement save(QDomDocument &doc, const QgsReadWriteContext &context) override
store renderer info to XML element
QgsDataDefinedSizeLegend * dataDefinedSizeLegend() const
Returns configuration of appearance of legend when using data-defined size for marker symbols...
QgsSingleSymbolRenderer(QgsSymbol *symbol)
const QgsFeatureRenderer * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
Object that keeps configuration of appearance of marker symbol&#39;s data-defined size in legend...
virtual QSet< QString > legendKeysForFeature(QgsFeature &feature, QgsRenderContext &context) override
Return legend keys matching a specified feature.
static bool createSymbolLayerListFromSld(QDomElement &element, QgsWkbTypes::GeometryType geomType, QgsSymbolLayerList &layers)
static QgsDataDefinedSizeLegend * readXml(const QDomElement &elem, const QgsReadWriteContext &context) SIP_FACTORY
Creates instance from given element and returns it (caller takes ownership). Returns null on error...
std::unique_ptr< QgsSymbol > mSymbol
void setOrderByEnabled(bool enabled)
Sets whether custom ordering should be applied before features are processed by this renderer...
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
virtual QgsSingleSymbolRenderer * clone() const override
Create a deep copy of this renderer.
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.