QGIS API Documentation  2.2.0-Valmiera
 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 
122 {
123  if ( !vl )
124  {
125  return;
126  }
127 
128  const QgsFields& fields = vl->pendingFields();
129  QMap< QString, QgsExpression* >::iterator it = mDataDefinedProperties.begin();
130  for ( ; it != mDataDefinedProperties.end(); ++it )
131  {
132  if ( it.value() )
133  {
134  it.value()->prepare( fields );
135  if ( scale > 0 )
136  {
137  it.value()->setScale( scale );
138  }
139  }
140  }
141 }
142 
143 QSet<QString> QgsSymbolLayerV2::usedAttributes() const
144 {
145  QStringList columns;
146 
147  QMap< QString, QgsExpression* >::const_iterator ddIt = mDataDefinedProperties.constBegin();
148  for ( ; ddIt != mDataDefinedProperties.constEnd(); ++ddIt )
149  {
150  if ( ddIt.value() )
151  {
152  columns.append( ddIt.value()->referencedColumns() );
153  }
154  }
155 
156  QSet<QString> attributes;
157  QStringList::const_iterator it = columns.constBegin();
158  for ( ; it != columns.constEnd(); ++it )
159  {
160  attributes.insert( *it );
161  }
162 
163  return attributes;
164 }
165 
167 {
168  QMap< QString, QgsExpression* >::const_iterator ddIt = mDataDefinedProperties.constBegin();
169  for ( ; ddIt != mDataDefinedProperties.constEnd(); ++ddIt )
170  {
171  if ( ddIt.value() )
172  {
173  stringMap.insert( ddIt.key() + "_expression", ddIt.value()->expression() );
174  }
175  }
176 }
177 
179 {
180  if ( !destLayer )
181  return;
182 
183  destLayer->removeDataDefinedProperties();
184 
185  QMap< QString, QgsExpression* >::const_iterator ddIt = mDataDefinedProperties.constBegin();
186  for ( ; ddIt != mDataDefinedProperties.constEnd(); ++ddIt )
187  {
188  if ( ddIt.value() )
189  {
190  destLayer->setDataDefinedProperty( ddIt.key(), ddIt.value()->expression() );
191  }
192  }
193 }
194 
195 
197  : QgsSymbolLayerV2( QgsSymbolV2::Marker, locked ), mSizeUnit( QgsSymbolV2::MM ), mOffsetUnit( QgsSymbolV2::MM ),
198  mHorizontalAnchorPoint( HCenter ), mVerticalAnchorPoint( VCenter )
199 {
200  mOffsetExpression = NULL;
203 }
204 
206  : QgsSymbolLayerV2( QgsSymbolV2::Line, locked ), mWidthUnit( QgsSymbolV2::MM )
207 {
208 }
209 
211  : QgsSymbolLayerV2( QgsSymbolV2::Fill, locked ), mAngle( 0.0 )
212 {
213 }
214 
216 {
217  Q_UNUSED( context );
218  mOffsetExpression = expression( "offset" );
219  mHorizontalAnchorExpression = expression( "horizontal_anchor_point" );
220  mVerticalAnchorExpression = expression( "vertical_anchor_point" );
221 }
222 
224 {
225  startRender( context );
226  renderPoint( QPointF( size.width() / 2, size.height() / 2 ), context );
227  stopRender( context );
228 }
229 
231 {
232  mSizeUnit = unit;
233  mOffsetUnit = unit;
234 }
235 
236 void QgsMarkerSymbolLayerV2::markerOffset( const QgsSymbolV2RenderContext& context, double& offsetX, double& offsetY ) const
237 {
238  markerOffset( context, mSize, mSize, mSizeUnit, mSizeUnit, offsetX, offsetY );
239 }
240 
241 void QgsMarkerSymbolLayerV2::markerOffset( const QgsSymbolV2RenderContext& context, double width, double height,
242  QgsSymbolV2::OutputUnit widthUnit, QgsSymbolV2::OutputUnit heightUnit,
243  double& offsetX, double& offsetY ) const
244 {
245  offsetX = mOffset.x();
246  offsetY = mOffset.y();
247 
248  if ( mOffsetExpression )
249  {
250  QPointF offset = QgsSymbolLayerV2Utils::decodePoint( mOffsetExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
251  offsetX = offset.x();
252  offsetY = offset.y();
253  }
254 
257 
261  {
262  horizontalAnchorPoint = decodeHorizontalAnchorPoint( mHorizontalAnchorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
263  }
265  {
266  verticalAnchorPoint = decodeVerticalAnchorPoint( mVerticalAnchorExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toString() );
267  }
268 
269  //correct horizontal position according to anchor point
270  if ( horizontalAnchorPoint == HCenter && verticalAnchorPoint == VCenter )
271  {
272  return;
273  }
274 
275  double anchorPointCorrectionX = width * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), widthUnit ) / 2.0;
276  double anchorPointCorrectionY = height * QgsSymbolLayerV2Utils::lineWidthScaleFactor( context.renderContext(), heightUnit ) / 2.0;
277  if ( horizontalAnchorPoint == Left )
278  {
279  offsetX += anchorPointCorrectionX;
280  }
281  else if ( horizontalAnchorPoint == Right )
282  {
283  offsetX -= anchorPointCorrectionX;
284  }
285 
286  //correct vertical position according to anchor point
287  if ( verticalAnchorPoint == Top )
288  {
289  offsetY += anchorPointCorrectionY;
290  }
291  else if ( verticalAnchorPoint == Bottom )
292  {
293  offsetY -= anchorPointCorrectionY;
294  }
295 }
296 
297 QPointF QgsMarkerSymbolLayerV2::_rotatedOffset( const QPointF& offset, double angle )
298 {
299  angle = DEG2RAD( angle );
300  double c = cos( angle ), s = sin( angle );
301  return QPointF( offset.x() * c - offset.y() * s, offset.x() * s + offset.y() * c );
302 }
303 
305 {
306  if ( str.compare( "left", Qt::CaseInsensitive ) == 0 )
307  {
309  }
310  else if ( str.compare( "right", Qt::CaseInsensitive ) == 0 )
311  {
313  }
314  else
315  {
317  }
318 }
319 
321 {
322  if ( str.compare( "top", Qt::CaseInsensitive ) == 0 )
323  {
325  }
326  else if ( str.compare( "bottom", Qt::CaseInsensitive ) == 0 )
327  {
329  }
330  else
331  {
333  }
334 }
335 
337 {
339  if ( mOffsetUnit != unit )
340  {
341  return QgsSymbolV2::Mixed;
342  }
343  return unit;
344 }
345 
347 {
348  QPolygonF points;
349  // we're adding 0.5 to get rid of blurred preview:
350  // drawing antialiased lines of width 1 at (x,0)-(x,100) creates 2px line
351  points << QPointF( 0, size.height() / 2 + 0.5 ) << QPointF( size.width(), size.height() / 2 + 0.5 );
352 
353  startRender( context );
354  renderPolyline( points, context );
355  stopRender( context );
356 }
357 
358 void QgsLineSymbolLayerV2::renderPolygonOutline( const QPolygonF& points, QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
359 {
360  renderPolyline( points, context );
361  if ( rings )
362  {
363  foreach ( const QPolygonF& ring, *rings )
364  renderPolyline( ring, context );
365  }
366 }
367 
368 double QgsLineSymbolLayerV2::dxfWidth( const QgsDxfExport& e, const QgsSymbolV2RenderContext& context ) const
369 {
370  Q_UNUSED( context );
371  return ( width() * e.mapUnitScaleFactor( e.symbologyScaleDenominator(), widthUnit(), e.mapUnits() ) );
372 }
373 
374 
376 {
377  QPolygonF poly = QRectF( QPointF( 0, 0 ), QPointF( size.width(), size.height() ) );
378  startRender( context );
379  renderPolygon( poly, NULL, context );
380  stopRender( context );
381 }
382 
383 void QgsFillSymbolLayerV2::_renderPolygon( QPainter* p, const QPolygonF& points, const QList<QPolygonF>* rings, QgsSymbolV2RenderContext& context )
384 {
385  if ( !p )
386  {
387  return;
388  }
389 
390  // Disable 'Antialiasing' if the geometry was generalized in the current RenderContext (We known that it must have least #5 points).
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 
399  if ( rings == NULL )
400  {
401  // simple polygon without holes
402  p->drawPolygon( points );
403  }
404  else
405  {
406  // polygon with holes must be drawn using painter path
407  QPainterPath path;
408  QPolygonF outerRing = points;
409  path.addPolygon( outerRing );
410 
411  QList<QPolygonF>::const_iterator it = rings->constBegin();
412  for ( ; it != rings->constEnd(); ++it )
413  {
414  QPolygonF ring = *it;
415  path.addPolygon( ring );
416  }
417 
418  p->drawPath( path );
419  }
420 }
421 
422 void QgsMarkerSymbolLayerV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
423 {
424  QDomElement symbolizerElem = doc.createElement( "se:PointSymbolizer" );
425  if ( !props.value( "uom", "" ).isEmpty() )
426  symbolizerElem.setAttribute( "uom", props.value( "uom", "" ) );
427  element.appendChild( symbolizerElem );
428 
429  // <Geometry>
430  QgsSymbolLayerV2Utils::createGeometryElement( doc, symbolizerElem, props.value( "geom", "" ) );
431 
432  writeSldMarker( doc, symbolizerElem, props );
433 }