QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
qgssinglesymbolrendererv2.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgssinglesymbolrendererv2.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 "qgssymbolv2.h"
19 #include "qgssymbollayerv2utils.h"
20 
21 #include "qgslogger.h"
22 #include "qgsfeature.h"
23 #include "qgsvectorlayer.h"
24 #include "qgssymbollayerv2.h"
25 #include "qgsogcutils.h"
28 #include "qgspainteffect.h"
29 #include "qgspainteffectregistry.h"
30 #include "qgsscaleexpression.h"
31 #include "qgsdatadefined.h"
32 
33 #include <QDomDocument>
34 #include <QDomElement>
35 
37  : QgsFeatureRendererV2( "singleSymbol" )
38  , mSymbol( symbol )
39  , mScaleMethod( DEFAULT_SCALE_METHOD )
40  , mOrigSize( 0.0 )
41 {
42  Q_ASSERT( symbol );
43 }
44 
46 {
47 }
48 
50 {
51  context.expressionContext().setFeature( feature );
52  if ( !mRotation.data() && !mSizeScale.data() ) return mSymbol.data();
53 
54  const double rotation = mRotation.data() ? mRotation->evaluate( &context.expressionContext() ).toDouble() : 0;
55  const double sizeScale = mSizeScale.data() ? mSizeScale->evaluate( &context.expressionContext() ).toDouble() : 1.;
56 
57  if ( mTempSymbol->type() == QgsSymbolV2::Marker )
58  {
59  QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( mTempSymbol.data() );
60  if ( mRotation.data() ) markerSymbol->setAngle( rotation );
61  markerSymbol->setSize( sizeScale * mOrigSize );
62  markerSymbol->setScaleMethod( mScaleMethod );
63  }
64  else if ( mTempSymbol->type() == QgsSymbolV2::Line )
65  {
66  QgsLineSymbolV2* lineSymbol = static_cast<QgsLineSymbolV2*>( mTempSymbol.data() );
67  lineSymbol->setWidth( sizeScale * mOrigSize );
68  }
69  else if ( mTempSymbol->type() == QgsSymbolV2::Fill )
70  {
71  QgsFillSymbolV2* fillSymbol = static_cast<QgsFillSymbolV2*>( mTempSymbol.data() );
72  if ( mRotation.data() ) fillSymbol->setAngle( rotation );
73  }
74 
75  return mTempSymbol.data();
76 }
77 
79 {
80  Q_UNUSED( context );
81  Q_UNUSED( feature );
82  return mSymbol.data();
83 }
84 
86 {
87  if ( !mSymbol.data() )
88  return;
89 
90  mSymbol->startRender( context, &fields );
91 
92  if ( mRotation.data() || mSizeScale.data() )
93  {
94  // we are going to need a temporary symbol
95  mTempSymbol.reset( mSymbol->clone() );
96 
97  int hints = 0;
98  if ( mRotation.data() )
100  if ( mSizeScale.data() )
102  mTempSymbol->setRenderHints( hints );
103 
104  mTempSymbol->startRender( context, &fields );
105 
106  if ( mSymbol->type() == QgsSymbolV2::Marker )
107  {
108  mOrigSize = static_cast<QgsMarkerSymbolV2*>( mSymbol.data() )->size();
109  }
110  else if ( mSymbol->type() == QgsSymbolV2::Line )
111  {
112  mOrigSize = static_cast<QgsLineSymbolV2*>( mSymbol.data() )->width();
113  }
114  else
115  {
116  mOrigSize = 0;
117  }
118  }
119 
120  return;
121 }
122 
124 {
125  if ( !mSymbol.data() ) return;
126 
127  mSymbol->stopRender( context );
128 
129  if ( mRotation.data() || mSizeScale.data() )
130  {
131  // we are going to need a temporary symbol
132  mTempSymbol->stopRender( context );
133  mTempSymbol.reset();
134  }
135 }
136 
138 {
139  QSet<QString> attributes;
140  if ( mSymbol.data() ) attributes.unite( mSymbol->usedAttributes() );
141  if ( mRotation.data() ) attributes.unite( mRotation->referencedColumns().toSet() );
142  if ( mSizeScale.data() ) attributes.unite( mSizeScale->referencedColumns().toSet() );
143  return attributes.toList();
144 }
145 
147 {
148  return mSymbol.data();
149 }
150 
152 {
153  Q_ASSERT( s );
154  mSymbol.reset( s );
155 }
156 
157 void QgsSingleSymbolRendererV2::setRotationField( const QString& fieldOrExpression )
158 {
159  if ( mSymbol->type() == QgsSymbolV2::Marker )
160  {
161  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( mSymbol.data() );
162  s->setDataDefinedAngle( QgsDataDefined( fieldOrExpression ) );
163  }
164 }
165 
167 {
168  if ( mSymbol->type() == QgsSymbolV2::Marker )
169  {
170  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( mSymbol.data() );
171  QgsDataDefined ddAngle = s->dataDefinedAngle();
172  return ddAngle.useExpression() ? ddAngle.expressionString() : ddAngle.field();
173  }
174 
175  return QString();
176 }
177 
179 {
181 }
182 
184 {
186 }
187 
189 {
192 }
193 
195 {
196  return mSymbol.data() ? QString( "SINGLE: %1" ).arg( mSymbol->dump() ) : "";
197 }
198 
200 {
204  copyRendererData( r );
205  return r;
206 }
207 
209 {
210  toSld( doc, element, QgsStringMap() );
211 }
212 
213 void QgsSingleSymbolRendererV2::toSld( QDomDocument& doc, QDomElement &element, const QgsStringMap& props ) const
214 {
215  QgsStringMap locProps( props );
216  if ( mRotation.data() )
217  locProps[ "angle" ] = mRotation->expression();
218  if ( mSizeScale.data() )
219  locProps[ "scale" ] = mSizeScale->expression();
220 
221  QDomElement ruleElem = doc.createElement( "se:Rule" );
222  element.appendChild( ruleElem );
223 
224  QDomElement nameElem = doc.createElement( "se:Name" );
225  nameElem.appendChild( doc.createTextNode( "Single symbol" ) );
226  ruleElem.appendChild( nameElem );
227 
228  QgsSymbolLayerV2Utils::applyScaleDependency( doc, ruleElem, locProps );
229 
230  if ( mSymbol.data() ) mSymbol->toSld( doc, ruleElem, locProps );
231 }
232 
234 {
235  Q_UNUSED( context );
236  QgsSymbolV2List lst;
237  lst.append( mSymbol.data() );
238  return lst;
239 }
240 
241 
243 {
244  QDomElement symbolsElem = element.firstChildElement( "symbols" );
245  if ( symbolsElem.isNull() )
246  return nullptr;
247 
248  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
249 
250  if ( !symbolMap.contains( "0" ) )
251  return nullptr;
252 
253  QgsSingleSymbolRendererV2* r = new QgsSingleSymbolRendererV2( symbolMap.take( "0" ) );
254 
255  // delete symbols if there are any more
257 
258  QDomElement rotationElem = element.firstChildElement( "rotation" );
259  if ( !rotationElem.isNull() && !rotationElem.attribute( "field" ).isEmpty() )
260  {
261  convertSymbolRotation( r->mSymbol.data(), rotationElem.attribute( "field" ) );
262  }
263 
264  QDomElement sizeScaleElem = element.firstChildElement( "sizescale" );
265  if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( "field" ).isEmpty() )
266  {
268  QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ),
269  sizeScaleElem.attribute( "field" ) );
270  }
271 
272  // TODO: symbol levels
273  return r;
274 }
275 
277 {
278  // XXX this renderer can handle only one Rule!
279 
280  // get the first Rule element
281  QDomElement ruleElem = element.firstChildElement( "Rule" );
282  if ( ruleElem.isNull() )
283  {
284  QgsDebugMsg( "no Rule elements found!" );
285  return nullptr;
286  }
287 
288  QString label, description;
289  QgsSymbolLayerV2List layers;
290 
291  // retrieve the Rule element child nodes
292  QDomElement childElem = ruleElem.firstChildElement();
293  while ( !childElem.isNull() )
294  {
295  if ( childElem.localName() == "Name" )
296  {
297  // <se:Name> tag contains the rule identifier,
298  // so prefer title tag for the label property value
299  if ( label.isEmpty() )
300  label = childElem.firstChild().nodeValue();
301  }
302  else if ( childElem.localName() == "Description" )
303  {
304  // <se:Description> can contains a title and an abstract
305  QDomElement titleElem = childElem.firstChildElement( "Title" );
306  if ( !titleElem.isNull() )
307  {
308  label = titleElem.firstChild().nodeValue();
309  }
310 
311  QDomElement abstractElem = childElem.firstChildElement( "Abstract" );
312  if ( !abstractElem.isNull() )
313  {
314  description = abstractElem.firstChild().nodeValue();
315  }
316  }
317  else if ( childElem.localName() == "Abstract" )
318  {
319  // <sld:Abstract> (v1.0)
320  description = childElem.firstChild().nodeValue();
321  }
322  else if ( childElem.localName() == "Title" )
323  {
324  // <sld:Title> (v1.0)
325  label = childElem.firstChild().nodeValue();
326  }
327  else if ( childElem.localName().endsWith( "Symbolizer" ) )
328  {
329  // create symbol layers for this symbolizer
330  QgsSymbolLayerV2Utils::createSymbolLayerV2ListFromSld( childElem, geomType, layers );
331  }
332 
333  childElem = childElem.nextSiblingElement();
334  }
335 
336  if ( layers.isEmpty() )
337  return nullptr;
338 
339  // now create the symbol
341  switch ( geomType )
342  {
343  case QGis::Line:
344  symbol = new QgsLineSymbolV2( layers );
345  break;
346 
347  case QGis::Polygon:
348  symbol = new QgsFillSymbolV2( layers );
349  break;
350 
351  case QGis::Point:
352  symbol = new QgsMarkerSymbolV2( layers );
353  break;
354 
355  default:
356  QgsDebugMsg( QString( "invalid geometry type: found %1" ).arg( geomType ) );
357  return nullptr;
358  }
359 
360  // and finally return the new renderer
361  return new QgsSingleSymbolRendererV2( symbol );
362 }
363 
365 {
366  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
367  rendererElem.setAttribute( "type", "singleSymbol" );
368  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
369  rendererElem.setAttribute( "forceraster", ( mForceRaster ? "1" : "0" ) );
370 
372  symbols["0"] = mSymbol.data();
373  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
374  rendererElem.appendChild( symbolsElem );
375 
376  QDomElement rotationElem = doc.createElement( "rotation" );
377  if ( mRotation.data() )
379  rendererElem.appendChild( rotationElem );
380 
381  QDomElement sizeScaleElem = doc.createElement( "sizescale" );
382  if ( mSizeScale.data() )
384  sizeScaleElem.setAttribute( "scalemethod", QgsSymbolLayerV2Utils::encodeScaleMethod( mScaleMethod ) );
385  rendererElem.appendChild( sizeScaleElem );
386 
388  mPaintEffect->saveProperties( doc, rendererElem );
389 
390  if ( !mOrderBy.isEmpty() )
391  {
392  QDomElement orderBy = doc.createElement( "orderby" );
393  mOrderBy.save( orderBy );
394  rendererElem.appendChild( orderBy );
395  }
396  rendererElem.setAttribute( "enableorderby", ( mOrderByEnabled ? "1" : "0" ) );
397 
398  return rendererElem;
399 }
400 
402 {
404  if ( mSymbol.data() )
405  {
407  lst << qMakePair( QString(), pix );
408  }
409  return lst;
410 }
411 
413 {
414  Q_UNUSED( scaleDenominator );
415  Q_UNUSED( rule );
417  lst << qMakePair( QString(), mSymbol.data() );
418  return lst;
419 }
420 
422 {
424  if ( mSymbol->type() == QgsSymbolV2::Marker )
425  {
426  const QgsMarkerSymbolV2 * symbol = static_cast<const QgsMarkerSymbolV2 *>( mSymbol.data() );
427  QgsDataDefined sizeDD = symbol->dataDefinedSize();
428  if ( sizeDD.isActive() && sizeDD.useExpression() )
429  {
430  QgsScaleExpression scaleExp( sizeDD.expressionString() );
431  if ( scaleExp.type() != QgsScaleExpression::Unknown )
432  {
433  QgsLegendSymbolItemV2 title( nullptr, scaleExp.baseExpression(), QString() );
434  lst << title;
435  Q_FOREACH ( double v, QgsSymbolLayerV2Utils::prettyBreaks( scaleExp.minValue(), scaleExp.maxValue(), 4 ) )
436  {
438  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( si.symbol() );
439  s->setDataDefinedSize( 0 );
440  s->setSize( scaleExp.size( v ) );
441  lst << si;
442  }
443  return lst;
444  }
445  }
446  }
447 
449  return lst;
450 }
451 
453 {
454  Q_UNUSED( feature );
455  Q_UNUSED( context );
456  return QSet< QString >() << QString();
457 }
458 
460 {
461  Q_UNUSED( key );
462  setSymbol( symbol );
463 }
464 
466 {
467  QgsSingleSymbolRendererV2* r = nullptr;
468  if ( renderer->type() == "singleSymbol" )
469  {
470  r = dynamic_cast<QgsSingleSymbolRendererV2*>( renderer->clone() );
471  }
472  else if ( renderer->type() == "pointDisplacement" )
473  {
474  const QgsPointDisplacementRenderer* pointDisplacementRenderer = dynamic_cast<const QgsPointDisplacementRenderer*>( renderer );
475  if ( pointDisplacementRenderer )
476  r = convertFromRenderer( pointDisplacementRenderer->embeddedRenderer() );
477  }
478  else if ( renderer->type() == "invertedPolygonRenderer" )
479  {
480  const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
481  if ( invertedPolygonRenderer )
482  r = convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
483  }
484 
485  if ( !r )
486  {
487  QgsRenderContext context;
488  QgsSymbolV2List symbols = const_cast<QgsFeatureRendererV2 *>( renderer )->symbols( context );
489  if ( !symbols.isEmpty() )
490  {
491  r = new QgsSingleSymbolRendererV2( symbols.at( 0 )->clone() );
492  }
493  }
494 
495  if ( r )
496  {
497  r->setOrderBy( renderer->orderBy() );
498  r->setOrderByEnabled( renderer->orderByEnabled() );
499  }
500 
501  return r;
502 }
QgsDataDefined dataDefinedAngle() const
Returns data defined angle for whole symbol (including all symbol layers).
static QDomElement saveSymbols(QgsSymbolV2Map &symbols, const QString &tagName, QDomDocument &doc)
Q_DECL_DEPRECATED void setRotationField(const QString &fieldOrExpression) override
sets rotation field of renderer (if supported by the renderer)
static QgsSymbolV2Map loadSymbols(QDomElement &element)
#define RENDERER_TAG_NAME
Definition: qgsrendererv2.h:49
QgsSymbolV2::ScaleMethod scaleMethod() const
void setDataDefinedAngle(const QgsDataDefined &dd)
Set data defined angle for whole symbol (including all symbol layers).
A container class for data source field mapping or expression.
static QList< double > prettyBreaks(double minimum, double maximum, int classes)
Computes a sequence of about &#39;classes&#39; equally spaced round values which cover the range of values fr...
bool contains(const Key &key) const
GeometryType
Definition: qgis.h:115
static void applyScaleDependency(QDomDocument &doc, QDomElement &ruleElem, const QgsStringMap &props)
Checks if the properties contain scaleMinDenom and scaleMaxDenom, if available, they are added into t...
QDomNode appendChild(const QDomNode &newChild)
virtual QDomElement save(QDomDocument &doc) override
store renderer info to XML element
virtual void stopRender(QgsRenderContext &context) override
Needs to be called when a render cycle has finished to clean up.
QString attribute(const QString &name, const QString &defValue) const
QString nodeValue() const
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void setDataDefinedSize(const QgsDataDefined &dd)
Set data defined size for whole symbol (including all symbol layers).
Class storing parameters of a scale expression, which is a subclass of QgsExpression for expressions ...
static QgsSymbolV2::ScaleMethod decodeScaleMethod(const QString &str)
static QgsFeatureRendererV2 * createFromSld(QDomElement &element, QGis::GeometryType geomType)
const T & at(int i) const
static bool isDefaultStack(QgsPaintEffect *effect)
Tests whether a paint effect matches the default effects stack.
QDomElement nextSiblingElement(const QString &tagName) const
Container of fields for a vector layer.
Definition: qgsfield.h:252
static QgsSingleSymbolRendererV2 * convertFromRenderer(const QgsFeatureRendererV2 *renderer)
creates a QgsSingleSymbolRendererV2 from an existing renderer.
Line symbol.
Definition: qgssymbolv2.h:82
QScopedPointer< QgsSymbolV2 > mSymbol
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:187
virtual QgsSymbolV2 * symbolForFeature(QgsFeature &feature, QgsRenderContext &context) override
QMap< QString, QString > QgsStringMap
Definition: qgis.h:492
QgsPaintEffect * mPaintEffect
QgsSymbolV2::ScaleMethod mScaleMethod
void setWidth(double width)
Marker symbol.
Definition: qgssymbolv2.h:81
virtual void toSld(QDomDocument &doc, QDomElement &element) const override
Writes the SLD element following the SLD v1.1 specs.
void setAngle(double angle)
void reset(T *other)
bool useExpression() const
Returns if the field or the expression part is active.
static bool createSymbolLayerV2ListFromSld(QDomElement &element, QGis::GeometryType geomType, QgsSymbolLayerV2List &layers)
virtual QgsFeatureRendererV2 * clone() const =0
QString number(int n, int base)
void append(const T &value)
QString localName() const
const QgsFeatureRendererV2 * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
QgsInvertedPolygonRenderer is a polygon-only feature renderer used to display features inverted...
void setAttribute(const QString &name, const QString &value)
#define DEFAULT_SCALE_METHOD
void setOrderByEnabled(bool enabled)
Sets whether custom ordering should be applied before features are processed by this renderer...
QString expressionString() const
Returns the expression string of this QgsDataDefined.
bool isEmpty() const
QString type() const
Definition: qgsrendererv2.h:92
bool isEmpty() const
QgsSingleSymbolRendererV2(QgsSymbolV2 *symbol)
void setAngle(double angle)
Sets the angle for the whole symbol.
virtual QList< QString > usedAttributes() override
Returns a set of attributes required for this renderer.
void setSize(double size)
Sets the size for the whole symbol.
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const
bool usingSymbolLevels() const
virtual Q_DECL_DEPRECATED QgsSymbolV2List symbols()
For symbol levels.
QScopedPointer< QgsExpression > mSizeScale
virtual QgsSingleSymbolRendererV2 * clone() const override
static void convertSymbolSizeScale(QgsSymbolV2 *symbol, QgsSymbolV2::ScaleMethod method, const QString &field)
QDomText createTextNode(const QString &value)
T * data() const
virtual QgsLegendSymbologyList legendSymbologyItems(QSize iconSize) override
return a list of symbology items for the legend
virtual QString dump() const override
for debugging
QgsExpressionContext & expressionContext()
Gets the expression context.
QString field() const
Get the field which this QgsDataDefined represents.
A renderer that automatically displaces points with the same position.
bool isNull() const
void setUsingSymbolLevels(bool usingSymbolLevels)
QgsFeatureRequest::OrderBy orderBy() const
Get the order in which features shall be processed by this renderer.
Q_DECL_DEPRECATED QString rotationField() const override
return rotation field name (or empty string if not set or not supported by renderer) ...
void setScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
void copyRendererData(QgsFeatureRendererV2 *destRenderer) const
Clones generic renderer data to another renderer.
Contains information about the context of a rendering operation.
virtual QgsSymbolV2 * originalSymbolForFeature(QgsFeature &feature, QgsRenderContext &context) override
virtual void setLegendSymbolItem(const QString &key, QgsSymbolV2 *symbol) override
Sets the symbol to be used for a legend symbol item.
void setOrderBy(const QgsFeatureRequest::OrderBy &orderBy)
Define the order in which features shall be processed by this renderer.
QDomNode firstChild() const
bool orderByEnabled() const
Returns whether custom ordering will be applied before features are processed by this renderer...
QSet< T > & unite(const QSet< T > &other)
static QgsExpression * fieldOrExpressionToExpression(const QString &fieldOrExpression)
Return a new valid expression instance for given field or expression string.
ScaleMethod
Scale method.
Definition: qgssymbolv2.h:90
QgsDataDefined dataDefinedSize() const
Returns data defined size for whole symbol (including all symbol layers).
static QPixmap symbolPreviewPixmap(QgsSymbolV2 *symbol, QSize size, QgsRenderContext *customContext=nullptr)
QgsFeatureRequest::OrderBy mOrderBy
void setSizeScaleField(const QString &fieldOrExpression)
static QString encodeScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
QDomElement firstChildElement(const QString &tagName) const
static QString fieldOrExpressionFromExpression(QgsExpression *expression)
Return a field name if the whole expression is just a name of the field .
void setScaleMethodToSymbol(QgsSymbolV2 *symbol, int scaleMethod)
void CORE_EXPORT save(QDomElement &elem) const
Serialize to XML.
Fill symbol.
Definition: qgssymbolv2.h:83
QList< T > toList() const
static void clearSymbolMap(QgsSymbolV2Map &symbols)
static QgsFeatureRendererV2 * create(QDomElement &element)
create renderer from XML element
QDomElement createElement(const QString &tagName)
virtual void startRender(QgsRenderContext &context, const QgsFields &fields) override
Needs to be called when a new render cycle is started.
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QScopedPointer< QgsSymbolV2 > mTempSymbol
bool isActive() const
T take(const Key &key)
void setScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
const QgsFeatureRendererV2 * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
virtual QgsLegendSymbolList legendSymbolItems(double scaleDenominator=-1, const QString &rule=QString()) override
return a list of item text / symbol
QScopedPointer< QgsExpression > mRotation
virtual QSet< QString > legendKeysForFeature(QgsFeature &feature, QgsRenderContext &context) override
Return legend keys matching a specified feature.
static void convertSymbolRotation(QgsSymbolV2 *symbol, const QString &field)
virtual QgsLegendSymbolListV2 legendSymbolItemsV2() const override
Return a list of symbology items for the legend.
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.