QGIS API Documentation  2.3.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgssymbollayerv2.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgssymbollayerv2.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 
16 #include "qgssymbollayerv2.h"
17 #include "qgsclipper.h"
18 #include "qgsexpression.h"
19 #include "qgsrendercontext.h"
20 #include "qgsvectorlayer.h"
21 #include "qgsdxfexport.h"
22 #include "qgsgeometrysimplifier.h"
23 
24 #include <QSize>
25 #include <QPainter>
26 #include <QPointF>
27 #include <QPolygonF>
28 
29 const QgsExpression* QgsSymbolLayerV2::dataDefinedProperty( const QString& property ) const
30 {
31  QMap< QString, QgsExpression* >::const_iterator it = mDataDefinedProperties.find( property );
32  if ( it != mDataDefinedProperties.constEnd() )
33  {
34  return it.value();
35  }
36  return 0;
37 }
38 
39 QgsExpression* QgsSymbolLayerV2::expression( const QString& property ) const
40 {
41  QMap< QString, QgsExpression* >::const_iterator it = mDataDefinedProperties.find( property );
42  if ( it != mDataDefinedProperties.constEnd() )
43  {
44  return it.value();
45  }
46  return 0;
47 }
48 
49 QString QgsSymbolLayerV2::dataDefinedPropertyString( const QString& property ) const
50 {
51  const QgsExpression* ex = dataDefinedProperty( property );
52  return ex ? ex->expression() : QString();
53 }
54 
55 void QgsSymbolLayerV2::setDataDefinedProperty( const QString& property, const QString& expressionString )
56 {
57  removeDataDefinedProperty( property );
58  mDataDefinedProperties.insert( property, new QgsExpression( expressionString ) );
59 }
60 
61 void QgsSymbolLayerV2::removeDataDefinedProperty( const QString& property )
62 {
63  QMap< QString, QgsExpression* >::iterator it = mDataDefinedProperties.find( property );
64  if ( it != mDataDefinedProperties.end() )
65  {
66  delete( it.value() );
67  mDataDefinedProperties.erase( it );
68  }
69 }
70 
72 {
73  QMap< QString, QgsExpression* >::iterator it = mDataDefinedProperties.begin();
74  for ( ; it != mDataDefinedProperties.constEnd(); ++it )
75  {
76  delete( it.value() );
77  }
78  mDataDefinedProperties.clear();
79 }
80 
81 bool QgsSymbolLayerV2::writeDxf( QgsDxfExport& e,
82  double mmMapUnitScaleFactor,
83  const QString& layerName,
84  const QgsSymbolV2RenderContext* context,
85  const QgsFeature* f,
86  const QPointF& shift ) const
87 {
88  Q_UNUSED( e );
89  Q_UNUSED( mmMapUnitScaleFactor );
90  Q_UNUSED( layerName );
91  Q_UNUSED( context );
92  Q_UNUSED( f );
93  Q_UNUSED( shift );
94  return false;
95 }
96 
97 double QgsSymbolLayerV2::dxfWidth( const QgsDxfExport& e, const QgsSymbolV2RenderContext& context ) const
98 {
99  Q_UNUSED( e );
100  Q_UNUSED( context );
101  return 1.0;
102 }
103 
105 {
106  Q_UNUSED( context );
107  return color();
108 }
109 
111 {
112  Q_UNUSED( unit );
113  return QVector<qreal>();
114 }
115 
116 Qt::PenStyle QgsSymbolLayerV2::dxfPenStyle() const
117 {
118  return Qt::SolidLine;
119 }
120 
121 void QgsSymbolLayerV2::prepareExpressions( const QgsFields* fields, double scale )
122 {
123  if ( !fields )
124  {
125  return;
126  }
127 
128  QMap< QString, QgsExpression* >::iterator it = mDataDefinedProperties.begin();
129  for ( ; it != mDataDefinedProperties.end(); ++it )
130  {
131  if ( it.value() )
132  {
133  it.value()->prepare( *fields );
134  if ( scale > 0 )
135  {
136  it.value()->setScale( scale );
137  }
138  }
139  }
140 }
141 
142 QSet<QString> QgsSymbolLayerV2::usedAttributes() const
143 {
144  QStringList columns;
145 
146  QMap< QString, QgsExpression* >::const_iterator ddIt = mDataDefinedProperties.constBegin();
147  for ( ; ddIt != mDataDefinedProperties.constEnd(); ++ddIt )
148  {
149  if ( ddIt.value() )
150  {
151  columns.append( ddIt.value()->referencedColumns() );
152  }
153  }
154 
155  QSet<QString> attributes;
156  QStringList::const_iterator it = columns.constBegin();
157  for ( ; it != columns.constEnd(); ++it )
158  {
159  attributes.insert( *it );
160  }
161 
162  return attributes;
163 }
164 
166 {
167  QMap< QString, QgsExpression* >::const_iterator ddIt = mDataDefinedProperties.constBegin();
168  for ( ; ddIt != mDataDefinedProperties.constEnd(); ++ddIt )
169  {
170  if ( ddIt.value() )
171  {
172  stringMap.insert( ddIt.key() + "_expression", ddIt.value()->expression() );
173  }
174  }
175 }
176 
178 {
179  if ( !destLayer )
180  return;
181 
182  destLayer->removeDataDefinedProperties();
183 
184  QMap< QString, QgsExpression* >::const_iterator ddIt = mDataDefinedProperties.constBegin();
185  for ( ; ddIt != mDataDefinedProperties.constEnd(); ++ddIt )
186  {
187  if ( ddIt.value() )
188  {
189  destLayer->setDataDefinedProperty( ddIt.key(), ddIt.value()->expression() );
190  }
191  }
192 }
193 
194 
196  : QgsSymbolLayerV2( QgsSymbolV2::Marker, locked ), mSizeUnit( QgsSymbolV2::MM ), mOffsetUnit( QgsSymbolV2::MM ),
197  mHorizontalAnchorPoint( HCenter ), mVerticalAnchorPoint( VCenter )
198 {
199  mOffsetExpression = NULL;
202 }
203 
205  : QgsSymbolLayerV2( QgsSymbolV2::Line, locked ), mWidthUnit( QgsSymbolV2::MM )
206 {
207 }
208 
210  : QgsSymbolLayerV2( QgsSymbolV2::Fill, locked ), mAngle( 0.0 )
211 {
212 }
213 
215 {
216  Q_UNUSED( context );
217  mOffsetExpression = expression( "offset" );
218  mHorizontalAnchorExpression = expression( "horizontal_anchor_point" );
219  mVerticalAnchorExpression = expression( "vertical_anchor_point" );
220 }
221 
223 {
224  startRender( context );
225  renderPoint( QPointF( size.width() / 2, size.height() / 2 ), context );
226  stopRender( context );
227 }
228 
230 {
231  mSizeUnit = unit;
232  mOffsetUnit = unit;
233 }
234 
235 void QgsMarkerSymbolLayerV2::markerOffset( const QgsSymbolV2RenderContext& context, double& offsetX, double& offsetY ) const
236 {
237  markerOffset( context, mSize, mSize, mSizeUnit, mSizeUnit, offsetX, offsetY );
238 }
239 
240 void QgsMarkerSymbolLayerV2::markerOffset( const QgsSymbolV2RenderContext& context, double width, double height,
241  QgsSymbolV2::OutputUnit widthUnit, QgsSymbolV2::OutputUnit heightUnit,
242  double& offsetX, double& offsetY ) const
243 {
244  offsetX = mOffset.x();
245  offsetY = mOffset.y();
246 
247  if ( mOffsetExpression )
248  {
249  QPointF offset = QgsSymbolLayerV2Utils::decodePoint( mOffsetExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
250  offsetX = offset.x();
251  offsetY = offset.y();
252  }
253 
256 
260  {
261  horizontalAnchorPoint = decodeHorizontalAnchorPoint( mHorizontalAnchorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
262  }
264  {
265  verticalAnchorPoint = decodeVerticalAnchorPoint( mVerticalAnchorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
266  }
267 
268  //correct horizontal position according to anchor point
269  if ( horizontalAnchorPoint == HCenter && verticalAnchorPoint == VCenter )
270  {
271  return;
272  }
273 
274  double anchorPointCorrectionX = width * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), widthUnit ) / 2.0;
275  double anchorPointCorrectionY = height * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), heightUnit ) / 2.0;
276  if ( horizontalAnchorPoint == Left )
277  {
278  offsetX += anchorPointCorrectionX;
279  }
280  else if ( horizontalAnchorPoint == Right )
281  {
282  offsetX -= anchorPointCorrectionX;
283  }
284 
285  //correct vertical position according to anchor point
286  if ( verticalAnchorPoint == Top )
287  {
288  offsetY += anchorPointCorrectionY;
289  }
290  else if ( verticalAnchorPoint == Bottom )
291  {
292  offsetY -= anchorPointCorrectionY;
293  }
294 }
295 
296 QPointF QgsMarkerSymbolLayerV2::_rotatedOffset( const QPointF& offset, double angle )
297 {
298  angle = DEG2RAD( angle );
299  double c = cos( angle ), s = sin( angle );
300  return QPointF( offset.x() * c - offset.y() * s, offset.x() * s + offset.y() * c );
301 }
302 
304 {
305  if ( str.compare( "left", Qt::CaseInsensitive ) == 0 )
306  {
308  }
309  else if ( str.compare( "right", Qt::CaseInsensitive ) == 0 )
310  {
312  }
313  else
314  {
316  }
317 }
318 
320 {
321  if ( str.compare( "top", Qt::CaseInsensitive ) == 0 )
322  {
324  }
325  else if ( str.compare( "bottom", Qt::CaseInsensitive ) == 0 )
326  {
328  }
329  else
330  {
332  }
333 }
334 
336 {
338  if ( mOffsetUnit != unit )
339  {
340  return QgsSymbolV2::Mixed;
341  }
342  return unit;
343 }
344 
346 {
347  QPolygonF points;
348  // we're adding 0.5 to get rid of blurred preview:
349  // drawing antialiased lines of width 1 at (x,0)-(x,100) creates 2px line
350  points << QPointF( 0, size.height() / 2 + 0.5 ) << QPointF( size.width(), size.height() / 2 + 0.5 );
351 
352  startRender( context );
353  renderPolyline( points, context );
354  stopRender( context );
355 }
356 
357 void QgsLineSymbolLayerV2::renderPolygonOutline( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
358 {
359  renderPolyline( points, context );
360  if ( rings )
361  {
362  foreach ( const QPolygonF& ring, *rings )
363  renderPolyline( ring, context );
364  }
365 }
366 
367 double QgsLineSymbolLayerV2::dxfWidth( const QgsDxfExport& e, const QgsSymbolV2RenderContext& context ) const
368 {
369  Q_UNUSED( context );
370  return ( width() * e.mapUnitScaleFactor( e.symbologyScaleDenominator(), widthUnit(), e.mapUnits() ) );
371 }
372 
373 
375 {
376  QPolygonF poly = QRectF( QPointF( 0, 0 ), QPointF( size.width(), size.height() ) );
377  startRender( context );
378  renderPolygon( poly, NULL, context );
379  stopRender( context );
380 }
381 
382 void QgsFillSymbolLayerV2::_renderPolygon( QPainter* p, const QPolygonF& points, const QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
383 {
384  if ( !p )
385  {
386  return;
387  }
388 
389  // Disable 'Antialiasing' if the geometry was generalized in the current RenderContext (We known that it must have least #5 points).
390 #if 0 // TODO[MD]: after merge
391  if ( points.size() <= 5 && context.layer() && context.layer()->simplifyDrawingCanbeApplied( context.renderContext(), QgsVectorSimplifyMethod::AntialiasingSimplification ) && QgsAbstractGeometrySimplifier::canbeGeneralizedByDeviceBoundingBox( points, context.layer()->simplifyMethod().threshold() ) && ( p->renderHints() & QPainter::Antialiasing ) )
392  {
393  p->setRenderHint( QPainter::Antialiasing, false );
394  p->drawRect( points.boundingRect() );
395  p->setRenderHint( QPainter::Antialiasing, true );
396  return;
397  }
398 #endif
399 
400  if ( rings == NULL )
401  {
402  // simple polygon without holes
403  p->drawPolygon( points );
404  }
405  else
406  {
407  // polygon with holes must be drawn using painter path
408  QPainterPath path;
409  QPolygonF outerRing = points;
410  path.addPolygon( outerRing );
411 
412  QList<QPolygonF>::const_iterator it = rings->constBegin();
413  for ( ; it != rings->constEnd(); ++it )
414  {
415  QPolygonF ring = *it;
416  path.addPolygon( ring );
417  }
418 
419  p->drawPath( path );
420  }
421 }
422 
423 void QgsMarkerSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
424 {
425  QDomElement symbolizerElem = doc.createElement( "se:PointSymbolizer" );
426  if ( !props.value( "uom", "" ).isEmpty() )
427  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
428  element.appendChild( symbolizerElem );
429 
430  // <Geometry>
431  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
432 
433  writeSldMarker( doc, symbolizerElem, props );
434 }
virtual QSet< QString > usedAttributes() const
void drawPreviewIcon(QgsSymbolV2RenderContext &context, QSize size)
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:89
VerticalAnchorPoint verticalAnchorPoint() const
virtual void removeDataDefinedProperties()
QMap< QString, QgsExpression * > mDataDefinedProperties
virtual Qt::PenStyle dxfPenStyle() const
virtual QgsSymbolV2::OutputUnit outputUnit() const
virtual double dxfWidth(const QgsDxfExport &e, const QgsSymbolV2RenderContext &context) const
virtual double width() const
HorizontalAnchorPoint horizontalAnchorPoint() const
const QString expression() const
Return the expression string that was given when created.
QVariant evaluate(const QgsFeature *f=NULL)
Evaluate the feature and return the result.
void startRender(QgsSymbolV2RenderContext &context)
QgsExpression * mVerticalAnchorExpression
#define DEG2RAD(x)
virtual void toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props) const
static QPointF decodePoint(QString str)
Container of fields for a vector layer.
Definition: qgsfield.h:164
virtual void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolV2RenderContext &context)=0
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:114
QgsExpression * mOffsetExpression
virtual void setOutputUnit(QgsSymbolV2::OutputUnit unit)
virtual void removeDataDefinedProperty(const QString &property)
QMap< QString, QString > QgsStringMap
Definition: qgis.h:416
static QgsMarkerSymbolLayerV2::HorizontalAnchorPoint decodeHorizontalAnchorPoint(const QString &str)
virtual QString dataDefinedPropertyString(const QString &property) const
virtual void startRender(QgsSymbolV2RenderContext &context)=0
virtual QgsExpression * expression(const QString &property) const
virtual void writeSldMarker(QDomDocument &doc, QDomElement &element, QgsStringMap props) const
const QgsFeature * feature() const
Current feature being rendered - may be null.
Definition: qgssymbolv2.h:186
virtual void renderPoint(const QPointF &point, QgsSymbolV2RenderContext &context)=0
virtual QVector< qreal > dxfCustomDashPattern(QgsSymbolV2::OutputUnit &unit) const
The geometries can be rendered with 'AntiAliasing' disabled because of it is '1-pixel size'...
QgsMarkerSymbolLayerV2(bool locked=false)
virtual QColor color() const
HorizontalAnchorPoint mHorizontalAnchorPoint
virtual void renderPolygonOutline(const QPolygonF &points, QList< QPolygonF > *rings, QgsSymbolV2RenderContext &context)
virtual void prepareExpressions(const QgsFields *fields, double scale=-1.0)
virtual QColor dxfColor(const QgsSymbolV2RenderContext &context) const
virtual bool writeDxf(QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, const QgsSymbolV2RenderContext *context, const QgsFeature *f, const QPointF &shift=QPointF(0.0, 0.0)) const
double ANALYSIS_EXPORT angle(Point3D *p1, Point3D *p2, Point3D *p3, Point3D *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
void drawPreviewIcon(QgsSymbolV2RenderContext &context, QSize size)
virtual void renderPolyline(const QPolygonF &points, QgsSymbolV2RenderContext &context)=0
void drawPreviewIcon(QgsSymbolV2RenderContext &context, QSize size)
QgsFillSymbolLayerV2(bool locked=false)
static void createGeometryElement(QDomDocument &doc, QDomElement &element, QString geomFunc)
QgsLineSymbolLayerV2(bool locked=false)
QgsExpression * mHorizontalAnchorExpression
virtual const QgsExpression * dataDefinedProperty(const QString &property) const
static QgsMarkerSymbolLayerV2::VerticalAnchorPoint decodeVerticalAnchorPoint(const QString &str)
QgsRenderContext & renderContext()
Definition: qgssymbolv2.h:164
static double lineWidthScaleFactor(const QgsRenderContext &c, QgsSymbolV2::OutputUnit u)
Returns the line width scale factor depending on the unit and the paint device.
QgsSymbolV2::OutputUnit mOffsetUnit
VerticalAnchorPoint mVerticalAnchorPoint
QgsSymbolV2::OutputUnit mSizeUnit
void markerOffset(const QgsSymbolV2RenderContext &context, double &offsetX, double &offsetY) const
void _renderPolygon(QPainter *p, const QPolygonF &points, const QList< QPolygonF > *rings, QgsSymbolV2RenderContext &context)
Default method to render polygon.
virtual double dxfWidth(const QgsDxfExport &e, const QgsSymbolV2RenderContext &context) const
double size
Definition: qgssvgcache.cpp:77
void saveDataDefinedProperties(QgsStringMap &stringMap) const
Saves data defined properties to string map.
virtual void stopRender(QgsSymbolV2RenderContext &context)=0
static bool canbeGeneralizedByDeviceBoundingBox(const QgsRectangle &envelope, float mapToPixelTol=1.0f)
Returns whether the device-envelope can be replaced by its BBOX when is applied the specified toleran...
QgsSymbolV2::OutputUnit widthUnit() const
void copyDataDefinedProperties(QgsSymbolLayerV2 *destLayer) const
Copies data defined properties of this layer to another symbol layer.
virtual void setDataDefinedProperty(const QString &property, const QString &expressionString)
static QPointF _rotatedOffset(const QPointF &offset, double angle)