QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
qgsellipsesymbollayer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsellipsesymbollayer.cpp
3  ---------------------
4  begin : June 2011
5  copyright : (C) 2011 by Marco Hugentobler
6  email : marco dot hugentobler at sourcepole dot ch
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 #include "qgsellipsesymbollayer.h"
16 #include "qgsdxfexport.h"
17 #include "qgsexpression.h"
18 #include "qgsfeature.h"
19 #include "qgsrendercontext.h"
20 #include "qgsvectorlayer.h"
21 #include "qgslogger.h"
22 #include "qgsunittypes.h"
23 #include "qgsproperty.h"
24 #include "qgssymbollayerutils.h"
25 
26 #include <QPainter>
27 #include <QSet>
28 #include <QDomDocument>
29 #include <QDomElement>
30 
32  : mSymbolName( QStringLiteral( "circle" ) )
33  , mStrokeColor( QColor( 35, 35, 35 ) )
34 {
35  mColor = Qt::white;
36  mPen.setColor( mStrokeColor );
37  mPen.setStyle( mStrokeStyle );
38  mPen.setJoinStyle( mPenJoinStyle );
39  mPen.setWidth( 1.0 );
40  mBrush.setColor( mColor );
41  mBrush.setStyle( Qt::SolidPattern );
42  mOffset = QPointF( 0, 0 );
43  mAngle = 0;
44 }
45 
47 {
49  if ( properties.contains( QStringLiteral( "symbol_name" ) ) )
50  {
51  layer->setSymbolName( properties[ QStringLiteral( "symbol_name" )] );
52  }
53  if ( properties.contains( QStringLiteral( "size" ) ) )
54  {
55  layer->setSize( properties[QStringLiteral( "size" )].toDouble() );
56  }
57  if ( properties.contains( QStringLiteral( "size_unit" ) ) )
58  {
59  layer->setSizeUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "size_unit" )] ) );
60  }
61  if ( properties.contains( QStringLiteral( "size_map_unit_scale" ) ) )
62  {
63  layer->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "size_map_unit_scale" )] ) );
64  }
65  if ( properties.contains( QStringLiteral( "symbol_width" ) ) )
66  {
67  layer->setSymbolWidth( properties[QStringLiteral( "symbol_width" )].toDouble() );
68  }
69  if ( properties.contains( QStringLiteral( "symbol_width_unit" ) ) )
70  {
71  layer->setSymbolWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "symbol_width_unit" )] ) );
72  }
73  if ( properties.contains( QStringLiteral( "symbol_width_map_unit_scale" ) ) )
74  {
75  layer->setSymbolWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "symbol_width_map_unit_scale" )] ) );
76  }
77  if ( properties.contains( QStringLiteral( "symbol_height" ) ) )
78  {
79  layer->setSymbolHeight( properties[QStringLiteral( "symbol_height" )].toDouble() );
80  }
81  if ( properties.contains( QStringLiteral( "symbol_height_unit" ) ) )
82  {
83  layer->setSymbolHeightUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "symbol_height_unit" )] ) );
84  }
85  if ( properties.contains( QStringLiteral( "symbol_height_map_unit_scale" ) ) )
86  {
87  layer->setSymbolHeightMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "symbol_height_map_unit_scale" )] ) );
88  }
89  if ( properties.contains( QStringLiteral( "angle" ) ) )
90  {
91  layer->setAngle( properties[QStringLiteral( "angle" )].toDouble() );
92  }
93  if ( properties.contains( QStringLiteral( "outline_style" ) ) )
94  {
95  layer->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( properties[QStringLiteral( "outline_style" )] ) );
96  }
97  else if ( properties.contains( QStringLiteral( "line_style" ) ) )
98  {
99  layer->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( properties[QStringLiteral( "line_style" )] ) );
100  }
101  if ( properties.contains( QStringLiteral( "joinstyle" ) ) )
102  {
103  layer->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( properties[QStringLiteral( "joinstyle" )] ) );
104  }
105  if ( properties.contains( QStringLiteral( "outline_width" ) ) )
106  {
107  layer->setStrokeWidth( properties[QStringLiteral( "outline_width" )].toDouble() );
108  }
109  else if ( properties.contains( QStringLiteral( "line_width" ) ) )
110  {
111  layer->setStrokeWidth( properties[QStringLiteral( "line_width" )].toDouble() );
112  }
113  if ( properties.contains( QStringLiteral( "outline_width_unit" ) ) )
114  {
115  layer->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "outline_width_unit" )] ) );
116  }
117  else if ( properties.contains( QStringLiteral( "line_width_unit" ) ) )
118  {
119  layer->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "line_width_unit" )] ) );
120  }
121  if ( properties.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
122  {
123  layer->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "outline_width_map_unit_scale" )] ) );
124  }
125  if ( properties.contains( QStringLiteral( "fill_color" ) ) )
126  {
127  //pre 2.5 projects used "fill_color"
128  layer->setFillColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "fill_color" )] ) );
129  }
130  else if ( properties.contains( QStringLiteral( "color" ) ) )
131  {
132  layer->setFillColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "color" )] ) );
133  }
134  if ( properties.contains( QStringLiteral( "outline_color" ) ) )
135  {
136  layer->setStrokeColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "outline_color" )] ) );
137  }
138  else if ( properties.contains( QStringLiteral( "line_color" ) ) )
139  {
140  layer->setStrokeColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "line_color" )] ) );
141  }
142  if ( properties.contains( QStringLiteral( "offset" ) ) )
143  {
144  layer->setOffset( QgsSymbolLayerUtils::decodePoint( properties[QStringLiteral( "offset" )] ) );
145  }
146  if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
147  {
148  layer->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )] ) );
149  }
150  if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
151  {
152  layer->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )] ) );
153  }
154  if ( properties.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
155  {
156  layer->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( properties[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
157  }
158  if ( properties.contains( QStringLiteral( "vertical_anchor_point" ) ) )
159  {
160  layer->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( properties[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
161  }
162 
163  //data defined properties
164  layer->restoreOldDataDefinedProperties( properties );
165 
166  return layer;
167 }
168 
170 {
171  double scaledWidth = mSymbolWidth;
172  double scaledHeight = mSymbolHeight;
173 
175  {
176  bool ok;
177  context.setOriginalValueVariable( mStrokeWidth );
179  if ( exprVal.isValid() )
180  {
181  double width = exprVal.toDouble( &ok );
182  if ( ok )
183  {
184  width = context.renderContext().convertToPainterUnits( width, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
185  mPen.setWidthF( width );
186  }
187  }
188 
191  if ( exprVal.isValid() )
192  {
193  mPen.setStyle( QgsSymbolLayerUtils::decodePenStyle( exprVal.toString() ) );
194  }
195 
198  if ( exprVal.isValid() )
199  {
200  mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( exprVal.toString() ) );
201  }
202 
205 
208 
210  {
211  QString symbolName = mSymbolName;
212  context.setOriginalValueVariable( mSymbolName );
214  if ( exprVal.isValid() )
215  {
216  symbolName = exprVal.toString();
217  }
218  preparePath( symbolName, context, &scaledWidth, &scaledHeight, context.feature() );
219  }
220  }
221 
222  //offset and rotation
223  bool hasDataDefinedRotation = false;
224  QPointF offset;
225  double angle = 0;
226  calculateOffsetAndRotation( context, scaledWidth, scaledHeight, hasDataDefinedRotation, offset, angle );
227 
228  QPainter *p = context.renderContext().painter();
229  if ( !p )
230  {
231  return;
232  }
233 
234  QMatrix transform;
235  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
236  if ( !qgsDoubleNear( angle, 0.0 ) )
237  {
238  transform.rotate( angle );
239  }
240 
241  p->setPen( mPen );
242  p->setBrush( mBrush );
243  p->drawPath( transform.map( mPainterPath ) );
244 }
245 
246 
247 void QgsEllipseSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
248  double scaledWidth,
249  double scaledHeight,
250  bool &hasDataDefinedRotation,
251  QPointF &offset,
252  double &angle ) const
253 {
254  double offsetX = 0;
255  double offsetY = 0;
256  markerOffset( context, scaledWidth, scaledHeight, mSymbolWidthUnit, mSymbolHeightUnit, offsetX, offsetY, mSymbolWidthMapUnitScale, mSymbolHeightMapUnitScale );
257  offset = QPointF( offsetX, offsetY );
258 
259 //priority for rotation: 1. data defined symbol level, 2. symbol layer rotation (mAngle)
260  bool ok = true;
261  angle = mAngle + mLineAngle;
262  bool usingDataDefinedRotation = false;
264  {
265  context.setOriginalValueVariable( angle );
267  usingDataDefinedRotation = ok;
268  }
269 
270  hasDataDefinedRotation = context.renderHints() & QgsSymbol::DynamicRotation || usingDataDefinedRotation;
271  if ( hasDataDefinedRotation )
272  {
273  // For non-point markers, "dataDefinedRotation" means following the
274  // shape (shape-data defined). For them, "field-data defined" does
275  // not work at all. TODO: if "field-data defined" ever gets implemented
276  // we'll need a way to distinguish here between the two, possibly
277  // using another flag in renderHints()
278  const QgsFeature *f = context.feature();
279  if ( f )
280  {
281  const QgsGeometry g = f->geometry();
282  if ( !g.isNull() && g.type() == QgsWkbTypes::PointGeometry )
283  {
284  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
285  angle += m2p.mapRotation();
286  }
287  }
288  }
289 
290  if ( angle )
291  offset = _rotatedOffset( offset, angle );
292 }
293 
295 {
296  return QStringLiteral( "EllipseMarker" );
297 }
298 
300 {
301  QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
302  if ( !context.feature() || !dataDefinedProperties().hasActiveProperties() )
303  {
304  preparePath( mSymbolName, context );
305  }
306  mPen.setColor( mStrokeColor );
307  mPen.setStyle( mStrokeStyle );
308  mPen.setJoinStyle( mPenJoinStyle );
309  mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
310  mBrush.setColor( mColor );
311 }
312 
314 {
315 }
316 
318 {
320  m->setSymbolName( mSymbolName );
321  m->setSymbolWidth( mSymbolWidth );
322  m->setSymbolHeight( mSymbolHeight );
323  m->setStrokeStyle( mStrokeStyle );
324  m->setOffset( mOffset );
327  m->setStrokeStyle( mStrokeStyle );
328  m->setPenJoinStyle( mPenJoinStyle );
329  m->setStrokeWidth( mStrokeWidth );
330  m->setColor( color() );
331  m->setStrokeColor( mStrokeColor );
332  m->setSymbolWidthUnit( mSymbolWidthUnit );
333  m->setSymbolWidthMapUnitScale( mSymbolWidthMapUnitScale );
334  m->setSymbolHeightUnit( mSymbolHeightUnit );
335  m->setSymbolHeightMapUnitScale( mSymbolHeightMapUnitScale );
336  m->setStrokeWidthUnit( mStrokeWidthUnit );
337  m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
338  m->setAngle( mAngle );
341 
343  copyPaintEffect( m );
344  return m;
345 }
346 
347 void QgsEllipseSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
348 {
349  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:PointSymbolizer" ) );
350  if ( !props.value( QStringLiteral( "uom" ), QString() ).isEmpty() )
351  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ) );
352  element.appendChild( symbolizerElem );
353 
354  // <Geometry>
355  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ) );
356 
357  writeSldMarker( doc, symbolizerElem, props );
358 }
359 
360 void QgsEllipseSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
361 {
362  // <Graphic>
363  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
364  element.appendChild( graphicElem );
365 
366  double strokeWidth = QgsSymbolLayerUtils::rescaleUom( mStrokeWidth, mStrokeWidthUnit, props );
367  double symbolWidth = QgsSymbolLayerUtils::rescaleUom( mSymbolWidth, mSymbolWidthUnit, props );
368  QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, mSymbolName, mColor, mStrokeColor, mStrokeStyle, strokeWidth, symbolWidth );
369 
370  // <Rotation>
372 
373  QString angleFunc = props.value( QStringLiteral( "angle" ), QString() );
374  if ( angleFunc.isEmpty() ) // symbol has no angle set
375  {
376  if ( ddRotation && ddRotation.isActive() )
377  {
378  angleFunc = ddRotation.asExpression();
379  }
380  else if ( !qgsDoubleNear( mAngle, 0.0 ) )
381  angleFunc = QString::number( mAngle );
382  }
383  else if ( ddRotation && ddRotation.isActive() )
384  {
385  // the symbol has an angle and the symbol layer have a rotation
386  // property set
387  angleFunc = QStringLiteral( "%1 + %2" ).arg( angleFunc, ddRotation.asExpression() );
388  }
389  else if ( !qgsDoubleNear( mAngle, 0.0 ) )
390  {
391  // both the symbol and the symbol layer have angle value set
392  bool ok;
393  double angle = angleFunc.toDouble( &ok );
394  if ( !ok )
395  {
396  // its a string (probably a property name or a function)
397  angleFunc = QStringLiteral( "%1 + %2" ).arg( angleFunc ).arg( mAngle );
398  }
399  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
400  {
401  // it's a double value
402  angleFunc = QString::number( angle + mAngle );
403  }
404  }
405  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
406 
407  // <Displacement>
409  QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
410 
411  // store w/h factor in a <VendorOption>
412  double widthHeightFactor = mSymbolWidth / mSymbolHeight;
413  QDomElement factorElem = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "widthHeightFactor" ), QString::number( widthHeightFactor ) );
414  graphicElem.appendChild( factorElem );
415 }
416 
418 {
419  QgsDebugMsg( QStringLiteral( "Entered." ) );
420 
421  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
422  if ( graphicElem.isNull() )
423  return nullptr;
424 
425  QString name = QStringLiteral( "circle" );
426  QColor fillColor, strokeColor;
427  double strokeWidth, size;
428  double widthHeightFactor = 1.0;
429  Qt::PenStyle strokeStyle;
430 
431  QgsStringMap vendorOptions = QgsSymbolLayerUtils::getVendorOptionList( graphicElem );
432  for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
433  {
434  if ( it.key() == QLatin1String( "widthHeightFactor" ) )
435  {
436  bool ok;
437  double v = it.value().toDouble( &ok );
438  if ( ok && !qgsDoubleNear( v, 0.0 ) && v > 0 )
439  widthHeightFactor = v;
440  }
441  }
442 
443  if ( !QgsSymbolLayerUtils::wellKnownMarkerFromSld( graphicElem, name, fillColor, strokeColor, strokeStyle, strokeWidth, size ) )
444  return nullptr;
445 
446  QString uom = element.attribute( QStringLiteral( "uom" ) );
448  strokeWidth = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, strokeWidth );
449 
450  double angle = 0.0;
451  QString angleFunc;
452  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
453  {
454  bool ok;
455  double d = angleFunc.toDouble( &ok );
456  if ( ok )
457  angle = d;
458  }
459 
461  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
462  m->setSymbolName( name );
463  m->setFillColor( fillColor );
464  m->setStrokeColor( strokeColor );
465  m->setStrokeStyle( strokeStyle );
466  m->setStrokeWidth( strokeWidth );
467  m->setSymbolWidth( size );
468  m->setSymbolHeight( size / widthHeightFactor );
469  m->setAngle( angle );
470  return m;
471 }
472 
474 {
475  QgsStringMap map;
476  map[QStringLiteral( "symbol_name" )] = mSymbolName;
477  map[QStringLiteral( "symbol_width" )] = QString::number( mSymbolWidth );
478  map[QStringLiteral( "symbol_width_unit" )] = QgsUnitTypes::encodeUnit( mSymbolWidthUnit );
479  map[QStringLiteral( "symbol_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSymbolWidthMapUnitScale );
480  map[QStringLiteral( "symbol_height" )] = QString::number( mSymbolHeight );
481  map[QStringLiteral( "symbol_height_unit" )] = QgsUnitTypes::encodeUnit( mSymbolHeightUnit );
482  map[QStringLiteral( "symbol_height_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSymbolHeightMapUnitScale );
483  map[QStringLiteral( "angle" )] = QString::number( mAngle );
484  map[QStringLiteral( "outline_style" )] = QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle );
485  map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
486  map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
487  map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
488  map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
489  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
490  map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
491  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
492  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
493  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
494  map[QStringLiteral( "size" )] = QString::number( mSize );
495  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
496  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
497  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
498  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
499  return map;
500 }
501 
502 QSizeF QgsEllipseSymbolLayer::calculateSize( QgsSymbolRenderContext &context, double *scaledWidth, double *scaledHeight )
503 {
504  double width = 0;
505 
506  if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyWidth ) ) //1. priority: data defined setting on symbol layer le
507  {
508  context.setOriginalValueVariable( mSymbolWidth );
510  }
511  else //2. priority: global width setting
512  {
513  width = mSymbolWidth;
514  }
515  if ( scaledWidth )
516  {
517  *scaledWidth = width;
518  }
519  width = context.renderContext().convertToPainterUnits( width, mSymbolWidthUnit, mSymbolHeightMapUnitScale );
520 
521  double height = 0;
522  if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyHeight ) ) //1. priority: data defined setting on symbol layer level
523  {
524  context.setOriginalValueVariable( mSymbolHeight );
526  }
527  else //2. priority: global height setting
528  {
529  height = mSymbolHeight;
530  }
531  if ( scaledHeight )
532  {
533  *scaledHeight = height;
534  }
535  height = context.renderContext().convertToPainterUnits( height, mSymbolHeightUnit, mSymbolHeightMapUnitScale );
536  return QSizeF( width, height );
537 }
538 
539 void QgsEllipseSymbolLayer::preparePath( const QString &symbolName, QgsSymbolRenderContext &context, double *scaledWidth, double *scaledHeight, const QgsFeature * )
540 {
541  mPainterPath = QPainterPath();
542 
543  QSizeF size = calculateSize( context, scaledWidth, scaledHeight );
544 
545  if ( symbolName == QLatin1String( "circle" ) )
546  {
547  mPainterPath.addEllipse( QRectF( -size.width() / 2.0, -size.height() / 2.0, size.width(), size.height() ) );
548  }
549  else if ( symbolName == QLatin1String( "semi_circle" ) )
550  {
551  mPainterPath.arcTo( -size.width() / 2.0, -size.height() / 2.0, size.width(), size.height(), 0, 180 );
552  mPainterPath.lineTo( 0, 0 );
553  }
554  else if ( symbolName == QLatin1String( "rectangle" ) )
555  {
556  mPainterPath.addRect( QRectF( -size.width() / 2.0, -size.height() / 2.0, size.width(), size.height() ) );
557  }
558  else if ( symbolName == QLatin1String( "diamond" ) )
559  {
560  mPainterPath.moveTo( -size.width() / 2.0, 0 );
561  mPainterPath.lineTo( 0, size.height() / 2.0 );
562  mPainterPath.lineTo( size.width() / 2.0, 0 );
563  mPainterPath.lineTo( 0, -size.height() / 2.0 );
564  mPainterPath.lineTo( -size.width() / 2.0, 0 );
565  }
566  else if ( symbolName == QLatin1String( "cross" ) )
567  {
568  mPainterPath.moveTo( 0, -size.height() / 2.0 );
569  mPainterPath.lineTo( 0, size.height() / 2.0 );
570  mPainterPath.moveTo( -size.width() / 2.0, 0 );
571  mPainterPath.lineTo( size.width() / 2.0, 0 );
572  }
573  else if ( symbolName == QLatin1String( "triangle" ) )
574  {
575  mPainterPath.moveTo( 0, -size.height() / 2.0 );
576  mPainterPath.lineTo( -size.width() / 2.0, size.height() / 2.0 );
577  mPainterPath.lineTo( size.width() / 2.0, size.height() / 2.0 );
578  mPainterPath.lineTo( 0, -size.height() / 2.0 );
579  }
580  else if ( symbolName == QLatin1String( "left_half_triangle" ) )
581  {
582  mPainterPath.moveTo( 0, size.height() / 2.0 );
583  mPainterPath.lineTo( size.width() / 2.0, size.height() / 2.0 );
584  mPainterPath.lineTo( 0, -size.height() / 2.0 );
585  mPainterPath.lineTo( 0, size.height() / 2.0 );
586  }
587  else if ( symbolName == QLatin1String( "right_half_triangle" ) )
588  {
589  mPainterPath.moveTo( -size.width() / 2.0, size.height() / 2.0 );
590  mPainterPath.lineTo( 0, size.height() / 2.0 );
591  mPainterPath.lineTo( 0, -size.height() / 2.0 );
592  mPainterPath.lineTo( -size.width() / 2.0, size.height() / 2.0 );
593  }
594 }
595 
597 {
598  if ( mSymbolWidth >= mSymbolHeight )
599  {
600  mSymbolHeight = mSymbolHeight * size / mSymbolWidth;
601  mSymbolWidth = size;
602  }
603  else
604  {
605  mSymbolWidth = mSymbolWidth * size / mSymbolHeight;
606  mSymbolHeight = size;
607  }
609 }
610 
612 {
613  mSymbolWidth = w;
614  QgsMarkerSymbolLayer::setSize( mSymbolWidth >= mSymbolHeight ? mSymbolWidth : mSymbolHeight );
615 }
616 
618 {
619  mSymbolHeight = h;
620  QgsMarkerSymbolLayer::setSize( mSymbolWidth >= mSymbolHeight ? mSymbolWidth : mSymbolHeight );
621 }
622 
624 {
626  mSymbolWidthUnit = unit;
627  mSymbolHeightUnit = unit;
628  mStrokeWidthUnit = unit;
629 }
630 
632 {
634  if ( mSymbolWidthUnit != unit || mSymbolHeightUnit != unit || mStrokeWidthUnit != unit )
635  {
637  }
638  return unit;
639 }
640 
642 {
644  mSymbolWidthMapUnitScale = scale;
645  mSymbolHeightMapUnitScale = scale;
646  mStrokeWidthMapUnitScale = scale;
647 }
648 
650 {
651  if ( QgsMarkerSymbolLayer::mapUnitScale() == mSymbolWidthMapUnitScale &&
652  mSymbolWidthMapUnitScale == mSymbolHeightMapUnitScale &&
653  mSymbolHeightMapUnitScale == mStrokeWidthMapUnitScale )
654  {
655  return mSymbolWidthMapUnitScale;
656  }
657  return QgsMapUnitScale();
658 }
659 
660 QRectF QgsEllipseSymbolLayer::bounds( QPointF point, QgsSymbolRenderContext &context )
661 {
662  QSizeF size = calculateSize( context );
663 
664  bool hasDataDefinedRotation = false;
665  QPointF offset;
666  double angle = 0;
667  calculateOffsetAndRotation( context, size.width(), size.height(), hasDataDefinedRotation, offset, angle );
668 
669  QMatrix transform;
670 
671  // move to the desired position
672  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
673 
674  if ( !qgsDoubleNear( angle, 0.0 ) )
675  transform.rotate( angle );
676 
677  double penWidth = 0.0;
679  {
680  context.setOriginalValueVariable( mStrokeWidth );
682 
683  if ( exprVal.isValid() )
684  {
685  bool ok;
686  double strokeWidth = exprVal.toDouble( &ok );
687  if ( ok )
688  {
689  penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
690  }
691  }
692  }
694  {
697  if ( exprVal.isValid() && exprVal.toString() == QLatin1String( "no" ) )
698  {
699  penWidth = 0.0;
700  }
701  }
702 
703  //antialiasing, add 1 pixel
704  penWidth += 1;
705 
706  QRectF symbolBounds = transform.mapRect( QRectF( -size.width() / 2.0,
707  -size.height() / 2.0,
708  size.width(),
709  size.height() ) );
710 
711  //extend bounds by pen width / 2.0
712  symbolBounds.adjust( -penWidth / 2.0, -penWidth / 2.0,
713  penWidth / 2.0, penWidth / 2.0 );
714 
715  return symbolBounds;
716 }
717 
718 bool QgsEllipseSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
719 {
720  //width
721  double symbolWidth = mSymbolWidth;
722 
723  if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyWidth ) ) //1. priority: data defined setting on symbol layer le
724  {
725  context.setOriginalValueVariable( mSymbolWidth );
727  }
728  if ( mSymbolWidthUnit == QgsUnitTypes::RenderMillimeters )
729  {
730  symbolWidth *= mmMapUnitScaleFactor;
731  }
732 
733  //height
734  double symbolHeight = mSymbolHeight;
735  if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyHeight ) ) //1. priority: data defined setting on symbol layer level
736  {
737  context.setOriginalValueVariable( mSymbolHeight );
739  }
740  if ( mSymbolHeightUnit == QgsUnitTypes::RenderMillimeters )
741  {
742  symbolHeight *= mmMapUnitScaleFactor;
743  }
744 
745  //stroke width
746  double strokeWidth = mStrokeWidth;
747 
749  {
750  context.setOriginalValueVariable( mStrokeWidth );
752  }
753  if ( mStrokeWidthUnit == QgsUnitTypes::RenderMillimeters )
754  {
755  strokeWidth *= strokeWidth;
756  }
757 
758  //fill color
759  QColor fc = mColor;
761  {
764  }
765 
766  //stroke color
767  QColor oc = mStrokeColor;
769  {
772  }
773 
774  //symbol name
775  QString symbolName = mSymbolName;
777  {
778  context.setOriginalValueVariable( mSymbolName );
780  }
781 
782  //offset
783  double offsetX = 0;
784  double offsetY = 0;
785  markerOffset( context, offsetX, offsetY );
786  QPointF off( offsetX, offsetY );
787 
788  //priority for rotation: 1. data defined symbol level, 2. symbol layer rotation (mAngle)
789  double rotation = 0.0;
791  {
792  context.setOriginalValueVariable( mAngle );
794  }
795  else if ( !qgsDoubleNear( mAngle + mLineAngle, 0.0 ) )
796  {
797  rotation = mAngle + mLineAngle;
798  }
799  rotation = -rotation; //rotation in Qt is counterclockwise
800  if ( rotation )
801  off = _rotatedOffset( off, rotation );
802 
803  QTransform t;
804  t.translate( shift.x() + offsetX, shift.y() + offsetY );
805 
806  if ( !qgsDoubleNear( rotation, 0.0 ) )
807  t.rotate( rotation );
808 
809  double halfWidth = symbolWidth / 2.0;
810  double halfHeight = symbolHeight / 2.0;
811 
812  if ( symbolName == QLatin1String( "circle" ) )
813  {
814  if ( qgsDoubleNear( halfWidth, halfHeight ) )
815  {
816  QgsPoint pt( t.map( QPointF( 0, 0 ) ) );
817  e.writeFilledCircle( layerName, oc, pt, halfWidth );
818  }
819  else
820  {
821  QgsPointSequence line;
822 
823  double stepsize = 2 * M_PI / 40;
824  for ( int i = 0; i < 39; ++i )
825  {
826  double angle = stepsize * i;
827  double x = halfWidth * std::cos( angle );
828  double y = halfHeight * std::sin( angle );
829  line << QgsPoint( t.map( QPointF( x, y ) ) );
830  }
831  //close ellipse with first point
832  line << line.at( 0 );
833 
834  if ( mBrush.style() != Qt::NoBrush )
835  e.writePolygon( QgsRingSequence() << line, layerName, QStringLiteral( "SOLID" ), fc );
836  if ( mPen.style() != Qt::NoPen )
837  e.writePolyline( line, layerName, QStringLiteral( "CONTINUOUS" ), oc, strokeWidth );
838  }
839  }
840  else if ( symbolName == QLatin1String( "rectangle" ) )
841  {
843  p << QgsPoint( t.map( QPointF( -halfWidth, -halfHeight ) ) )
844  << QgsPoint( t.map( QPointF( halfWidth, -halfHeight ) ) )
845  << QgsPoint( t.map( QPointF( halfWidth, halfHeight ) ) )
846  << QgsPoint( t.map( QPointF( -halfWidth, halfHeight ) ) );
847  p << p[0];
848 
849  if ( mBrush.style() != Qt::NoBrush )
850  e.writePolygon( QgsRingSequence() << p, layerName, QStringLiteral( "SOLID" ), fc );
851  if ( mPen.style() != Qt::NoPen )
852  e.writePolyline( p, layerName, QStringLiteral( "CONTINUOUS" ), oc, strokeWidth );
853  return true;
854  }
855  else if ( symbolName == QLatin1String( "cross" ) && mPen.style() != Qt::NoPen )
856  {
858  << QgsPoint( t.map( QPointF( -halfWidth, 0 ) ) )
859  << QgsPoint( t.map( QPointF( halfWidth, 0 ) ) ),
860  layerName, QStringLiteral( "CONTINUOUS" ), oc, strokeWidth );
862  << QgsPoint( t.map( QPointF( 0, halfHeight ) ) )
863  << QgsPoint( t.map( QPointF( 0, -halfHeight ) ) ),
864  layerName, QStringLiteral( "CONTINUOUS" ), oc, strokeWidth );
865  return true;
866  }
867  else if ( symbolName == QLatin1String( "triangle" ) )
868  {
870  p << QgsPoint( t.map( QPointF( -halfWidth, -halfHeight ) ) )
871  << QgsPoint( t.map( QPointF( halfWidth, -halfHeight ) ) )
872  << QgsPoint( t.map( QPointF( 0, halfHeight ) ) );
873  p << p[0];
874  if ( mBrush.style() != Qt::NoBrush )
875  e.writePolygon( QgsRingSequence() << p, layerName, QStringLiteral( "SOLID" ), fc );
876  if ( mPen.style() != Qt::NoPen )
877  e.writePolyline( p, layerName, QStringLiteral( "CONTINUOUS" ), oc, strokeWidth );
878  return true;
879  }
880 
881  return false; //soon...
882 }
883 
884 
void setOffset(QPointF offset)
Sets the marker&#39;s offset, which is the horizontal and vertical displacement which the rendered marker...
static void wellKnownMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &name, const QColor &color, const QColor &strokeColor, Qt::PenStyle strokeStyle, double strokeWidth=-1, double size=-1)
QgsMapUnitScale mapUnitScale() const override
QColor valueAsColor(int key, const QgsExpressionContext &context, const QColor &defaultColor=QColor(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a color...
void setOffsetUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol&#39;s offset.
QString asExpression() const
Returns an expression string representing the state of the property, or an empty string if the proper...
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
static QDomElement createVendorOptionElement(QDomDocument &doc, const QString &name, const QString &value)
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double...
void writePolyline(const QgsPointSequence &line, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Draw dxf primitives (LWPOLYLINE)
void setFillColor(const QColor &c) override
Set fill color.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
void startRender(QgsSymbolRenderContext &context) override
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:280
double mLineAngle
Line rotation angle (see setLineAngle() for details)
QgsUnitTypes::RenderUnit mSizeUnit
Marker size unit.
static QPointF decodePoint(const QString &string)
Decodes a QSizeF from a string.
void copyPaintEffect(QgsSymbolLayer *destLayer) const
Copies paint effect of this layer to another symbol layer.
QString layerType() const override
Returns a string that represents this layer type.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
static void createDisplacementElement(QDomDocument &doc, QDomElement &element, QPointF offset)
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:122
Mixed or unknown units.
Definition: qgsunittypes.h:153
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol&#39;s offset.
void restoreOldDataDefinedProperties(const QgsStringMap &stringMap)
Restores older data defined properties from string map.
static double sizeInPixelsFromSldUom(const QString &uom, double size)
Returns the size scaled in pixels according to the uom attribute.
void setPenJoinStyle(Qt::PenJoinStyle style)
Set stroke join style.
QVariant value(const QgsExpressionContext &context, const QVariant &defaultValue=QVariant(), bool *ok=nullptr) const
Calculates the current value of the property, including any transforms which are set for the property...
QPointF mOffset
Marker offset.
void setSymbolWidthUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol&#39;s width.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
static bool rotationFromSldElement(QDomElement &element, QString &rotationFunc)
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
QColor strokeColor() const override
Gets stroke color.
QMap< QString, QString > QgsStringMap
Definition: qgis.h:612
Rotation of symbol may be changed during rendering and symbol should not be cached.
Definition: qgssymbol.h:104
static QString encodePoint(QPointF point)
Encodes a QPointF to a string.
Name, eg shape name for simple markers.
static Qt::PenJoinStyle decodePenJoinStyle(const QString &str)
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
void writePolygon(const QgsRingSequence &polygon, const QString &layer, const QString &hatchPattern, const QColor &color)
Draw dxf filled polygon (HATCH)
double mapRotation() const
Returns current map rotation in degrees.
static QString encodeColor(const QColor &color)
virtual void setColor(const QColor &color)
The fill color.
void setSize(double size) override
Sets the symbol size.
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const override
Returns the calculated value of the property with the specified key from within the collection...
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for data defined symbology.
Definition: qgssymbol.cpp:1192
static QString encodePenStyle(Qt::PenStyle style)
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:37
static double rescaleUom(double size, QgsUnitTypes::RenderUnit unit, const QgsStringMap &props)
Rescales the given size based on the uomScale found in the props, if any is found, otherwise returns the value un-modified.
Qt::PenStyle strokeStyle() const
double angle() const
Returns the rotation angle for the marker, in degrees clockwise from north.
QgsUnitTypes::RenderUnit mOffsetUnit
Offset units.
A symbol layer for rendering objects with major and minor axis (e.g.
static QgsStringMap getVendorOptionList(QDomElement &element)
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
double size() const
Returns the symbol size.
void writeSldMarker(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
Writes the symbol layer definition as a SLD XML element.
void stopRender(QgsSymbolRenderContext &context) override
void setSymbolName(const QString &name)
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the symbol layer&#39;s property collection, used for data defined overrides...
void setHorizontalAnchorPoint(HorizontalAnchorPoint h)
Sets the horizontal anchor point for positioning the symbol.
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol&#39;s stroke width.
void setSizeMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol&#39;s size.
virtual QColor color() const
The fill color.
static void createRotationElement(QDomDocument &doc, QDomElement &element, const QString &rotationFunc)
double mAngle
Marker rotation angle, in degrees clockwise from north.
void writeFilledCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius)
Write filled circle (as hatch)
bool writeDxf(QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift=QPointF(0.0, 0.0)) const override
write as DXF
static QgsSymbolLayer * createFromSld(QDomElement &element)
void setMapUnitScale(const QgsMapUnitScale &scale) override
void markerOffset(QgsSymbolRenderContext &context, double &offsetX, double &offsetY) const
Calculates the required marker offset, including both the symbol offset and any displacement required...
void setSymbolHeightMapUnitScale(const QgsMapUnitScale &scale)
QgsMapUnitScale mSizeMapUnitScale
Marker size map unit scale.
static Q_INVOKABLE QgsUnitTypes::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
A store for object properties.
Definition: qgsproperty.h:229
QgsRenderContext & renderContext()
Returns a reference to the context&#39;s render context.
Definition: qgssymbol.h:668
QgsMapUnitScale mapUnitScale() const override
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
static Qt::PenStyle decodePenStyle(const QString &str)
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
HorizontalAnchorPoint
Symbol horizontal anchor points.
VerticalAnchorPoint
Symbol vertical anchor points.
static void createGeometryElement(QDomDocument &doc, QDomElement &element, const QString &geomFunc)
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
void setSymbolHeightUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol&#39;s height.
QgsExpressionContext & expressionContext()
Gets the expression context.
QVector< QgsPoint > QgsPointSequence
QVector< QgsPointSequence > QgsRingSequence
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol&#39;s size.
QString valueAsString(int key, const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a string...
Stroke style (eg solid, dashed)
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
QPainter * painter()
Returns the destination QPainter for the render operation.
const QgsMapToPixel & mapToPixel() const
Returns the context&#39;s map to pixel transform, which transforms between map coordinates and device coo...
void setStrokeStyle(Qt::PenStyle strokeStyle)
void setStrokeColor(const QColor &c) override
Set stroke color.
Struct for storing maximum and minimum scales for measurements in map units.
void setMapUnitScale(const QgsMapUnitScale &scale) override
const QgsFeature * feature() const
Returns the current feature being rendered.
Definition: qgssymbol.h:749
virtual void setSize(double size)
Sets the symbol size.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
HorizontalAnchorPoint mHorizontalAnchorPoint
Horizontal anchor point.
static bool wellKnownMarkerFromSld(QDomElement &element, QString &name, QColor &color, QColor &strokeColor, Qt::PenStyle &strokeStyle, double &strokeWidth, double &size)
double mSize
Marker size.
void setAngle(double angle)
Sets the rotation angle for the marker.
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
QColor fillColor() const override
Gets fill color.
bool hasActiveProperties() const override
Returns true if the collection has any active properties, or false if all properties within the colle...
QgsMapUnitScale mOffsetMapUnitScale
Offset map unit scale.
QgsWkbTypes::GeometryType type
Definition: qgsgeometry.h:126
QgsGeometry geometry
Definition: qgsfeature.h:67
VerticalAnchorPoint mVerticalAnchorPoint
Vertical anchor point.
QgsEllipseSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
static QPointF _rotatedOffset(QPointF offset, double angle)
Adjusts a marker offset to account for rotation.
QPointF offset() const
Returns the marker&#39;s offset, which is the horizontal and vertical displacement which the rendered mar...
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void setSymbolWidthMapUnitScale(const QgsMapUnitScale &scale)
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
QgsPropertyCollection mDataDefinedProperties
bool isActive() const
Returns whether the property is currently active.
QgsProperty property(int key) const override
Returns a matching property from the collection, if one exists.
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:145
static QColor decodeColor(const QString &str)
void copyDataDefinedProperties(QgsSymbolLayer *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
void setVerticalAnchorPoint(VerticalAnchorPoint v)
Sets the vertical anchor point for positioning the symbol.
QgsSymbol::RenderHints renderHints() const
Returns the rendering hint flags for the symbol.
Definition: qgssymbol.h:736
void startRender(QgsSymbolRenderContext &context) override