QGIS API Documentation  2.99.0-Master (0cba29c)
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 }
46 
48 {
49  return mSymbol.get();
50 }
51 
53 {
54  Q_UNUSED( context );
55  Q_UNUSED( feature );
56  return mSymbol.get();
57 }
58 
60 {
61  if ( !mSymbol )
62  return;
63 
64  mSymbol->startRender( context, fields );
65 }
66 
68 {
69  if ( !mSymbol )
70  return;
71 
72  mSymbol->stopRender( context );
73 }
74 
75 QSet<QString> QgsSingleSymbolRenderer::usedAttributes( const QgsRenderContext &context ) const
76 {
77  QSet<QString> attributes;
78  if ( mSymbol )
79  attributes.unite( mSymbol->usedAttributes( context ) );
80  return attributes;
81 }
82 
84 {
85  return mSymbol.get();
86 }
87 
89 {
90  Q_ASSERT( s );
91  mSymbol.reset( s );
92 }
93 
95 {
96  return mSymbol ? QStringLiteral( "SINGLE: %1" ).arg( mSymbol->dump() ) : QLatin1String( "" );
97 }
98 
100 {
104  copyRendererData( r );
105  return r;
106 }
107 
108 void QgsSingleSymbolRenderer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
109 {
110  QgsStringMap newProps = props;
111 
112  QDomElement ruleElem = doc.createElement( QStringLiteral( "se:Rule" ) );
113  element.appendChild( ruleElem );
114 
115  QDomElement nameElem = doc.createElement( QStringLiteral( "se:Name" ) );
116  nameElem.appendChild( doc.createTextNode( QStringLiteral( "Single symbol" ) ) );
117  ruleElem.appendChild( nameElem );
118 
119  QgsSymbolLayerUtils::applyScaleDependency( doc, ruleElem, newProps );
120 
121  if ( mSymbol ) mSymbol->toSld( doc, ruleElem, newProps );
122 }
123 
125 {
126  Q_UNUSED( context );
127  QgsSymbolList lst;
128  lst.append( mSymbol.get() );
129  return lst;
130 }
131 
132 
134 {
135  QDomElement symbolsElem = element.firstChildElement( QStringLiteral( "symbols" ) );
136  if ( symbolsElem.isNull() )
137  return nullptr;
138 
139  QgsSymbolMap symbolMap = QgsSymbolLayerUtils::loadSymbols( symbolsElem, context );
140 
141  if ( !symbolMap.contains( QStringLiteral( "0" ) ) )
142  return nullptr;
143 
144  QgsSingleSymbolRenderer *r = new QgsSingleSymbolRenderer( symbolMap.take( QStringLiteral( "0" ) ) );
145 
146  // delete symbols if there are any more
148 
149  QDomElement rotationElem = element.firstChildElement( QStringLiteral( "rotation" ) );
150  if ( !rotationElem.isNull() && !rotationElem.attribute( QStringLiteral( "field" ) ).isEmpty() )
151  {
152  convertSymbolRotation( r->mSymbol.get(), rotationElem.attribute( QStringLiteral( "field" ) ) );
153  }
154 
155  QDomElement sizeScaleElem = element.firstChildElement( QStringLiteral( "sizescale" ) );
156  if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( QStringLiteral( "field" ) ).isEmpty() )
157  {
159  QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( QStringLiteral( "scalemethod" ) ) ),
160  sizeScaleElem.attribute( QStringLiteral( "field" ) ) );
161  }
162 
163  QDomElement ddsLegendSizeElem = element.firstChildElement( "data-defined-size-legend" );
164  if ( !ddsLegendSizeElem.isNull() )
165  {
166  r->mDataDefinedSizeLegend.reset( QgsDataDefinedSizeLegend::readXml( ddsLegendSizeElem, context ) );
167  }
168 
169  // TODO: symbol levels
170  return r;
171 }
172 
174 {
175  // XXX this renderer can handle only one Rule!
176 
177  // get the first Rule element
178  QDomElement ruleElem = element.firstChildElement( QStringLiteral( "Rule" ) );
179  if ( ruleElem.isNull() )
180  {
181  QgsDebugMsg( "no Rule elements found!" );
182  return nullptr;
183  }
184 
185  QString label, description;
186  QgsSymbolLayerList layers;
187 
188  // retrieve the Rule element child nodes
189  QDomElement childElem = ruleElem.firstChildElement();
190  while ( !childElem.isNull() )
191  {
192  if ( childElem.localName() == QLatin1String( "Name" ) )
193  {
194  // <se:Name> tag contains the rule identifier,
195  // so prefer title tag for the label property value
196  if ( label.isEmpty() )
197  label = childElem.firstChild().nodeValue();
198  }
199  else if ( childElem.localName() == QLatin1String( "Description" ) )
200  {
201  // <se:Description> can contains a title and an abstract
202  QDomElement titleElem = childElem.firstChildElement( QStringLiteral( "Title" ) );
203  if ( !titleElem.isNull() )
204  {
205  label = titleElem.firstChild().nodeValue();
206  }
207 
208  QDomElement abstractElem = childElem.firstChildElement( QStringLiteral( "Abstract" ) );
209  if ( !abstractElem.isNull() )
210  {
211  description = abstractElem.firstChild().nodeValue();
212  }
213  }
214  else if ( childElem.localName() == QLatin1String( "Abstract" ) )
215  {
216  // <sld:Abstract> (v1.0)
217  description = childElem.firstChild().nodeValue();
218  }
219  else if ( childElem.localName() == QLatin1String( "Title" ) )
220  {
221  // <sld:Title> (v1.0)
222  label = childElem.firstChild().nodeValue();
223  }
224  else if ( childElem.localName().endsWith( QLatin1String( "Symbolizer" ) ) )
225  {
226  // create symbol layers for this symbolizer
227  QgsSymbolLayerUtils::createSymbolLayerListFromSld( childElem, geomType, layers );
228  }
229 
230  childElem = childElem.nextSiblingElement();
231  }
232 
233  if ( layers.isEmpty() )
234  return nullptr;
235 
236  // now create the symbol
237  QgsSymbol *symbol = nullptr;
238  switch ( geomType )
239  {
241  symbol = new QgsLineSymbol( layers );
242  break;
243 
245  symbol = new QgsFillSymbol( layers );
246  break;
247 
249  symbol = new QgsMarkerSymbol( layers );
250  break;
251 
252  default:
253  QgsDebugMsg( QString( "invalid geometry type: found %1" ).arg( geomType ) );
254  return nullptr;
255  }
256 
257  // and finally return the new renderer
258  return new QgsSingleSymbolRenderer( symbol );
259 }
260 
261 QDomElement QgsSingleSymbolRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
262 {
263  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
264  rendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "singleSymbol" ) );
265  rendererElem.setAttribute( QStringLiteral( "symbollevels" ), ( mUsingSymbolLevels ? "1" : "0" ) );
266  rendererElem.setAttribute( QStringLiteral( "forceraster" ), ( mForceRaster ? "1" : "0" ) );
267 
269  symbols[QStringLiteral( "0" )] = mSymbol.get();
270  QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, context );
271  rendererElem.appendChild( symbolsElem );
272 
273  QDomElement rotationElem = doc.createElement( QStringLiteral( "rotation" ) );
274  rendererElem.appendChild( rotationElem );
275 
276  QDomElement sizeScaleElem = doc.createElement( QStringLiteral( "sizescale" ) );
277  rendererElem.appendChild( sizeScaleElem );
278 
280  mPaintEffect->saveProperties( doc, rendererElem );
281 
282  if ( !mOrderBy.isEmpty() )
283  {
284  QDomElement orderBy = doc.createElement( QStringLiteral( "orderby" ) );
285  mOrderBy.save( orderBy );
286  rendererElem.appendChild( orderBy );
287  }
288  rendererElem.setAttribute( QStringLiteral( "enableorderby" ), ( mOrderByEnabled ? "1" : "0" ) );
289 
291  {
292  QDomElement ddsLegendElem = doc.createElement( QStringLiteral( "data-defined-size-legend" ) );
293  mDataDefinedSizeLegend->writeXml( ddsLegendElem, context );
294  rendererElem.appendChild( ddsLegendElem );
295  }
296 
297  return rendererElem;
298 }
299 
301 {
303  {
304  const QgsMarkerSymbol *symbol = static_cast<const QgsMarkerSymbol *>( mSymbol.get() );
305  QgsProperty sizeDD( symbol->dataDefinedSize() );
306  if ( sizeDD && sizeDD.isActive() )
307  {
309  ddSizeLegend.updateFromSymbolAndProperty( static_cast<const QgsMarkerSymbol *>( mSymbol.get() ), sizeDD );
310  return ddSizeLegend.legendSymbolList();
311  }
312  }
313 
315  lst << QgsLegendSymbolItem( mSymbol.get(), QString(), QString() );
316  return lst;
317 }
318 
320 {
321  Q_UNUSED( feature );
322  Q_UNUSED( context );
323  return QSet< QString >() << QString();
324 }
325 
327 {
328  Q_UNUSED( key );
329  setSymbol( symbol );
330 }
331 
333 {
334  QgsSingleSymbolRenderer *r = nullptr;
335  if ( renderer->type() == QLatin1String( "singleSymbol" ) )
336  {
337  r = dynamic_cast<QgsSingleSymbolRenderer *>( renderer->clone() );
338  }
339  else if ( renderer->type() == QLatin1String( "pointDisplacement" ) || renderer->type() == QLatin1String( "pointCluster" ) )
340  {
341  const QgsPointDistanceRenderer *pointDistanceRenderer = dynamic_cast<const QgsPointDistanceRenderer *>( renderer );
342  if ( pointDistanceRenderer )
343  r = convertFromRenderer( pointDistanceRenderer->embeddedRenderer() );
344  }
345  else if ( renderer->type() == QLatin1String( "invertedPolygonRenderer" ) )
346  {
347  const QgsInvertedPolygonRenderer *invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer *>( renderer );
348  if ( invertedPolygonRenderer )
349  r = convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
350  }
351 
352  if ( !r )
353  {
354  QgsRenderContext context;
355  QgsSymbolList symbols = const_cast<QgsFeatureRenderer *>( renderer )->symbols( context );
356  if ( !symbols.isEmpty() )
357  {
358  r = new QgsSingleSymbolRenderer( symbols.at( 0 )->clone() );
359  }
360  }
361 
362  if ( r )
363  {
364  r->setOrderBy( renderer->orderBy() );
365  r->setOrderByEnabled( renderer->orderByEnabled() );
366  }
367 
368  return r;
369 }
370 
372 {
373  mDataDefinedSizeLegend.reset( settings );
374 }
375 
377 {
378  return mDataDefinedSizeLegend.get();
379 }
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:340
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.