QGIS API Documentation  2.13.0-Master
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  return rendererElem;
391 }
392 
394 {
396  if ( mSymbol.data() )
397  {
399  lst << qMakePair( QString(), pix );
400  }
401  return lst;
402 }
403 
405 {
406  Q_UNUSED( scaleDenominator );
407  Q_UNUSED( rule );
409  lst << qMakePair( QString(), mSymbol.data() );
410  return lst;
411 }
412 
414 {
416  if ( mSymbol->type() == QgsSymbolV2::Marker )
417  {
418  const QgsMarkerSymbolV2 * symbol = static_cast<const QgsMarkerSymbolV2 *>( mSymbol.data() );
419  QgsDataDefined sizeDD = symbol->dataDefinedSize();
420  if ( sizeDD.isActive() && sizeDD.useExpression() )
421  {
422  QgsScaleExpression scaleExp( sizeDD.expressionString() );
423  if ( scaleExp.type() != QgsScaleExpression::Unknown )
424  {
425  QgsLegendSymbolItemV2 title( nullptr, scaleExp.baseExpression(), nullptr );
426  lst << title;
427  Q_FOREACH ( double v, QgsSymbolLayerV2Utils::prettyBreaks( scaleExp.minValue(), scaleExp.maxValue(), 4 ) )
428  {
429  QgsLegendSymbolItemV2 si( mSymbol.data(), QString::number( v ), nullptr );
430  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( si.symbol() );
431  s->setDataDefinedSize( 0 );
432  s->setSize( scaleExp.size( v ) );
433  lst << si;
434  }
435  return lst;
436  }
437  }
438  }
439 
440  lst << QgsLegendSymbolItemV2( mSymbol.data(), QString(), nullptr );
441  return lst;
442 }
443 
445 {
446  Q_UNUSED( feature );
447  Q_UNUSED( context );
448  return QSet< QString >() << QString();
449 }
450 
452 {
453  QgsSingleSymbolRendererV2* r = nullptr;
454  if ( renderer->type() == "singleSymbol" )
455  {
456  r = dynamic_cast<QgsSingleSymbolRendererV2*>( renderer->clone() );
457  }
458  else if ( renderer->type() == "pointDisplacement" )
459  {
460  const QgsPointDisplacementRenderer* pointDisplacementRenderer = dynamic_cast<const QgsPointDisplacementRenderer*>( renderer );
461  if ( pointDisplacementRenderer )
462  r = convertFromRenderer( pointDisplacementRenderer->embeddedRenderer() );
463  }
464  else if ( renderer->type() == "invertedPolygonRenderer" )
465  {
466  const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
467  if ( invertedPolygonRenderer )
468  r = convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
469  }
470 
471  if ( !r )
472  {
473  QgsRenderContext context;
474  QgsSymbolV2List symbols = const_cast<QgsFeatureRendererV2 *>( renderer )->symbols( context );
475  if ( !symbols.isEmpty() )
476  {
477  r = new QgsSingleSymbolRendererV2( symbols.at( 0 )->clone() );
478  }
479  }
480 
481  if ( r )
482  {
483  r->setOrderBy( renderer->orderBy() );
484  }
485 
486  return r;
487 }
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 ...
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:189
static QgsSingleSymbolRendererV2 * convertFromRenderer(const QgsFeatureRendererV2 *renderer)
creates a QgsSingleSymbolRendererV2 from an existing renderer.
Line symbol.
Definition: qgssymbolv2.h:76
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:75
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:83
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
bool isEmpty() const
bool isEmpty() const
QgsSingleSymbolRendererV2(QgsSymbolV2 *symbol)
void setAngle(double angle)
virtual QList< QString > usedAttributes() override
Returns a set of attributes required for this renderer.
void setSize(double size)
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
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:84
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:77
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.