QGIS API Documentation  2.9.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 "qgsscaleexpression.h"
30 #include "qgsdatadefined.h"
31 
32 #include <QDomDocument>
33 #include <QDomElement>
34 
36  : QgsFeatureRendererV2( "singleSymbol" )
37  , mSymbol( symbol )
38  , mScaleMethod( DEFAULT_SCALE_METHOD )
39  , mOrigSize( 0.0 )
40 {
41  Q_ASSERT( symbol );
42 }
43 
45 {
46 }
47 
49 {
50  if ( !mRotation.data() && !mSizeScale.data() ) return mSymbol.data();
51 
52  const double rotation = mRotation.data() ? mRotation->evaluate( feature ).toDouble() : 0;
53  const double sizeScale = mSizeScale.data() ? mSizeScale->evaluate( feature ).toDouble() : 1.;
54 
55  if ( mTempSymbol->type() == QgsSymbolV2::Marker )
56  {
57  QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( mTempSymbol.data() );
58  if ( mRotation.data() ) markerSymbol->setAngle( rotation );
59  markerSymbol->setSize( sizeScale * mOrigSize );
60  markerSymbol->setScaleMethod( mScaleMethod );
61  }
62  else if ( mTempSymbol->type() == QgsSymbolV2::Line )
63  {
64  QgsLineSymbolV2* lineSymbol = static_cast<QgsLineSymbolV2*>( mTempSymbol.data() );
65  lineSymbol->setWidth( sizeScale * mOrigSize );
66  }
67  else if ( mTempSymbol->type() == QgsSymbolV2::Fill )
68  {
69  QgsFillSymbolV2* fillSymbol = static_cast<QgsFillSymbolV2*>( mTempSymbol.data() );
70  if ( mRotation.data() ) fillSymbol->setAngle( rotation );
71  }
72 
73  return mTempSymbol.data();
74 }
75 
77 {
78  Q_UNUSED( feature );
79  return mSymbol.data();
80 }
81 
83 {
84  if ( !mSymbol.data() ) return;
85 
86  mSymbol->startRender( context, &fields );
87 
88  if ( mRotation.data() || mSizeScale.data() )
89  {
90  // we are going to need a temporary symbol
91  mTempSymbol.reset( mSymbol->clone() );
92 
93  int hints = 0;
94  if ( mRotation.data() )
96  if ( mSizeScale.data() )
98  mTempSymbol->setRenderHints( hints );
99 
100  mTempSymbol->startRender( context, &fields );
101 
102  if ( mSymbol->type() == QgsSymbolV2::Marker )
103  {
104  mOrigSize = static_cast<QgsMarkerSymbolV2*>( mSymbol.data() )->size();
105  }
106  else if ( mSymbol->type() == QgsSymbolV2::Line )
107  {
108  mOrigSize = static_cast<QgsLineSymbolV2*>( mSymbol.data() )->width();
109  }
110  else
111  {
112  mOrigSize = 0;
113  }
114  }
115 }
116 
118 {
119  if ( !mSymbol.data() ) return;
120 
121  mSymbol->stopRender( context );
122 
123  if ( mRotation.data() || mSizeScale.data() )
124  {
125  // we are going to need a temporary symbol
126  mTempSymbol->stopRender( context );
127  mTempSymbol.reset();
128  }
129 }
130 
132 {
133  QSet<QString> attributes;
134  if ( mSymbol.data() ) attributes.unite( mSymbol->usedAttributes() );
135  if ( mRotation.data() ) attributes.unite( mRotation->referencedColumns().toSet() );
136  if ( mSizeScale.data() ) attributes.unite( mSizeScale->referencedColumns().toSet() );
137  return attributes.toList();
138 }
139 
141 {
142  return mSymbol.data();
143 }
144 
146 {
147  Q_ASSERT( s );
148  mSymbol.reset( s );
149 }
150 
151 void QgsSingleSymbolRendererV2::setRotationField( QString fieldOrExpression )
152 {
154 }
155 
157 {
158  return mRotation.data() ? QgsSymbolLayerV2Utils::fieldOrExpressionFromExpression( mRotation.data() ) : QString();
159 }
160 
161 void QgsSingleSymbolRendererV2::setSizeScaleField( QString fieldOrExpression )
162 {
164 }
165 
167 {
169 }
170 
172 {
175 }
176 
178 {
179  return mSymbol.data() ? QString( "SINGLE: %1" ).arg( mSymbol->dump() ) : "";
180 }
181 
183 {
188  r->setScaleMethod( scaleMethod() );
189  copyPaintEffect( r );
190  return r;
191 }
192 
193 void QgsSingleSymbolRendererV2::toSld( QDomDocument& doc, QDomElement &element ) const
194 {
195  QgsStringMap props;
196  QString errorMsg;
197  if ( mRotation.data() )
198  props[ "angle" ] = mRotation->expression();
199  if ( mSizeScale.data() )
200  props[ "scale" ] = mSizeScale->expression();
201 
202  QDomElement ruleElem = doc.createElement( "se:Rule" );
203  element.appendChild( ruleElem );
204 
205  QDomElement nameElem = doc.createElement( "se:Name" );
206  nameElem.appendChild( doc.createTextNode( "Single symbol" ) );
207  ruleElem.appendChild( nameElem );
208 
209  if ( mSymbol.data() ) mSymbol->toSld( doc, ruleElem, props );
210 }
211 
213 {
214  QgsSymbolV2List lst;
215  lst.append( mSymbol.data() );
216  return lst;
217 }
218 
220 {
221  QDomElement symbolsElem = element.firstChildElement( "symbols" );
222  if ( symbolsElem.isNull() )
223  return NULL;
224 
225  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
226 
227  if ( !symbolMap.contains( "0" ) )
228  return NULL;
229 
230  QgsSingleSymbolRendererV2* r = new QgsSingleSymbolRendererV2( symbolMap.take( "0" ) );
231 
232  // delete symbols if there are any more
234 
235  QDomElement rotationElem = element.firstChildElement( "rotation" );
236  if ( !rotationElem.isNull() )
237  r->setRotationField( rotationElem.attribute( "field" ) );
238 
239  QDomElement sizeScaleElem = element.firstChildElement( "sizescale" );
240  if ( !sizeScaleElem.isNull() )
241  {
242  r->setSizeScaleField( sizeScaleElem.attribute( "field" ) );
243  r->setScaleMethod( QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ) );
244  }
245 
246  // TODO: symbol levels
247  return r;
248 }
249 
251 {
252  // XXX this renderer can handle only one Rule!
253 
254  // get the first Rule element
255  QDomElement ruleElem = element.firstChildElement( "Rule" );
256  if ( ruleElem.isNull() )
257  {
258  QgsDebugMsg( "no Rule elements found!" );
259  return NULL;
260  }
261 
262  QString label, description;
263  QgsSymbolLayerV2List layers;
264 
265  // retrieve the Rule element child nodes
266  QDomElement childElem = ruleElem.firstChildElement();
267  while ( !childElem.isNull() )
268  {
269  if ( childElem.localName() == "Name" )
270  {
271  // <se:Name> tag contains the rule identifier,
272  // so prefer title tag for the label property value
273  if ( label.isEmpty() )
274  label = childElem.firstChild().nodeValue();
275  }
276  else if ( childElem.localName() == "Description" )
277  {
278  // <se:Description> can contains a title and an abstract
279  QDomElement titleElem = childElem.firstChildElement( "Title" );
280  if ( !titleElem.isNull() )
281  {
282  label = titleElem.firstChild().nodeValue();
283  }
284 
285  QDomElement abstractElem = childElem.firstChildElement( "Abstract" );
286  if ( !abstractElem.isNull() )
287  {
288  description = abstractElem.firstChild().nodeValue();
289  }
290  }
291  else if ( childElem.localName() == "Abstract" )
292  {
293  // <sld:Abstract> (v1.0)
294  description = childElem.firstChild().nodeValue();
295  }
296  else if ( childElem.localName() == "Title" )
297  {
298  // <sld:Title> (v1.0)
299  label = childElem.firstChild().nodeValue();
300  }
301  else if ( childElem.localName().endsWith( "Symbolizer" ) )
302  {
303  // create symbol layers for this symbolizer
304  QgsSymbolLayerV2Utils::createSymbolLayerV2ListFromSld( childElem, geomType, layers );
305  }
306 
307  childElem = childElem.nextSiblingElement();
308  }
309 
310  if ( layers.size() == 0 )
311  return NULL;
312 
313  // now create the symbol
315  switch ( geomType )
316  {
317  case QGis::Line:
318  symbol = new QgsLineSymbolV2( layers );
319  break;
320 
321  case QGis::Polygon:
322  symbol = new QgsFillSymbolV2( layers );
323  break;
324 
325  case QGis::Point:
326  symbol = new QgsMarkerSymbolV2( layers );
327  break;
328 
329  default:
330  QgsDebugMsg( QString( "invalid geometry type: found %1" ).arg( geomType ) );
331  return NULL;
332  }
333 
334  // and finally return the new renderer
335  return new QgsSingleSymbolRendererV2( symbol );
336 }
337 
338 QDomElement QgsSingleSymbolRendererV2::save( QDomDocument& doc )
339 {
340  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
341  rendererElem.setAttribute( "type", "singleSymbol" );
342  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
343 
345  symbols["0"] = mSymbol.data();
346  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
347  rendererElem.appendChild( symbolsElem );
348 
349  QDomElement rotationElem = doc.createElement( "rotation" );
350  if ( mRotation.data() )
351  rotationElem.setAttribute( "field", QgsSymbolLayerV2Utils::fieldOrExpressionFromExpression( mRotation.data() ) );
352  rendererElem.appendChild( rotationElem );
353 
354  QDomElement sizeScaleElem = doc.createElement( "sizescale" );
355  if ( mSizeScale.data() )
356  sizeScaleElem.setAttribute( "field", QgsSymbolLayerV2Utils::fieldOrExpressionFromExpression( mSizeScale.data() ) );
357  sizeScaleElem.setAttribute( "scalemethod", QgsSymbolLayerV2Utils::encodeScaleMethod( mScaleMethod ) );
358  rendererElem.appendChild( sizeScaleElem );
359 
360  if ( mPaintEffect )
361  mPaintEffect->saveProperties( doc, rendererElem );
362 
363  return rendererElem;
364 }
365 
367 {
369  if ( mSymbol.data() )
370  {
371  QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( mSymbol.data(), iconSize );
372  lst << qMakePair( QString(), pix );
373  }
374  return lst;
375 }
376 
378 {
379  Q_UNUSED( scaleDenominator );
380  Q_UNUSED( rule );
382  lst << qMakePair( QString(), mSymbol.data() );
383  return lst;
384 }
385 
387 {
389  if ( mSymbol->type() == QgsSymbolV2::Marker )
390  {
391  const QgsMarkerSymbolV2 * symbol = static_cast<const QgsMarkerSymbolV2 *>( mSymbol.data() );
392  QgsDataDefined sizeDD = symbol->dataDefinedSize();
393  if ( sizeDD.isActive() && sizeDD.useExpression() )
394  {
395  QgsScaleExpression scaleExp( sizeDD.expressionString() );
396  if ( scaleExp.type() != QgsScaleExpression::Unknown )
397  {
398  QgsLegendSymbolItemV2 title( NULL, scaleExp.baseExpression(), 0 );
399  lst << title;
400  foreach ( double v, QgsSymbolLayerV2Utils::prettyBreaks( scaleExp.minValue(), scaleExp.maxValue(), 4 ) )
401  {
402  QgsLegendSymbolItemV2 si( mSymbol.data(), QString::number( v ), 0 );
403  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( si.symbol() );
404  s->setDataDefinedSize( 0 );
405  s->setSize( scaleExp.size( v ) );
406  lst << si;
407  }
408  return lst;
409  }
410  }
411  }
412 
413  lst << QgsLegendSymbolItemV2( mSymbol.data(), QString(), 0 );
414  return lst;
415 }
416 
418 {
419  if ( renderer->type() == "singleSymbol" )
420  {
421  return dynamic_cast<QgsSingleSymbolRendererV2*>( renderer->clone() );
422  }
423  if ( renderer->type() == "pointDisplacement" )
424  {
425  const QgsPointDisplacementRenderer* pointDisplacementRenderer = dynamic_cast<const QgsPointDisplacementRenderer*>( renderer );
426  if ( pointDisplacementRenderer )
427  return convertFromRenderer( pointDisplacementRenderer->embeddedRenderer() );
428  }
429  if ( renderer->type() == "invertedPolygonRenderer" )
430  {
431  const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
432  if ( invertedPolygonRenderer )
433  return convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
434  }
435 
436  QgsSymbolV2List symbols = const_cast<QgsFeatureRendererV2 *>( renderer )->symbols();
437  if ( symbols.size() > 0 )
438  {
439  return new QgsSingleSymbolRendererV2( symbols.at( 0 )->clone() );
440  }
441  return 0;
442 }
QMap< QString, QgsSymbolV2 * > QgsSymbolV2Map
Definition: qgsrendererv2.h:40
static QgsSymbolV2Map loadSymbols(QDomElement &element)
#define RENDERER_TAG_NAME
Definition: qgsrendererv2.h:48
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 'classes' equally spaced round values which cover the range of values fr...
QList< QgsSymbolV2 * > QgsSymbolV2List
Definition: qgsrendererv2.h:39
virtual QDomElement save(QDomDocument &doc) override
store renderer info to XML element
virtual void stopRender(QgsRenderContext &context) override
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void setDataDefinedSize(const QgsDataDefined &dd)
Set data defined size for whole symbol (including all symbol layers).
virtual QgsSymbolV2List symbols() override
for symbol levels
virtual QgsSymbolV2 * symbolForFeature(QgsFeature &feature) override
to be overridden
Class storing parameters of a scale expression, which is a subclass of QgsExpression for expressions ...
virtual QgsSymbolV2 * originalSymbolForFeature(QgsFeature &feature) override
Return symbol for feature.
QgsDataDefined dataDefinedSize() const
Returns data defined size for whole symbol (including all symbol layers).
void setSizeScaleField(QString fieldOrExpression)
static QgsFeatureRendererV2 * createFromSld(QDomElement &element, QGis::GeometryType geomType)
QgsSymbolV2::ScaleMethod scaleMethod() const
Container of fields for a vector layer.
Definition: qgsfield.h:173
static QgsSingleSymbolRendererV2 * convertFromRenderer(const QgsFeatureRendererV2 *renderer)
creates a QgsSingleSymbolRendererV2 from an existing renderer.
QString expressionString() const
GeometryType
Definition: qgis.h:155
QScopedPointer< QgsSymbolV2 > mSymbol
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:119
virtual QgsFeatureRendererV2 * clone() const override
QMap< QString, QString > QgsStringMap
Definition: qgis.h:438
QgsPaintEffect * mPaintEffect
QgsSymbolV2::ScaleMethod mScaleMethod
void setWidth(double width)
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)
QString type() const
Definition: qgsrendererv2.h:82
static bool createSymbolLayerV2ListFromSld(QDomElement &element, QGis::GeometryType geomType, QgsSymbolLayerV2List &layers)
virtual QgsFeatureRendererV2 * clone() const =0
QgsInvertedPolygonRenderer is a polygon-only feature renderer used to display features inverted...
#define DEFAULT_SCALE_METHOD
static QDomElement saveSymbols(QgsSymbolV2Map &symbols, QString tagName, QDomDocument &doc)
QgsSingleSymbolRendererV2(QgsSymbolV2 *symbol)
void setAngle(double angle)
virtual QList< QString > usedAttributes() override
void setSize(double size)
QScopedPointer< QgsExpression > mSizeScale
QList< QPair< QString, QPixmap > > QgsLegendSymbologyList
QgsFeatureRendererV2 * embeddedRenderer() const
bool useExpression() const
virtual QgsLegendSymbologyList legendSymbologyItems(QSize iconSize) override
return a list of symbology items for the legend
virtual QString dump() const override
for debugging
A renderer that automatically displaces points with the same position.
void setUsingSymbolLevels(bool usingSymbolLevels)
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.
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
void copyPaintEffect(QgsFeatureRendererV2 *destRenderer) const
Copies paint effect of this renderer to another renderer.
static QgsExpression * fieldOrExpressionToExpression(const QString &fieldOrExpression)
Return a new valid expression instance for given field or expression string.
QList< QgsSymbolLayerV2 * > QgsSymbolLayerV2List
Definition: qgssymbolv2.h:44
virtual QgsLegendSymbolList legendSymbolItems(double scaleDenominator=-1, QString rule=QString()) override
return a list of item text / symbol
static QString encodeScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
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)
static void clearSymbolMap(QgsSymbolV2Map &symbols)
static QgsSymbolV2::ScaleMethod decodeScaleMethod(QString str)
QList< QgsLegendSymbolItemV2 > QgsLegendSymbolListV2
static QgsFeatureRendererV2 * create(QDomElement &element)
create renderer from XML element
static QPixmap symbolPreviewPixmap(QgsSymbolV2 *symbol, QSize size, QgsRenderContext *customContext=0)
double size
Definition: qgssvgcache.cpp:77
QList< QPair< QString, QgsSymbolV2 * > > QgsLegendSymbolList
Definition: qgsrendererv2.h:43
virtual void startRender(QgsRenderContext &context, const QgsFields &fields) override
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
QScopedPointer< QgsSymbolV2 > mTempSymbol
bool isActive() const
void setScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
void setRotationField(QString fieldOrExpression) override
sets rotation field of renderer (if supported by the renderer)
QScopedPointer< QgsExpression > mRotation
virtual QgsLegendSymbolListV2 legendSymbolItemsV2() const override
Return a list of symbology items for the legend.