QGIS API Documentation
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  QgsStringMap props;
211  QString errorMsg;
212  if ( mRotation.data() )
213  props[ "angle" ] = mRotation->expression();
214  if ( mSizeScale.data() )
215  props[ "scale" ] = mSizeScale->expression();
216 
217  QDomElement ruleElem = doc.createElement( "se:Rule" );
218  element.appendChild( ruleElem );
219 
220  QDomElement nameElem = doc.createElement( "se:Name" );
221  nameElem.appendChild( doc.createTextNode( "Single symbol" ) );
222  ruleElem.appendChild( nameElem );
223 
224  if ( mSymbol.data() ) mSymbol->toSld( doc, ruleElem, props );
225 }
226 
228 {
229  Q_UNUSED( context );
230  QgsSymbolV2List lst;
231  lst.append( mSymbol.data() );
232  return lst;
233 }
234 
235 
237 {
238  QDomElement symbolsElem = element.firstChildElement( "symbols" );
239  if ( symbolsElem.isNull() )
240  return nullptr;
241 
242  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
243 
244  if ( !symbolMap.contains( "0" ) )
245  return nullptr;
246 
247  QgsSingleSymbolRendererV2* r = new QgsSingleSymbolRendererV2( symbolMap.take( "0" ) );
248 
249  // delete symbols if there are any more
251 
252  QDomElement rotationElem = element.firstChildElement( "rotation" );
253  if ( !rotationElem.isNull() && !rotationElem.attribute( "field" ).isEmpty() )
254  {
255  convertSymbolRotation( r->mSymbol.data(), rotationElem.attribute( "field" ) );
256  }
257 
258  QDomElement sizeScaleElem = element.firstChildElement( "sizescale" );
259  if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( "field" ).isEmpty() )
260  {
262  QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ),
263  sizeScaleElem.attribute( "field" ) );
264  }
265 
266  // TODO: symbol levels
267  return r;
268 }
269 
271 {
272  // XXX this renderer can handle only one Rule!
273 
274  // get the first Rule element
275  QDomElement ruleElem = element.firstChildElement( "Rule" );
276  if ( ruleElem.isNull() )
277  {
278  QgsDebugMsg( "no Rule elements found!" );
279  return nullptr;
280  }
281 
282  QString label, description;
283  QgsSymbolLayerV2List layers;
284 
285  // retrieve the Rule element child nodes
286  QDomElement childElem = ruleElem.firstChildElement();
287  while ( !childElem.isNull() )
288  {
289  if ( childElem.localName() == "Name" )
290  {
291  // <se:Name> tag contains the rule identifier,
292  // so prefer title tag for the label property value
293  if ( label.isEmpty() )
294  label = childElem.firstChild().nodeValue();
295  }
296  else if ( childElem.localName() == "Description" )
297  {
298  // <se:Description> can contains a title and an abstract
299  QDomElement titleElem = childElem.firstChildElement( "Title" );
300  if ( !titleElem.isNull() )
301  {
302  label = titleElem.firstChild().nodeValue();
303  }
304 
305  QDomElement abstractElem = childElem.firstChildElement( "Abstract" );
306  if ( !abstractElem.isNull() )
307  {
308  description = abstractElem.firstChild().nodeValue();
309  }
310  }
311  else if ( childElem.localName() == "Abstract" )
312  {
313  // <sld:Abstract> (v1.0)
314  description = childElem.firstChild().nodeValue();
315  }
316  else if ( childElem.localName() == "Title" )
317  {
318  // <sld:Title> (v1.0)
319  label = childElem.firstChild().nodeValue();
320  }
321  else if ( childElem.localName().endsWith( "Symbolizer" ) )
322  {
323  // create symbol layers for this symbolizer
324  QgsSymbolLayerV2Utils::createSymbolLayerV2ListFromSld( childElem, geomType, layers );
325  }
326 
327  childElem = childElem.nextSiblingElement();
328  }
329 
330  if ( layers.isEmpty() )
331  return nullptr;
332 
333  // now create the symbol
335  switch ( geomType )
336  {
337  case QGis::Line:
338  symbol = new QgsLineSymbolV2( layers );
339  break;
340 
341  case QGis::Polygon:
342  symbol = new QgsFillSymbolV2( layers );
343  break;
344 
345  case QGis::Point:
346  symbol = new QgsMarkerSymbolV2( layers );
347  break;
348 
349  default:
350  QgsDebugMsg( QString( "invalid geometry type: found %1" ).arg( geomType ) );
351  return nullptr;
352  }
353 
354  // and finally return the new renderer
355  return new QgsSingleSymbolRendererV2( symbol );
356 }
357 
359 {
360  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
361  rendererElem.setAttribute( "type", "singleSymbol" );
362  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
363  rendererElem.setAttribute( "forceraster", ( mForceRaster ? "1" : "0" ) );
364 
366  symbols["0"] = mSymbol.data();
367  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
368  rendererElem.appendChild( symbolsElem );
369 
370  QDomElement rotationElem = doc.createElement( "rotation" );
371  if ( mRotation.data() )
373  rendererElem.appendChild( rotationElem );
374 
375  QDomElement sizeScaleElem = doc.createElement( "sizescale" );
376  if ( mSizeScale.data() )
378  sizeScaleElem.setAttribute( "scalemethod", QgsSymbolLayerV2Utils::encodeScaleMethod( mScaleMethod ) );
379  rendererElem.appendChild( sizeScaleElem );
380 
382  mPaintEffect->saveProperties( doc, rendererElem );
383 
384  if ( !mOrderBy.isEmpty() )
385  {
386  QDomElement orderBy = doc.createElement( "orderby" );
387  mOrderBy.save( orderBy );
388  rendererElem.appendChild( orderBy );
389  }
390  rendererElem.setAttribute( "enableorderby", ( mOrderByEnabled ? "1" : "0" ) );
391 
392  return rendererElem;
393 }
394 
396 {
398  if ( mSymbol.data() )
399  {
401  lst << qMakePair( QString(), pix );
402  }
403  return lst;
404 }
405 
407 {
408  Q_UNUSED( scaleDenominator );
409  Q_UNUSED( rule );
411  lst << qMakePair( QString(), mSymbol.data() );
412  return lst;
413 }
414 
416 {
418  if ( mSymbol->type() == QgsSymbolV2::Marker )
419  {
420  const QgsMarkerSymbolV2 * symbol = static_cast<const QgsMarkerSymbolV2 *>( mSymbol.data() );
421  QgsDataDefined sizeDD = symbol->dataDefinedSize();
422  if ( sizeDD.isActive() && sizeDD.useExpression() )
423  {
424  QgsScaleExpression scaleExp( sizeDD.expressionString() );
425  if ( scaleExp.type() != QgsScaleExpression::Unknown )
426  {
427  QgsLegendSymbolItemV2 title( nullptr, scaleExp.baseExpression(), nullptr );
428  lst << title;
429  Q_FOREACH ( double v, QgsSymbolLayerV2Utils::prettyBreaks( scaleExp.minValue(), scaleExp.maxValue(), 4 ) )
430  {
431  QgsLegendSymbolItemV2 si( mSymbol.data(), QString::number( v ), nullptr );
432  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( si.symbol() );
433  s->setDataDefinedSize( 0 );
434  s->setSize( scaleExp.size( v ) );
435  lst << si;
436  }
437  return lst;
438  }
439  }
440  }
441 
442  lst << QgsLegendSymbolItemV2( mSymbol.data(), QString(), nullptr );
443  return lst;
444 }
445 
447 {
448  Q_UNUSED( feature );
449  Q_UNUSED( context );
450  return QSet< QString >() << QString();
451 }
452 
454 {
455  Q_UNUSED( key );
456  setSymbol( symbol );
457 }
458 
460 {
461  QgsSingleSymbolRendererV2* r = nullptr;
462  if ( renderer->type() == "singleSymbol" )
463  {
464  r = dynamic_cast<QgsSingleSymbolRendererV2*>( renderer->clone() );
465  }
466  else if ( renderer->type() == "pointDisplacement" )
467  {
468  const QgsPointDisplacementRenderer* pointDisplacementRenderer = dynamic_cast<const QgsPointDisplacementRenderer*>( renderer );
469  if ( pointDisplacementRenderer )
470  r = convertFromRenderer( pointDisplacementRenderer->embeddedRenderer() );
471  }
472  else if ( renderer->type() == "invertedPolygonRenderer" )
473  {
474  const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
475  if ( invertedPolygonRenderer )
476  r = convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
477  }
478 
479  if ( !r )
480  {
481  QgsRenderContext context;
482  QgsSymbolV2List symbols = const_cast<QgsFeatureRendererV2 *>( renderer )->symbols( context );
483  if ( !symbols.isEmpty() )
484  {
485  r = new QgsSingleSymbolRendererV2( symbols.at( 0 )->clone() );
486  }
487  }
488 
489  if ( r )
490  {
491  r->setOrderBy( renderer->orderBy() );
492  r->setOrderByEnabled( renderer->orderByEnabled() );
493  }
494 
495  return r;
496 }
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
void CORE_EXPORT save(QDomElement &elem) const
Serialize to XML.
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:111
QgsFeatureRequest::OrderBy orderBy() const
Get the order in which features shall be processed by this renderer.
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 field() const
Get the field which this QgsDataDefined represents.
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 ...
bool orderByEnabled() const
Returns whether custom ordering will be applied before features are processed by this renderer...
QgsDataDefined dataDefinedSize() const
Returns data defined size for whole symbol (including all symbol layers).
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.
QgsSymbolV2::ScaleMethod scaleMethod() const
QDomElement nextSiblingElement(const QString &tagName) const
Container of fields for a vector layer.
Definition: qgsfield.h:187
static QgsSingleSymbolRendererV2 * convertFromRenderer(const QgsFeatureRendererV2 *renderer)
creates a QgsSingleSymbolRendererV2 from an existing renderer.
Line symbol.
Definition: qgssymbolv2.h:79
QString expressionString() const
Returns the expression string of this QgsDataDefined.
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
QgsPaintEffect * mPaintEffect
QgsSymbolV2::ScaleMethod mScaleMethod
void setWidth(double width)
Marker symbol.
Definition: qgssymbolv2.h:78
virtual void toSld(QDomDocument &doc, QDomElement &element) const override
used from subclasses to create SLD Rule elements following SLD v1.1 specs
void setAngle(double angle)
void reset(T *other)
QString type() const
Definition: qgsrendererv2.h:86
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
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...
bool isEmpty() const
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
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)
QgsFeatureRendererV2 * embeddedRenderer() const
bool useExpression() const
Returns if the field or the expression part is active.
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.
void copyRendererData(QgsFeatureRendererV2 *destRenderer) const
Clones generic renderer data to another renderer.
A renderer that automatically displaces points with the same position.
bool isNull() const
void setUsingSymbolLevels(bool usingSymbolLevels)
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.
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)
Contains information about the context of a rendering operation.
const QgsFeatureRendererV2 * embeddedRenderer() const
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
QgsDataDefined dataDefinedAngle() const
Returns data defined angle for whole symbol (including all symbol layers).
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:87
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 .
bool usingSymbolLevels() const
void setScaleMethodToSymbol(QgsSymbolV2 *symbol, int scaleMethod)
Fill symbol.
Definition: qgssymbolv2.h:80
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)
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.