QGIS API Documentation  2.15.0-Master (1f0fce7)
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  if ( mRotation.data() )
212  props[ "angle" ] = mRotation->expression();
213  if ( mSizeScale.data() )
214  props[ "scale" ] = mSizeScale->expression();
215 
216  QDomElement ruleElem = doc.createElement( "se:Rule" );
217  element.appendChild( ruleElem );
218 
219  QDomElement nameElem = doc.createElement( "se:Name" );
220  nameElem.appendChild( doc.createTextNode( "Single symbol" ) );
221  ruleElem.appendChild( nameElem );
222 
223  if ( mSymbol.data() ) mSymbol->toSld( doc, ruleElem, props );
224 }
225 
227 {
228  Q_UNUSED( context );
229  QgsSymbolV2List lst;
230  lst.append( mSymbol.data() );
231  return lst;
232 }
233 
234 
236 {
237  QDomElement symbolsElem = element.firstChildElement( "symbols" );
238  if ( symbolsElem.isNull() )
239  return nullptr;
240 
241  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
242 
243  if ( !symbolMap.contains( "0" ) )
244  return nullptr;
245 
246  QgsSingleSymbolRendererV2* r = new QgsSingleSymbolRendererV2( symbolMap.take( "0" ) );
247 
248  // delete symbols if there are any more
250 
251  QDomElement rotationElem = element.firstChildElement( "rotation" );
252  if ( !rotationElem.isNull() && !rotationElem.attribute( "field" ).isEmpty() )
253  {
254  convertSymbolRotation( r->mSymbol.data(), rotationElem.attribute( "field" ) );
255  }
256 
257  QDomElement sizeScaleElem = element.firstChildElement( "sizescale" );
258  if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( "field" ).isEmpty() )
259  {
261  QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ),
262  sizeScaleElem.attribute( "field" ) );
263  }
264 
265  // TODO: symbol levels
266  return r;
267 }
268 
270 {
271  // XXX this renderer can handle only one Rule!
272 
273  // get the first Rule element
274  QDomElement ruleElem = element.firstChildElement( "Rule" );
275  if ( ruleElem.isNull() )
276  {
277  QgsDebugMsg( "no Rule elements found!" );
278  return nullptr;
279  }
280 
281  QString label, description;
282  QgsSymbolLayerV2List layers;
283 
284  // retrieve the Rule element child nodes
285  QDomElement childElem = ruleElem.firstChildElement();
286  while ( !childElem.isNull() )
287  {
288  if ( childElem.localName() == "Name" )
289  {
290  // <se:Name> tag contains the rule identifier,
291  // so prefer title tag for the label property value
292  if ( label.isEmpty() )
293  label = childElem.firstChild().nodeValue();
294  }
295  else if ( childElem.localName() == "Description" )
296  {
297  // <se:Description> can contains a title and an abstract
298  QDomElement titleElem = childElem.firstChildElement( "Title" );
299  if ( !titleElem.isNull() )
300  {
301  label = titleElem.firstChild().nodeValue();
302  }
303 
304  QDomElement abstractElem = childElem.firstChildElement( "Abstract" );
305  if ( !abstractElem.isNull() )
306  {
307  description = abstractElem.firstChild().nodeValue();
308  }
309  }
310  else if ( childElem.localName() == "Abstract" )
311  {
312  // <sld:Abstract> (v1.0)
313  description = childElem.firstChild().nodeValue();
314  }
315  else if ( childElem.localName() == "Title" )
316  {
317  // <sld:Title> (v1.0)
318  label = childElem.firstChild().nodeValue();
319  }
320  else if ( childElem.localName().endsWith( "Symbolizer" ) )
321  {
322  // create symbol layers for this symbolizer
323  QgsSymbolLayerV2Utils::createSymbolLayerV2ListFromSld( childElem, geomType, layers );
324  }
325 
326  childElem = childElem.nextSiblingElement();
327  }
328 
329  if ( layers.isEmpty() )
330  return nullptr;
331 
332  // now create the symbol
334  switch ( geomType )
335  {
336  case QGis::Line:
337  symbol = new QgsLineSymbolV2( layers );
338  break;
339 
340  case QGis::Polygon:
341  symbol = new QgsFillSymbolV2( layers );
342  break;
343 
344  case QGis::Point:
345  symbol = new QgsMarkerSymbolV2( layers );
346  break;
347 
348  default:
349  QgsDebugMsg( QString( "invalid geometry type: found %1" ).arg( geomType ) );
350  return nullptr;
351  }
352 
353  // and finally return the new renderer
354  return new QgsSingleSymbolRendererV2( symbol );
355 }
356 
358 {
359  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
360  rendererElem.setAttribute( "type", "singleSymbol" );
361  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
362  rendererElem.setAttribute( "forceraster", ( mForceRaster ? "1" : "0" ) );
363 
365  symbols["0"] = mSymbol.data();
366  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
367  rendererElem.appendChild( symbolsElem );
368 
369  QDomElement rotationElem = doc.createElement( "rotation" );
370  if ( mRotation.data() )
372  rendererElem.appendChild( rotationElem );
373 
374  QDomElement sizeScaleElem = doc.createElement( "sizescale" );
375  if ( mSizeScale.data() )
377  sizeScaleElem.setAttribute( "scalemethod", QgsSymbolLayerV2Utils::encodeScaleMethod( mScaleMethod ) );
378  rendererElem.appendChild( sizeScaleElem );
379 
381  mPaintEffect->saveProperties( doc, rendererElem );
382 
383  if ( !mOrderBy.isEmpty() )
384  {
385  QDomElement orderBy = doc.createElement( "orderby" );
386  mOrderBy.save( orderBy );
387  rendererElem.appendChild( orderBy );
388  }
389  rendererElem.setAttribute( "enableorderby", ( mOrderByEnabled ? "1" : "0" ) );
390 
391  return rendererElem;
392 }
393 
395 {
397  if ( mSymbol.data() )
398  {
400  lst << qMakePair( QString(), pix );
401  }
402  return lst;
403 }
404 
406 {
407  Q_UNUSED( scaleDenominator );
408  Q_UNUSED( rule );
410  lst << qMakePair( QString(), mSymbol.data() );
411  return lst;
412 }
413 
415 {
417  if ( mSymbol->type() == QgsSymbolV2::Marker )
418  {
419  const QgsMarkerSymbolV2 * symbol = static_cast<const QgsMarkerSymbolV2 *>( mSymbol.data() );
420  QgsDataDefined sizeDD = symbol->dataDefinedSize();
421  if ( sizeDD.isActive() && sizeDD.useExpression() )
422  {
423  QgsScaleExpression scaleExp( sizeDD.expressionString() );
424  if ( scaleExp.type() != QgsScaleExpression::Unknown )
425  {
426  QgsLegendSymbolItemV2 title( nullptr, scaleExp.baseExpression(), QString() );
427  lst << title;
428  Q_FOREACH ( double v, QgsSymbolLayerV2Utils::prettyBreaks( scaleExp.minValue(), scaleExp.maxValue(), 4 ) )
429  {
431  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( si.symbol() );
432  s->setDataDefinedSize( 0 );
433  s->setSize( scaleExp.size( v ) );
434  lst << si;
435  }
436  return lst;
437  }
438  }
439  }
440 
442  return lst;
443 }
444 
446 {
447  Q_UNUSED( feature );
448  Q_UNUSED( context );
449  return QSet< QString >() << QString();
450 }
451 
453 {
454  Q_UNUSED( key );
455  setSymbol( symbol );
456 }
457 
459 {
460  QgsSingleSymbolRendererV2* r = nullptr;
461  if ( renderer->type() == "singleSymbol" )
462  {
463  r = dynamic_cast<QgsSingleSymbolRendererV2*>( renderer->clone() );
464  }
465  else if ( renderer->type() == "pointDisplacement" )
466  {
467  const QgsPointDisplacementRenderer* pointDisplacementRenderer = dynamic_cast<const QgsPointDisplacementRenderer*>( renderer );
468  if ( pointDisplacementRenderer )
469  r = convertFromRenderer( pointDisplacementRenderer->embeddedRenderer() );
470  }
471  else if ( renderer->type() == "invertedPolygonRenderer" )
472  {
473  const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
474  if ( invertedPolygonRenderer )
475  r = convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
476  }
477 
478  if ( !r )
479  {
480  QgsRenderContext context;
481  QgsSymbolV2List symbols = const_cast<QgsFeatureRendererV2 *>( renderer )->symbols( context );
482  if ( !symbols.isEmpty() )
483  {
484  r = new QgsSingleSymbolRendererV2( symbols.at( 0 )->clone() );
485  }
486  }
487 
488  if ( r )
489  {
490  r->setOrderBy( renderer->orderBy() );
491  r->setOrderByEnabled( renderer->orderByEnabled() );
492  }
493 
494  return r;
495 }
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:115
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:193
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
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...
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)
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.
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)
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.