QGIS API Documentation  3.27.0-Master (11ef3e5184)
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  : mStrokeColor( QColor( 35, 35, 35 ) )
33 {
34  mColor = Qt::white;
35  mPen.setColor( mStrokeColor );
36  mPen.setStyle( mStrokeStyle );
37  mPen.setJoinStyle( mPenJoinStyle );
38  mPen.setWidth( 1.0 );
39  mBrush.setColor( mColor );
40  mBrush.setStyle( Qt::SolidPattern );
41  mOffset = QPointF( 0, 0 );
42  mAngle = 0;
43 }
44 
46 
47 QgsSymbolLayer *QgsEllipseSymbolLayer::create( const QVariantMap &properties )
48 {
50  if ( properties.contains( QStringLiteral( "symbol_name" ) ) )
51  {
52  layer->setShape( decodeShape( properties[ QStringLiteral( "symbol_name" )].toString() ) );
53  }
54  if ( properties.contains( QStringLiteral( "size" ) ) )
55  {
56  layer->setSize( properties[QStringLiteral( "size" )].toDouble() );
57  }
58  if ( properties.contains( QStringLiteral( "size_unit" ) ) )
59  {
60  layer->setSizeUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "size_unit" )].toString() ) );
61  }
62  if ( properties.contains( QStringLiteral( "size_map_unit_scale" ) ) )
63  {
64  layer->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "size_map_unit_scale" )].toString() ) );
65  }
66  if ( properties.contains( QStringLiteral( "symbol_width" ) ) )
67  {
68  layer->setSymbolWidth( properties[QStringLiteral( "symbol_width" )].toDouble() );
69  }
70  if ( properties.contains( QStringLiteral( "symbol_width_unit" ) ) )
71  {
72  layer->setSymbolWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "symbol_width_unit" )].toString() ) );
73  }
74  if ( properties.contains( QStringLiteral( "symbol_width_map_unit_scale" ) ) )
75  {
76  layer->setSymbolWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "symbol_width_map_unit_scale" )].toString() ) );
77  }
78  if ( properties.contains( QStringLiteral( "symbol_height" ) ) )
79  {
80  layer->setSymbolHeight( properties[QStringLiteral( "symbol_height" )].toDouble() );
81  }
82  if ( properties.contains( QStringLiteral( "symbol_height_unit" ) ) )
83  {
84  layer->setSymbolHeightUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "symbol_height_unit" )].toString() ) );
85  }
86  if ( properties.contains( QStringLiteral( "symbol_height_map_unit_scale" ) ) )
87  {
88  layer->setSymbolHeightMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "symbol_height_map_unit_scale" )].toString() ) );
89  }
90  if ( properties.contains( QStringLiteral( "angle" ) ) )
91  {
92  layer->setAngle( properties[QStringLiteral( "angle" )].toDouble() );
93  }
94  if ( properties.contains( QStringLiteral( "outline_style" ) ) )
95  {
96  layer->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( properties[QStringLiteral( "outline_style" )].toString() ) );
97  }
98  else if ( properties.contains( QStringLiteral( "line_style" ) ) )
99  {
100  layer->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( properties[QStringLiteral( "line_style" )].toString() ) );
101  }
102  if ( properties.contains( QStringLiteral( "joinstyle" ) ) )
103  {
104  layer->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( properties[QStringLiteral( "joinstyle" )].toString() ) );
105  }
106  if ( properties.contains( QStringLiteral( "cap_style" ) ) )
107  {
108  layer->setPenCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( properties[QStringLiteral( "cap_style" )].toString() ) );
109  }
110  if ( properties.contains( QStringLiteral( "outline_width" ) ) )
111  {
112  layer->setStrokeWidth( properties[QStringLiteral( "outline_width" )].toDouble() );
113  }
114  else if ( properties.contains( QStringLiteral( "line_width" ) ) )
115  {
116  layer->setStrokeWidth( properties[QStringLiteral( "line_width" )].toDouble() );
117  }
118  if ( properties.contains( QStringLiteral( "outline_width_unit" ) ) )
119  {
120  layer->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "outline_width_unit" )].toString() ) );
121  }
122  else if ( properties.contains( QStringLiteral( "line_width_unit" ) ) )
123  {
124  layer->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "line_width_unit" )].toString() ) );
125  }
126  if ( properties.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
127  {
128  layer->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
129  }
130  if ( properties.contains( QStringLiteral( "fill_color" ) ) )
131  {
132  //pre 2.5 projects used "fill_color"
133  layer->setFillColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "fill_color" )].toString() ) );
134  }
135  else if ( properties.contains( QStringLiteral( "color" ) ) )
136  {
137  layer->setFillColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "color" )].toString() ) );
138  }
139  if ( properties.contains( QStringLiteral( "outline_color" ) ) )
140  {
141  layer->setStrokeColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "outline_color" )].toString() ) );
142  }
143  else if ( properties.contains( QStringLiteral( "line_color" ) ) )
144  {
145  layer->setStrokeColor( QgsSymbolLayerUtils::decodeColor( properties[QStringLiteral( "line_color" )].toString() ) );
146  }
147  if ( properties.contains( QStringLiteral( "offset" ) ) )
148  {
149  layer->setOffset( QgsSymbolLayerUtils::decodePoint( properties[QStringLiteral( "offset" )].toString() ) );
150  }
151  if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
152  {
153  layer->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )].toString() ) );
154  }
155  if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
156  {
157  layer->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
158  }
159  if ( properties.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
160  {
161  layer->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( properties[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
162  }
163  if ( properties.contains( QStringLiteral( "vertical_anchor_point" ) ) )
164  {
165  layer->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( properties[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
166  }
167 
168  //data defined properties
170 
171  return layer;
172 }
173 
175 {
176  double scaledWidth = mSymbolWidth;
177  double scaledHeight = mSymbolHeight;
178 
179  QColor brushColor = mColor;
180  brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
181  mBrush.setColor( brushColor );
182 
183  QColor penColor = mStrokeColor;
184  penColor.setAlphaF( penColor.alphaF() * context.opacity() );
185  mPen.setColor( penColor );
186 
187  bool ok;
189  {
190  context.setOriginalValueVariable( mStrokeWidth );
192  if ( !QgsVariantUtils::isNull( exprVal ) )
193  {
194  double width = exprVal.toDouble( &ok );
195  if ( ok )
196  {
197  width = context.renderContext().convertToPainterUnits( width, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
198  mPen.setWidthF( width );
199  mSelPen.setWidthF( width );
200  }
201  }
202  }
203 
205  {
208  if ( !QgsVariantUtils::isNull( exprVal ) )
209  {
210  mPen.setStyle( QgsSymbolLayerUtils::decodePenStyle( exprVal.toString() ) );
211  mSelPen.setStyle( mPen.style() );
212  }
213  }
214 
216  {
219  if ( !QgsVariantUtils::isNull( exprVal ) )
220  {
221  mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( exprVal.toString() ) );
222  mSelPen.setJoinStyle( mPen.joinStyle() );
223  }
224  }
225 
227  {
229  const QString style = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyCapStyle, context.renderContext().expressionContext(), QString(), &ok );
230  if ( ok )
231  {
232  mPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
233  mSelPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
234  }
235  }
236 
238  {
241  brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
242  mBrush.setColor( brushColor );
243  }
244 
246  {
249  penColor.setAlphaF( penColor.alphaF() * context.opacity() );
250  mPen.setColor( penColor );
251  }
252 
255  {
258  if ( !QgsVariantUtils::isNull( exprVal ) )
259  {
260  shape = decodeShape( exprVal.toString() );
261  }
262  preparePath( shape, context, &scaledWidth, &scaledHeight, context.feature() );
263  }
264 
265  //offset and rotation
266  bool hasDataDefinedRotation = false;
267  QPointF offset;
268  double angle = 0;
269  calculateOffsetAndRotation( context, scaledWidth, scaledHeight, hasDataDefinedRotation, offset, angle );
270 
271  QPainter *p = context.renderContext().painter();
272  if ( !p )
273  {
274  return;
275  }
276 
277  QTransform transform;
278  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
279  if ( !qgsDoubleNear( angle, 0.0 ) )
280  {
281  transform.rotate( angle );
282  }
283 
284  if ( shapeIsFilled( shape ) )
285  {
286  p->setPen( context.selected() ? mSelPen : mPen );
287  p->setBrush( context.selected() ? mSelBrush : mBrush );
288  }
289  else
290  {
291  p->setPen( context.selected() ? mSelPen : mPen );
292  p->setBrush( QBrush() );
293  }
294  p->drawPath( transform.map( mPainterPath ) );
295 }
296 
297 
298 void QgsEllipseSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
299  double scaledWidth,
300  double scaledHeight,
301  bool &hasDataDefinedRotation,
302  QPointF &offset,
303  double &angle ) const
304 {
305  double offsetX = 0;
306  double offsetY = 0;
307  markerOffset( context, scaledWidth, scaledHeight, mSymbolWidthUnit, mSymbolHeightUnit, offsetX, offsetY, mSymbolWidthMapUnitScale, mSymbolHeightMapUnitScale );
308  offset = QPointF( offsetX, offsetY );
309 
310 //priority for rotation: 1. data defined symbol level, 2. symbol layer rotation (mAngle)
311  const bool ok = true;
312  angle = mAngle + mLineAngle;
313  bool usingDataDefinedRotation = false;
315  {
316  context.setOriginalValueVariable( angle );
318  usingDataDefinedRotation = ok;
319  }
320 
321  hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || usingDataDefinedRotation;
322  if ( hasDataDefinedRotation )
323  {
324  // For non-point markers, "dataDefinedRotation" means following the
325  // shape (shape-data defined). For them, "field-data defined" does
326  // not work at all. TODO: if "field-data defined" ever gets implemented
327  // we'll need a way to distinguish here between the two, possibly
328  // using another flag in renderHints()
329  const QgsFeature *f = context.feature();
330  if ( f )
331  {
332  const QgsGeometry g = f->geometry();
333  if ( !g.isNull() && g.type() == QgsWkbTypes::PointGeometry )
334  {
335  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
336  angle += m2p.mapRotation();
337  }
338  }
339  }
340 
341  if ( angle )
343 }
344 
346 {
347  return QStringLiteral( "EllipseMarker" );
348 }
349 
351 {
352  QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
353  if ( !context.feature() || !dataDefinedProperties().hasActiveProperties() )
354  {
355  preparePath( mShape, context );
356  }
357  mPen.setColor( mStrokeColor );
358  mPen.setStyle( mStrokeStyle );
359  mPen.setJoinStyle( mPenJoinStyle );
360  mPen.setCapStyle( mPenCapStyle );
361  mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
362  mBrush.setColor( mColor );
363 
364  QColor selBrushColor = context.renderContext().selectionColor();
365  QColor selPenColor = selBrushColor == mColor ? selBrushColor : mStrokeColor;
366  if ( context.opacity() < 1 && !SELECTION_IS_OPAQUE )
367  {
368  selBrushColor.setAlphaF( context.opacity() );
369  selPenColor.setAlphaF( context.opacity() );
370  }
371  mSelBrush = QBrush( selBrushColor );
372  mSelPen = QPen( !shapeIsFilled( mShape ) ? selBrushColor : selPenColor );
373  mSelPen.setStyle( mStrokeStyle );
374  mSelPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
375 }
376 
378 {
379 }
380 
382 {
384  m->setShape( mShape );
385  m->setSymbolWidth( mSymbolWidth );
386  m->setSymbolHeight( mSymbolHeight );
387  m->setStrokeStyle( mStrokeStyle );
388  m->setOffset( mOffset );
391  m->setStrokeStyle( mStrokeStyle );
392  m->setPenJoinStyle( mPenJoinStyle );
393  m->setPenCapStyle( mPenCapStyle );
394  m->setStrokeWidth( mStrokeWidth );
395  m->setColor( color() );
396  m->setStrokeColor( mStrokeColor );
397  m->setSymbolWidthUnit( mSymbolWidthUnit );
398  m->setSymbolWidthMapUnitScale( mSymbolWidthMapUnitScale );
399  m->setSymbolHeightUnit( mSymbolHeightUnit );
400  m->setSymbolHeightMapUnitScale( mSymbolHeightMapUnitScale );
401  m->setStrokeWidthUnit( mStrokeWidthUnit );
402  m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
403  m->setAngle( mAngle );
406 
408  copyPaintEffect( m );
409  return m;
410 }
411 
412 void QgsEllipseSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
413 {
414  QDomElement symbolizerElem = doc.createElement( QStringLiteral( "se:PointSymbolizer" ) );
415  if ( !props.value( QStringLiteral( "uom" ), QString() ).toString().isEmpty() )
416  symbolizerElem.setAttribute( QStringLiteral( "uom" ), props.value( QStringLiteral( "uom" ), QString() ).toString() );
417  element.appendChild( symbolizerElem );
418 
419  // <Geometry>
420  QgsSymbolLayerUtils::createGeometryElement( doc, symbolizerElem, props.value( QStringLiteral( "geom" ), QString() ).toString() );
421 
422  writeSldMarker( doc, symbolizerElem, props );
423 }
424 
425 void QgsEllipseSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
426 {
427  // <Graphic>
428  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
429  element.appendChild( graphicElem );
430 
431  const double strokeWidth = QgsSymbolLayerUtils::rescaleUom( mStrokeWidth, mStrokeWidthUnit, props );
432  const double symbolWidth = QgsSymbolLayerUtils::rescaleUom( mSymbolWidth, mSymbolWidthUnit, props );
433  QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, encodeShape( mShape ), mColor, mStrokeColor, mStrokeStyle, strokeWidth, symbolWidth );
434 
435  // <Rotation>
437 
438  QString angleFunc = props.value( QStringLiteral( "angle" ), QString() ).toString();
439  if ( angleFunc.isEmpty() ) // symbol has no angle set
440  {
441  if ( ddRotation && ddRotation.isActive() )
442  {
443  angleFunc = ddRotation.asExpression();
444  }
445  else if ( !qgsDoubleNear( mAngle, 0.0 ) )
446  angleFunc = QString::number( mAngle );
447  }
448  else if ( ddRotation && ddRotation.isActive() )
449  {
450  // the symbol has an angle and the symbol layer have a rotation
451  // property set
452  angleFunc = QStringLiteral( "%1 + %2" ).arg( angleFunc, ddRotation.asExpression() );
453  }
454  else if ( !qgsDoubleNear( mAngle, 0.0 ) )
455  {
456  // both the symbol and the symbol layer have angle value set
457  bool ok;
458  const double angle = angleFunc.toDouble( &ok );
459  if ( !ok )
460  {
461  // its a string (probably a property name or a function)
462  angleFunc = QStringLiteral( "%1 + %2" ).arg( angleFunc ).arg( mAngle );
463  }
464  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
465  {
466  // it's a double value
467  angleFunc = QString::number( angle + mAngle );
468  }
469  }
470  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
471 
472  // <Displacement>
473  const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
475 
476  // store w/h factor in a <VendorOption>
477  const double widthHeightFactor = mSymbolWidth / mSymbolHeight;
478  const QDomElement factorElem = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "widthHeightFactor" ), QString::number( widthHeightFactor ) );
479  graphicElem.appendChild( factorElem );
480 }
481 
483 {
484  QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
485 
486  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
487  if ( graphicElem.isNull() )
488  return nullptr;
489 
490  QString name = QStringLiteral( "circle" );
491  QColor fillColor, strokeColor;
492  double strokeWidth, size;
493  double widthHeightFactor = 1.0;
494  Qt::PenStyle strokeStyle;
495 
496  QgsStringMap vendorOptions = QgsSymbolLayerUtils::getVendorOptionList( graphicElem );
497  for ( QgsStringMap::iterator it = vendorOptions.begin(); it != vendorOptions.end(); ++it )
498  {
499  if ( it.key() == QLatin1String( "widthHeightFactor" ) )
500  {
501  bool ok;
502  const double v = it.value().toDouble( &ok );
503  if ( ok && !qgsDoubleNear( v, 0.0 ) && v > 0 )
504  widthHeightFactor = v;
505  }
506  }
507 
509  return nullptr;
510 
511  double scaleFactor = 1.0;
512  const QString uom = element.attribute( QStringLiteral( "uom" ) );
513  QgsUnitTypes::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
514  size = size * scaleFactor;
515  strokeWidth = strokeWidth * scaleFactor;
516 
517  double angle = 0.0;
518  QString angleFunc;
519  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
520  {
521  bool ok;
522  const double d = angleFunc.toDouble( &ok );
523  if ( ok )
524  angle = d;
525  }
526 
528  m->setOutputUnit( sldUnitSize );
529  m->setShape( decodeShape( name ) );
530  m->setFillColor( fillColor );
534  m->setSymbolWidth( size );
535  m->setSymbolHeight( size / widthHeightFactor );
536  m->setAngle( angle );
537  return m;
538 }
539 
541 {
542  QVariantMap map;
543  map[QStringLiteral( "symbol_name" )] = encodeShape( mShape );
544  map[QStringLiteral( "symbol_width" )] = QString::number( mSymbolWidth );
545  map[QStringLiteral( "symbol_width_unit" )] = QgsUnitTypes::encodeUnit( mSymbolWidthUnit );
546  map[QStringLiteral( "symbol_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSymbolWidthMapUnitScale );
547  map[QStringLiteral( "symbol_height" )] = QString::number( mSymbolHeight );
548  map[QStringLiteral( "symbol_height_unit" )] = QgsUnitTypes::encodeUnit( mSymbolHeightUnit );
549  map[QStringLiteral( "symbol_height_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSymbolHeightMapUnitScale );
550  map[QStringLiteral( "angle" )] = QString::number( mAngle );
551  map[QStringLiteral( "outline_style" )] = QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle );
552  map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
553  map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
554  map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
555  map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
556  map[QStringLiteral( "cap_style" )] = QgsSymbolLayerUtils::encodePenCapStyle( mPenCapStyle );
557  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
558  map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
559  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
560  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
561  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
562  map[QStringLiteral( "size" )] = QString::number( mSize );
563  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
564  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
565  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
566  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
567  return map;
568 }
569 
570 QSizeF QgsEllipseSymbolLayer::calculateSize( QgsSymbolRenderContext &context, double *scaledWidth, double *scaledHeight )
571 {
572  double width = 0;
573 
574  if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyWidth ) ) //1. priority: data defined setting on symbol layer le
575  {
576  context.setOriginalValueVariable( mSymbolWidth );
578  }
579  else //2. priority: global width setting
580  {
581  width = mSymbolWidth;
582  }
583  if ( scaledWidth )
584  {
585  *scaledWidth = width;
586  }
587  width = context.renderContext().convertToPainterUnits( width, mSymbolWidthUnit, mSymbolHeightMapUnitScale );
588 
589  double height = 0;
590  if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyHeight ) ) //1. priority: data defined setting on symbol layer level
591  {
592  context.setOriginalValueVariable( mSymbolHeight );
594  }
595  else //2. priority: global height setting
596  {
597  height = mSymbolHeight;
598  }
599  if ( scaledHeight )
600  {
601  *scaledHeight = height;
602  }
603  height = context.renderContext().convertToPainterUnits( height, mSymbolHeightUnit, mSymbolHeightMapUnitScale );
604  return QSizeF( width, height );
605 }
606 
607 void QgsEllipseSymbolLayer::preparePath( const QgsEllipseSymbolLayer::Shape &shape, QgsSymbolRenderContext &context, double *scaledWidth, double *scaledHeight, const QgsFeature * )
608 {
609  mPainterPath = QPainterPath();
610 
611  const QSizeF size = calculateSize( context, scaledWidth, scaledHeight );
612 
613  switch ( shape )
614  {
615  case Circle:
616  mPainterPath.addEllipse( QRectF( -size.width() / 2.0, -size.height() / 2.0, size.width(), size.height() ) );
617  return;
618 
619  case SemiCircle:
620  mPainterPath.arcTo( -size.width() / 2.0, -size.height() / 2.0, size.width(), size.height(), 0, 180 );
621  mPainterPath.lineTo( 0, 0 );
622  return;
623 
624  case ThirdCircle:
625  mPainterPath.arcTo( -size.width() / 2.0, -size.height() / 2.0, size.width(), size.height(), 90, 120 );
626  mPainterPath.lineTo( 0, 0 );
627  return;
628 
629  case QuarterCircle:
630  mPainterPath.arcTo( -size.width() / 2.0, -size.height() / 2.0, size.width(), size.height(), 90, 90 );
631  mPainterPath.lineTo( 0, 0 );
632  return;
633 
634  case Rectangle:
635  mPainterPath.addRect( QRectF( -size.width() / 2.0, -size.height() / 2.0, size.width(), size.height() ) );
636  return;
637 
638  case Diamond:
639  mPainterPath.moveTo( -size.width() / 2.0, 0 );
640  mPainterPath.lineTo( 0, size.height() / 2.0 );
641  mPainterPath.lineTo( size.width() / 2.0, 0 );
642  mPainterPath.lineTo( 0, -size.height() / 2.0 );
643  mPainterPath.lineTo( -size.width() / 2.0, 0 );
644  return;
645 
646  case Cross:
647  mPainterPath.moveTo( 0, -size.height() / 2.0 );
648  mPainterPath.lineTo( 0, size.height() / 2.0 );
649  mPainterPath.moveTo( -size.width() / 2.0, 0 );
650  mPainterPath.lineTo( size.width() / 2.0, 0 );
651  return;
652 
653  case Arrow:
654  mPainterPath.moveTo( -size.width() / 2.0, size.height() / 2.0 );
655  mPainterPath.lineTo( 0, -size.height() / 2.0 );
656  mPainterPath.lineTo( size.width() / 2.0, size.height() / 2.0 );
657  return;
658 
659  case HalfArc:
660  mPainterPath.moveTo( size.width() / 2.0, 0 );
661  mPainterPath.arcTo( -size.width() / 2.0, -size.height() / 2.0, size.width(), size.height(), 0, 180 );
662  return;
663 
664  case Triangle:
665  mPainterPath.moveTo( 0, -size.height() / 2.0 );
666  mPainterPath.lineTo( -size.width() / 2.0, size.height() / 2.0 );
667  mPainterPath.lineTo( size.width() / 2.0, size.height() / 2.0 );
668  mPainterPath.lineTo( 0, -size.height() / 2.0 );
669  return;
670 
671  case LeftHalfTriangle:
672  mPainterPath.moveTo( 0, size.height() / 2.0 );
673  mPainterPath.lineTo( size.width() / 2.0, size.height() / 2.0 );
674  mPainterPath.lineTo( 0, -size.height() / 2.0 );
675  mPainterPath.lineTo( 0, size.height() / 2.0 );
676  return;
677 
678  case RightHalfTriangle:
679  mPainterPath.moveTo( -size.width() / 2.0, size.height() / 2.0 );
680  mPainterPath.lineTo( 0, size.height() / 2.0 );
681  mPainterPath.lineTo( 0, -size.height() / 2.0 );
682  mPainterPath.lineTo( -size.width() / 2.0, size.height() / 2.0 );
683  return;
684 
685  case Pentagon:
686  mPainterPath.moveTo( ( size.width() * -0.9511 ) / 2.0, size.height() / ( 2 / -0.309 ) );
687  mPainterPath.lineTo( ( size.width() * -0.5878 ) / 2.0, size.height() / ( 2 / 0.8090 ) );
688  mPainterPath.lineTo( ( size.width() * 0.5878 ) / 2.0, size.height() / ( 2 / 0.8090 ) );
689  mPainterPath.lineTo( ( size.width() * 0.9511 ) / 2.0, size.height() / ( 2 / -0.309 ) );
690  mPainterPath.lineTo( 0, size.height() / -2.0 );
691  mPainterPath.lineTo( ( size.width() * -0.9511 ) / 2.0, size.height() / ( 2 / -0.309 ) );
692  return;
693 
694  case Hexagon:
695  mPainterPath.moveTo( ( size.width() * 0.8660 ) / 2.0, size.height() / 4.0 );
696  mPainterPath.lineTo( ( size.width() * 0.8660 ) / 2.0, size.height() / -4.0 );
697  mPainterPath.lineTo( 0, size.height() / -2.0 );
698  mPainterPath.lineTo( ( size.width() * 0.8660 ) / -2.0, size.height() / -4.0 );
699  mPainterPath.lineTo( ( size.width() * 0.8660 ) / -2.0, size.height() / 4.0 );
700  mPainterPath.lineTo( 0, size.height() / 2.0 );
701  mPainterPath.lineTo( ( size.width() * 0.8660 ) / 2.0, size.height() / 4.0 );
702  return;
703 
704  case Octagon:
705  {
706  static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 1.0 / ( 1 + M_SQRT2 );
707  mPainterPath.moveTo( ( size.width() * VERTEX_OFFSET_FROM_ORIGIN ) / -2.0, size.height() / 2.0 );
708  mPainterPath.lineTo( ( size.width() * VERTEX_OFFSET_FROM_ORIGIN ) / 2.0, size.height() / 2.0 );
709  mPainterPath.lineTo( size.width() / 2.0, ( size.height() * VERTEX_OFFSET_FROM_ORIGIN ) / 2.0 );
710  mPainterPath.lineTo( size.width() / 2.0, ( size.height() * VERTEX_OFFSET_FROM_ORIGIN ) / -2.0 );
711  mPainterPath.lineTo( ( size.width() * VERTEX_OFFSET_FROM_ORIGIN ) / 2.0, size.height() / -2.0 );
712  mPainterPath.lineTo( ( size.width() * VERTEX_OFFSET_FROM_ORIGIN ) / -2.0, size.height() / -2.0 );
713  mPainterPath.lineTo( size.width() / -2.0, ( size.height() * VERTEX_OFFSET_FROM_ORIGIN ) / -2.0 );
714  mPainterPath.lineTo( size.width() / -2.0, ( size.height() * VERTEX_OFFSET_FROM_ORIGIN ) / 2.0 );
715  mPainterPath.lineTo( ( size.width() * VERTEX_OFFSET_FROM_ORIGIN ) / -2.0, size.height() / 2.0 );
716  return;
717  }
718 
719  case Star:
720  {
721  const double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
722  mPainterPath.moveTo( ( size.width() * inner_r * std::sin( DEG2RAD( 324.0 ) ) ) / 2.0, ( size.height() * inner_r * std::cos( DEG2RAD( 324.0 ) ) ) / -2.0 );
723  mPainterPath.lineTo( ( size.width() * std::sin( DEG2RAD( 288.0 ) ) ) / 2.0, ( size.height() * std::cos( DEG2RAD( 288.0 ) ) ) / -2.0 );
724  mPainterPath.lineTo( ( size.width() * inner_r * std::sin( DEG2RAD( 252.0 ) ) ) / 2.0, ( size.height() * inner_r * std::cos( DEG2RAD( 252.0 ) ) ) / -2.0 );
725  mPainterPath.lineTo( ( size.width() * std::sin( DEG2RAD( 216.0 ) ) ) / 2.0, ( size.height() * std::cos( DEG2RAD( 216.0 ) ) ) / -2.0 );
726  mPainterPath.lineTo( 0, ( size.height() * inner_r ) / 2.0 );
727  mPainterPath.lineTo( ( size.width() * std::sin( DEG2RAD( 144.0 ) ) ) / 2.0, ( size.height() * std::cos( DEG2RAD( 144.0 ) ) ) / -2.0 );
728  mPainterPath.lineTo( ( size.width() * inner_r * std::sin( DEG2RAD( 108.0 ) ) ) / 2.0, ( size.height() * inner_r * std::cos( DEG2RAD( 108.0 ) ) ) / -2.0 );
729  mPainterPath.lineTo( ( size.width() * std::sin( DEG2RAD( 72.0 ) ) ) / 2.0, ( size.height() * std::cos( DEG2RAD( 72.0 ) ) ) / -2.0 );
730  mPainterPath.lineTo( ( size.width() * inner_r * std::sin( DEG2RAD( 36.0 ) ) ) / 2.0, ( size.height() * inner_r * std::cos( DEG2RAD( 36.0 ) ) ) / -2.0 );
731  mPainterPath.lineTo( 0, size.height() / -2.0 );
732  mPainterPath.lineTo( ( size.width() * inner_r * std::sin( DEG2RAD( 324.0 ) ) ) / 2.0, ( size.height() * inner_r * std::cos( DEG2RAD( 324.0 ) ) ) / -2.0 );
733  return;
734  }
735  }
736 }
737 
739 {
740  switch ( shape )
741  {
742  case Circle:
743  case Rectangle:
744  case Diamond:
745  case Triangle:
746  case RightHalfTriangle:
747  case LeftHalfTriangle:
748  case SemiCircle:
749  case ThirdCircle:
750  case QuarterCircle:
751  case Pentagon:
752  case Hexagon:
753  case Octagon:
754  case Star:
755  return true;
756 
757  case Cross:
758  case Arrow:
759  case HalfArc:
760  return false;
761  }
762 
763  return true;
764 }
765 
767 {
768  if ( mSymbolWidth >= mSymbolHeight )
769  {
770  mSymbolHeight = mSymbolHeight * size / mSymbolWidth;
771  mSymbolWidth = size;
772  }
773  else
774  {
775  mSymbolWidth = mSymbolWidth * size / mSymbolHeight;
776  mSymbolHeight = size;
777  }
779 }
780 
782 {
783  mSymbolWidth = w;
784  QgsMarkerSymbolLayer::setSize( mSymbolWidth >= mSymbolHeight ? mSymbolWidth : mSymbolHeight );
785 }
786 
788 {
789  mSymbolHeight = h;
790  QgsMarkerSymbolLayer::setSize( mSymbolWidth >= mSymbolHeight ? mSymbolWidth : mSymbolHeight );
791 }
792 
794 {
796  mSymbolWidthUnit = unit;
797  mSymbolHeightUnit = unit;
798  mStrokeWidthUnit = unit;
799 }
800 
802 {
804  if ( mSymbolWidthUnit != unit || mSymbolHeightUnit != unit || mStrokeWidthUnit != unit )
805  {
807  }
808  return unit;
809 }
810 
812 {
813  return mSymbolWidthUnit == QgsUnitTypes::RenderMapUnits || mSymbolWidthUnit == QgsUnitTypes::RenderMetersInMapUnits
814  || mSymbolHeightUnit == QgsUnitTypes::RenderMapUnits || mSymbolHeightUnit == QgsUnitTypes::RenderMetersInMapUnits
815  || mStrokeWidthUnit == QgsUnitTypes::RenderMapUnits || mStrokeWidthUnit == QgsUnitTypes::RenderMetersInMapUnits
817 }
818 
820 {
822  mSymbolWidthMapUnitScale = scale;
823  mSymbolHeightMapUnitScale = scale;
824  mStrokeWidthMapUnitScale = scale;
825 }
826 
828 {
829  if ( QgsMarkerSymbolLayer::mapUnitScale() == mSymbolWidthMapUnitScale &&
830  mSymbolWidthMapUnitScale == mSymbolHeightMapUnitScale &&
831  mSymbolHeightMapUnitScale == mStrokeWidthMapUnitScale )
832  {
833  return mSymbolWidthMapUnitScale;
834  }
835  return QgsMapUnitScale();
836 }
837 
838 QRectF QgsEllipseSymbolLayer::bounds( QPointF point, QgsSymbolRenderContext &context )
839 {
840  const QSizeF size = calculateSize( context );
841 
842  bool hasDataDefinedRotation = false;
843  QPointF offset;
844  double angle = 0;
845  calculateOffsetAndRotation( context, size.width(), size.height(), hasDataDefinedRotation, offset, angle );
846 
847  QTransform transform;
848 
849  // move to the desired position
850  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
851 
852  if ( !qgsDoubleNear( angle, 0.0 ) )
853  transform.rotate( angle );
854 
855  double penWidth = mStrokeWidth;
857  {
858  context.setOriginalValueVariable( mStrokeWidth );
860 
861  if ( !QgsVariantUtils::isNull( exprVal ) )
862  {
863  bool ok;
864  const double strokeWidth = exprVal.toDouble( &ok );
865  if ( ok )
866  {
867  penWidth = strokeWidth;
868  }
869  }
870  }
871  penWidth = context.renderContext().convertToPainterUnits( penWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
872 
874  {
877  if ( !QgsVariantUtils::isNull( exprVal ) && exprVal.toString() == QLatin1String( "no" ) )
878  {
879  penWidth = 0.0;
880  }
881  }
882  else if ( mStrokeStyle == Qt::NoPen )
883  penWidth = 0;
884 
885  //antialiasing, add 1 pixel
886  penWidth += 1;
887 
888  QRectF symbolBounds = transform.mapRect( QRectF( -size.width() / 2.0,
889  -size.height() / 2.0,
890  size.width(),
891  size.height() ) );
892 
893  //extend bounds by pen width / 2.0
894  symbolBounds.adjust( -penWidth / 2.0, -penWidth / 2.0,
895  penWidth / 2.0, penWidth / 2.0 );
896 
897  return symbolBounds;
898 }
899 
900 bool QgsEllipseSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
901 {
902  //width
903  double symbolWidth = mSymbolWidth;
904 
905  if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyWidth ) ) //1. priority: data defined setting on symbol layer le
906  {
907  context.setOriginalValueVariable( mSymbolWidth );
909  }
910  if ( mSymbolWidthUnit == QgsUnitTypes::RenderMillimeters )
911  {
912  symbolWidth *= mmMapUnitScaleFactor;
913  }
914 
915  //height
916  double symbolHeight = mSymbolHeight;
917  if ( mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyHeight ) ) //1. priority: data defined setting on symbol layer level
918  {
919  context.setOriginalValueVariable( mSymbolHeight );
921  }
922  if ( mSymbolHeightUnit == QgsUnitTypes::RenderMillimeters )
923  {
924  symbolHeight *= mmMapUnitScaleFactor;
925  }
926 
927  //stroke width
928  double strokeWidth = mStrokeWidth;
929 
931  {
932  context.setOriginalValueVariable( mStrokeWidth );
934  }
935  if ( mStrokeWidthUnit == QgsUnitTypes::RenderMillimeters )
936  {
938  }
939 
940  //fill color
941  QColor fc = mColor;
943  {
946  }
947 
948  //stroke color
949  QColor oc = mStrokeColor;
951  {
954  }
955 
956  //symbol name
959  {
962  }
963 
964  //offset
965  double offsetX = 0;
966  double offsetY = 0;
967  markerOffset( context, offsetX, offsetY );
968  QPointF off( offsetX, offsetY );
969 
970  //priority for rotation: 1. data defined symbol level, 2. symbol layer rotation (mAngle)
971  double rotation = 0.0;
973  {
974  context.setOriginalValueVariable( mAngle );
976  }
977  else if ( !qgsDoubleNear( mAngle + mLineAngle, 0.0 ) )
978  {
979  rotation = mAngle + mLineAngle;
980  }
981  rotation = -rotation; //rotation in Qt is counterclockwise
982  if ( rotation )
983  off = _rotatedOffset( off, rotation );
984 
985  QTransform t;
986  t.translate( shift.x() + offsetX, shift.y() + offsetY );
987 
988  if ( !qgsDoubleNear( rotation, 0.0 ) )
989  t.rotate( rotation );
990 
991  const double halfWidth = symbolWidth / 2.0;
992  const double halfHeight = symbolHeight / 2.0;
993 
994  switch ( shape )
995  {
996  case Circle:
997  {
998  if ( qgsDoubleNear( halfWidth, halfHeight ) )
999  {
1000  const QgsPoint pt( t.map( QPointF( 0, 0 ) ) );
1001  e.writeFilledCircle( layerName, oc, pt, halfWidth );
1002  }
1003  else
1004  {
1005  QgsPointSequence line;
1006 
1007  const double stepsize = 2 * M_PI / 40;
1008  for ( int i = 0; i < 39; ++i )
1009  {
1010  const double angle = stepsize * i;
1011  const double x = halfWidth * std::cos( angle );
1012  const double y = halfHeight * std::sin( angle );
1013  line << QgsPoint( t.map( QPointF( x, y ) ) );
1014  }
1015  //close ellipse with first point
1016  line << line.at( 0 );
1017 
1018  if ( mBrush.style() != Qt::NoBrush )
1019  e.writePolygon( QgsRingSequence() << line, layerName, QStringLiteral( "SOLID" ), fc );
1020  if ( mPen.style() != Qt::NoPen )
1021  e.writePolyline( line, layerName, QStringLiteral( "CONTINUOUS" ), oc, strokeWidth );
1022  }
1023  return true;
1024  }
1025 
1026  case Rectangle:
1027  {
1028  QgsPointSequence p;
1029  p << QgsPoint( t.map( QPointF( -halfWidth, -halfHeight ) ) )
1030  << QgsPoint( t.map( QPointF( halfWidth, -halfHeight ) ) )
1031  << QgsPoint( t.map( QPointF( halfWidth, halfHeight ) ) )
1032  << QgsPoint( t.map( QPointF( -halfWidth, halfHeight ) ) );
1033  p << p[0];
1034 
1035  if ( mBrush.style() != Qt::NoBrush )
1036  e.writePolygon( QgsRingSequence() << p, layerName, QStringLiteral( "SOLID" ), fc );
1037  if ( mPen.style() != Qt::NoPen )
1038  e.writePolyline( p, layerName, QStringLiteral( "CONTINUOUS" ), oc, strokeWidth );
1039  return true;
1040  }
1041  case Cross:
1042  {
1043  if ( mPen.style() != Qt::NoPen )
1044  {
1046  << QgsPoint( t.map( QPointF( -halfWidth, 0 ) ) )
1047  << QgsPoint( t.map( QPointF( halfWidth, 0 ) ) ),
1048  layerName, QStringLiteral( "CONTINUOUS" ), oc, strokeWidth );
1050  << QgsPoint( t.map( QPointF( 0, halfHeight ) ) )
1051  << QgsPoint( t.map( QPointF( 0, -halfHeight ) ) ),
1052  layerName, QStringLiteral( "CONTINUOUS" ), oc, strokeWidth );
1053  return true;
1054  }
1055  break;
1056  }
1057 
1058  case Triangle:
1059  {
1060  QgsPointSequence p;
1061  p << QgsPoint( t.map( QPointF( -halfWidth, -halfHeight ) ) )
1062  << QgsPoint( t.map( QPointF( halfWidth, -halfHeight ) ) )
1063  << QgsPoint( t.map( QPointF( 0, halfHeight ) ) );
1064  p << p[0];
1065  if ( mBrush.style() != Qt::NoBrush )
1066  e.writePolygon( QgsRingSequence() << p, layerName, QStringLiteral( "SOLID" ), fc );
1067  if ( mPen.style() != Qt::NoPen )
1068  e.writePolyline( p, layerName, QStringLiteral( "CONTINUOUS" ), oc, strokeWidth );
1069  return true;
1070  }
1071 
1072  case Diamond:
1073  case Arrow:
1074  case HalfArc:
1075  case RightHalfTriangle:
1076  case LeftHalfTriangle:
1077  case SemiCircle:
1078  case ThirdCircle:
1079  case QuarterCircle:
1080  case Pentagon:
1081  case Hexagon:
1082  case Octagon:
1083  case Star:
1084  return false;
1085  }
1086 
1087  return false;
1088 }
1089 
1091 {
1092  if ( ok )
1093  *ok = true;
1094  const QString cleaned = name.toLower().trimmed();
1095 
1096  if ( cleaned == QLatin1String( "circle" ) )
1097  return Circle;
1098  else if ( cleaned == QLatin1String( "square" ) || cleaned == QLatin1String( "rectangle" ) )
1099  return Rectangle;
1100  else if ( cleaned == QLatin1String( "diamond" ) )
1101  return Diamond;
1102  else if ( cleaned == QLatin1String( "cross" ) )
1103  return Cross;
1104  else if ( cleaned == QLatin1String( "arrow" ) )
1105  return Arrow;
1106  else if ( cleaned == QLatin1String( "half_arc" ) )
1107  return HalfArc;
1108  else if ( cleaned == QLatin1String( "triangle" ) )
1109  return Triangle;
1110  else if ( cleaned == QLatin1String( "right_half_triangle" ) )
1111  return RightHalfTriangle;
1112  else if ( cleaned == QLatin1String( "left_half_triangle" ) )
1113  return LeftHalfTriangle;
1114  else if ( cleaned == QLatin1String( "semi_circle" ) )
1115  return SemiCircle;
1116  else if ( cleaned == QLatin1String( "third_circle" ) )
1117  return ThirdCircle;
1118  else if ( cleaned == QLatin1String( "quarter_circle" ) )
1119  return QuarterCircle;
1120  else if ( cleaned == QLatin1String( "pentagon" ) )
1121  return Pentagon;
1122  else if ( cleaned == QLatin1String( "hexagon" ) )
1123  return Hexagon;
1124  else if ( cleaned == QLatin1String( "octagon" ) )
1125  return Octagon;
1126  else if ( cleaned == QLatin1String( "star" ) )
1127  return Star; if ( ok )
1128  *ok = false;
1129  return Circle;
1130 }
1131 
1133 {
1134  switch ( shape )
1135  {
1136  case Circle:
1137  return QStringLiteral( "circle" );
1138  case Rectangle:
1139  return QStringLiteral( "rectangle" );
1140  case Diamond:
1141  return QStringLiteral( "diamond" );
1142  case Cross:
1143  return QStringLiteral( "cross" );
1144  case Arrow:
1145  return QStringLiteral( "arrow" );
1146  case HalfArc:
1147  return QStringLiteral( "half_arc" );
1148  case Triangle:
1149  return QStringLiteral( "triangle" );
1150  case RightHalfTriangle:
1151  return QStringLiteral( "right_half_triangle" );
1152  case LeftHalfTriangle:
1153  return QStringLiteral( "left_half_triangle" );
1154  case SemiCircle:
1155  return QStringLiteral( "semi_circle" );
1156  case ThirdCircle:
1157  return QStringLiteral( "third_circle" );
1158  case QuarterCircle:
1159  return QStringLiteral( "quarter_circle" );
1160  case Pentagon:
1161  return QStringLiteral( "pentagon" );
1162  case Hexagon:
1163  return QStringLiteral( "hexagon" );
1164  case Octagon:
1165  return QStringLiteral( "octagon" );
1166  case Star:
1167  return QStringLiteral( "star" );
1168  }
1169  return QString();
1170 }
1171 
1172 QList<QgsEllipseSymbolLayer::Shape> QgsEllipseSymbolLayer::availableShapes()
1173 {
1174  QList< Shape > shapes;
1175  shapes << Circle
1176  << Rectangle
1177  << Diamond
1178  << Cross
1179  << Arrow
1180  << HalfArc
1181  << Triangle
1182  << LeftHalfTriangle
1184  << SemiCircle
1185  << ThirdCircle
1186  << QuarterCircle
1187  << Pentagon
1188  << Hexagon
1189  << Octagon
1190  << Star;
1191  return shapes;
1192 }
@ DynamicRotation
Rotation of symbol may be changed during rendering and symbol should not be cached.
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.
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.
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.
Exports QGIS layers to the DXF format.
Definition: qgsdxfexport.h:65
void writeFilledCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius)
Write filled circle (as hatch)
void writePolygon(const QgsRingSequence &polygon, const QString &layer, const QString &hatchPattern, const QColor &color)
Draw dxf filled polygon (HATCH)
void writePolyline(const QgsPointSequence &line, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Draw dxf primitives (LWPOLYLINE)
A symbol layer for rendering objects with major and minor axis (e.g.
void setPenCapStyle(Qt::PenCapStyle style)
Sets the marker's stroke cap style (e.g., flat, round, etc).
bool writeDxf(QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift=QPointF(0.0, 0.0)) const override
write as DXF
void setSymbolHeightMapUnitScale(const QgsMapUnitScale &scale)
void setSymbolWidthMapUnitScale(const QgsMapUnitScale &scale)
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
void toSld(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Saves the symbol layer as SLD.
void setStrokeColor(const QColor &c) override
Sets the stroke color for the symbol layer.
void setStrokeStyle(Qt::PenStyle strokeStyle)
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
static QgsEllipseSymbolLayer::Shape decodeShape(const QString &name, bool *ok=nullptr)
Attempts to decode a string representation of a shape name to the corresponding shape.
void setSize(double size) override
Sets the symbol size.
QgsMapUnitScale mapUnitScale() const override
Shape
Marker symbol shapes.
@ ThirdCircle
Third Circle (since QGIS 3.28)
@ Pentagon
Pentagon (since QGIS 3.28)
@ Hexagon
Hexagon (since QGIS 3.28)
@ Cross
Stroke-only cross.
@ QuarterCircle
Quarter Circle (since QGIS 3.28)
@ HalfArc
Stroke-only half arc (since QGIS 3.20)
@ Arrow
Stroke-only arrow (since QGIS 3.20)
@ RightHalfTriangle
Right half of a triangle.
@ Star
Star (since QGIS 3.28)
@ Octagon
Octagon (since QGIS 3.28)
@ LeftHalfTriangle
Left half of a triangle.
QColor fillColor() const override
Returns the fill color for the symbol layer.
QgsEllipseSymbolLayer::Shape shape() const
Returns the shape for the rendered ellipse marker symbol.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
void setFillColor(const QColor &c) override
Sets the fill color for the symbol layer.
void setShape(QgsEllipseSymbolLayer::Shape shape)
Sets the rendered ellipse marker shape.
static QList< QgsEllipseSymbolLayer::Shape > availableShapes()
Returns a list of all available shape types.
Qt::PenStyle strokeStyle() const
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates the symbol layer.
static QgsSymbolLayer * createFromSld(QDomElement &element)
static QString encodeShape(QgsEllipseSymbolLayer::Shape shape)
Encodes a shape to its string representation.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
QString layerType() const override
Returns a string that represents this layer type.
QColor strokeColor() const override
Returns the stroke color for the symbol layer.
void setSymbolWidthUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol's width.
void setPenJoinStyle(Qt::PenJoinStyle style)
Set stroke join style.
static bool shapeIsFilled(const QgsEllipseSymbolLayer::Shape &shape)
Returns true if a shape has a fill.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol's stroke width.
~QgsEllipseSymbolLayer() override
void setMapUnitScale(const QgsMapUnitScale &scale) override
void setSymbolHeightUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol's height.
void writeSldMarker(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Writes the symbol layer definition as a SLD XML element.
QgsEllipseSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsGeometry geometry
Definition: qgsfeature.h:67
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:164
Q_GADGET bool isNull
Definition: qgsgeometry.h:166
QgsWkbTypes::GeometryType type
Definition: qgsgeometry.h:167
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
double mapRotation() const
Returns the current map rotation in degrees (clockwise).
Struct for storing maximum and minimum scales for measurements in map units.
double mSize
Marker size.
virtual void setSize(double size)
Sets the symbol size.
QPointF offset() const
Returns the marker's offset, which is the horizontal and vertical displacement which the rendered mar...
double mLineAngle
Line rotation angle (see setLineAngle() for details)
HorizontalAnchorPoint
Symbol horizontal anchor points.
void setAngle(double angle)
Sets the rotation angle for the marker.
QgsUnitTypes::RenderUnit mOffsetUnit
Offset units.
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol's size.
void setVerticalAnchorPoint(VerticalAnchorPoint v)
Sets the vertical anchor point for positioning the symbol.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QPointF mOffset
Marker offset.
void setHorizontalAnchorPoint(HorizontalAnchorPoint h)
Sets the horizontal anchor point for positioning the symbol.
QgsMapUnitScale mapUnitScale() const override
void setOffset(QPointF offset)
Sets the marker's offset, which is the horizontal and vertical displacement which the rendered marker...
void setSizeMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol's size.
double size() const
Returns the symbol size.
QgsMapUnitScale mOffsetMapUnitScale
Offset map unit scale.
HorizontalAnchorPoint mHorizontalAnchorPoint
Horizontal anchor point.
static QPointF _rotatedOffset(QPointF offset, double angle)
Adjusts a marker offset to account for rotation.
QgsMapUnitScale mSizeMapUnitScale
Marker size map unit scale.
VerticalAnchorPoint
Symbol vertical anchor points.
void markerOffset(QgsSymbolRenderContext &context, double &offsetX, double &offsetY) const
Calculates the required marker offset, including both the symbol offset and any displacement required...
VerticalAnchorPoint mVerticalAnchorPoint
Vertical anchor point.
QgsUnitTypes::RenderUnit mSizeUnit
Marker size unit.
void setOffsetUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol's offset.
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol's offset.
double mAngle
Marker rotation angle, in degrees clockwise from north.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
double angle() const
Returns the rotation angle for the marker, in degrees clockwise from north.
void setMapUnitScale(const QgsMapUnitScale &scale) override
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
QgsProperty property(int key) const override
Returns a matching property from the collection, if one exists.
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.
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
A store for object properties.
Definition: qgsproperty.h:231
QString asExpression() const
Returns an expression string representing the state of the property, or an empty string if the proper...
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...
bool isActive() const
Returns whether the property is currently active.
QPainter * painter()
Returns the destination QPainter for the render operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QColor selectionColor() const
Returns the color to use when rendering selected features.
static bool rotationFromSldElement(QDomElement &element, QString &rotationFunc)
static QString encodePenStyle(Qt::PenStyle style)
static Qt::PenJoinStyle decodePenJoinStyle(const QString &str)
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
static QColor decodeColor(const QString &str)
static double rescaleUom(double size, QgsUnitTypes::RenderUnit unit, const QVariantMap &props)
Rescales the given size based on the uomScale found in the props, if any is found,...
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
static Qt::PenCapStyle decodePenCapStyle(const QString &str)
static QgsUnitTypes::RenderUnit decodeSldUom(const QString &str, double *scaleFactor=nullptr)
Decodes a SLD unit of measure string to a render unit.
static QString encodePenCapStyle(Qt::PenCapStyle style)
static QDomElement createVendorOptionElement(QDomDocument &doc, const QString &name, const QString &value)
static bool wellKnownMarkerFromSld(QDomElement &element, QString &name, QColor &color, QColor &strokeColor, Qt::PenStyle &strokeStyle, double &strokeWidth, double &size)
static void createDisplacementElement(QDomDocument &doc, QDomElement &element, QPointF offset)
static QString encodeColor(const QColor &color)
static void createGeometryElement(QDomDocument &doc, QDomElement &element, const QString &geomFunc)
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)
static Qt::PenStyle decodePenStyle(const QString &str)
static void createRotationElement(QDomDocument &doc, QDomElement &element, const QString &rotationFunc)
static QString encodePoint(QPointF point)
Encodes a QPointF to a string.
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
static QgsStringMap getVendorOptionList(QDomElement &element)
static QPointF decodePoint(const QString &string)
Decodes a QSizeF from a string.
@ PropertyStrokeStyle
Stroke style (eg solid, dashed)
@ PropertyCapStyle
Line cap style.
@ PropertyAngle
Symbol angle.
@ PropertyJoinStyle
Line join style.
@ PropertyStrokeWidth
Stroke width.
@ PropertyFillColor
Fill color.
@ PropertyHeight
Symbol height.
@ PropertyName
Name, eg shape name for simple markers.
@ PropertyStrokeColor
Stroke color.
@ PropertyWidth
Symbol width.
static const bool SELECTION_IS_OPAQUE
Whether styles for selected features ignore symbol alpha.
void copyDataDefinedProperties(QgsSymbolLayer *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
void restoreOldDataDefinedProperties(const QVariantMap &stringMap)
Restores older data defined properties from string map.
virtual void setColor(const QColor &color)
Sets the "representative" color for the symbol layer.
virtual QColor color() const
Returns the "representative" color of the symbol layer.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the symbol layer's property collection, used for data defined overrides.
void copyPaintEffect(QgsSymbolLayer *destLayer) const
Copies paint effect of this layer to another symbol layer.
QgsPropertyCollection mDataDefinedProperties
bool selected() const
Returns true if symbols should be rendered using the selected symbol coloring and style.
Qgis::SymbolRenderHints renderHints() const
Returns the rendering hint flags for the symbol.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
const QgsFeature * feature() const
Returns the current feature being rendered.
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for data defined symbology.
qreal opacity() const
Returns the opacity for the symbol.
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
static Q_INVOKABLE QgsUnitTypes::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:168
@ RenderUnknownUnit
Mixed or unknown units.
Definition: qgsunittypes.h:175
@ RenderMetersInMapUnits
Meters value as Map units.
Definition: qgsunittypes.h:176
@ RenderMillimeters
Millimeters.
Definition: qgsunittypes.h:169
@ RenderMapUnits
Map units.
Definition: qgsunittypes.h:170
static bool isNull(const QVariant &variant)
Returns true if the specified variant should be considered a NULL value.
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2527
QMap< QString, QString > QgsStringMap
Definition: qgis.h:3022
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
#define DEG2RAD(x)
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39