QGIS API Documentation  3.21.0-Master (5b68dc587e)
qgsmarkersymbollayer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmarkersymbollayer.cpp
3  ---------------------
4  begin : November 2009
5  copyright : (C) 2009 by Martin Dobias
6  email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgsmarkersymbollayer.h"
17 #include "qgssymbollayerutils.h"
18 
19 #include "qgsdxfexport.h"
20 #include "qgsdxfpaintdevice.h"
21 #include "qgsexpression.h"
22 #include "qgsfontutils.h"
23 #include "qgsimagecache.h"
24 #include "qgsimageoperation.h"
25 #include "qgsrendercontext.h"
26 #include "qgslogger.h"
27 #include "qgssvgcache.h"
28 #include "qgsunittypes.h"
29 #include "qgssymbol.h"
30 #include "qgsfillsymbol.h"
31 
32 #include <QPainter>
33 #include <QSvgRenderer>
34 #include <QFileInfo>
35 #include <QDir>
36 #include <QDomDocument>
37 #include <QDomElement>
38 
39 #include <cmath>
40 
41 Q_GUI_EXPORT extern int qt_defaultDpiX();
42 Q_GUI_EXPORT extern int qt_defaultDpiY();
43 
44 static constexpr int MAX_FONT_CHARACTER_SIZE_IN_PIXELS = 500;
45 
46 static void _fixQPictureDPI( QPainter *p )
47 {
48  // QPicture makes an assumption that we drawing to it with system DPI.
49  // Then when being drawn, it scales the painter. The following call
50  // negates the effect. There is no way of setting QPicture's DPI.
51  // See QTBUG-20361
52  p->scale( static_cast< double >( qt_defaultDpiX() ) / p->device()->logicalDpiX(),
53  static_cast< double >( qt_defaultDpiY() ) / p->device()->logicalDpiY() );
54 }
55 
56 
58 
59 
60 //
61 // QgsSimpleMarkerSymbolLayerBase
62 //
63 
64 QList<QgsSimpleMarkerSymbolLayerBase::Shape> QgsSimpleMarkerSymbolLayerBase::availableShapes()
65 {
66  QList< Shape > shapes;
67  shapes << Square
68  << Diamond
69  << Pentagon
70  << Hexagon
71  << Octagon
73  << Triangle
75  << Star
76  << Arrow
77  << Circle
78  << Cross
79  << CrossFill
80  << Cross2
81  << Line
82  << HalfArc
83  << ThirdArc
84  << QuarterArc
85  << ArrowHead
87  << SemiCircle
88  << ThirdCircle
89  << QuarterCircle
90  << QuarterSquare
91  << HalfSquare
95  << AsteriskFill;
96 
97  return shapes;
98 }
99 
101  : mShape( shape )
102 {
103  mSize = size;
104  mAngle = angle;
105  mOffset = QPointF( 0, 0 );
109 }
110 
112 
114 {
115  switch ( shape )
116  {
117  case Square:
118  case Diamond:
119  case Pentagon:
120  case Hexagon:
121  case Octagon:
122  case SquareWithCorners:
123  case Triangle:
124  case EquilateralTriangle:
125  case Star:
126  case Arrow:
127  case Circle:
128  case CrossFill:
129  case ArrowHeadFilled:
130  case SemiCircle:
131  case ThirdCircle:
132  case QuarterCircle:
133  case QuarterSquare:
134  case HalfSquare:
135  case DiagonalHalfSquare:
136  case RightHalfTriangle:
137  case LeftHalfTriangle:
138  case AsteriskFill:
139  return true;
140 
141  case Cross:
142  case Cross2:
143  case Line:
144  case ArrowHead:
145  case HalfArc:
146  case ThirdArc:
147  case QuarterArc:
148  return false;
149  }
150  return true;
151 }
152 
154 {
155  const bool hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation
157  const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
158 
159  // use either QPolygonF or QPainterPath for drawing
160  if ( !prepareMarkerShape( mShape ) ) // drawing as a polygon
161  {
162  prepareMarkerPath( mShape ); // drawing as a painter path
163  }
164 
165  QTransform transform;
166 
167  // scale the shape (if the size is not going to be modified)
168  if ( !hasDataDefinedSize )
169  {
170  double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
172  {
173  // rendering for symbol previews -- an size in meters in map units can't be calculated, so treat the size as millimeters
174  // and clamp it to a reasonable range. It's the best we can do in this situation!
175  scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, QgsUnitTypes::RenderMillimeters ), 3.0 ), 100.0 );
176  }
177 
178  const double half = scaledSize / 2.0;
179  transform.scale( half, half );
180  }
181 
182  // rotate if the rotation is not going to be changed during the rendering
183  if ( !hasDataDefinedRotation && !qgsDoubleNear( mAngle, 0.0 ) )
184  {
185  transform.rotate( mAngle );
186  }
187 
188  if ( !mPolygon.isEmpty() )
189  mPolygon = transform.map( mPolygon );
190  else
191  mPath = transform.map( mPath );
192 
194 }
195 
197 {
198  Q_UNUSED( context )
199 }
200 
202 {
203  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
204  //of the rendered point!
205 
206  QPainter *p = context.renderContext().painter();
207  if ( !p )
208  {
209  return;
210  }
211 
212  bool hasDataDefinedSize = false;
213  const double scaledSize = calculateSize( context, hasDataDefinedSize );
214 
215  bool hasDataDefinedRotation = false;
216  QPointF offset;
217  double angle = 0;
218  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
219 
220  //data defined shape?
221  bool createdNewPath = false;
222  bool ok = true;
223  Shape symbol = mShape;
225  {
226  context.setOriginalValueVariable( encodeShape( symbol ) );
228  if ( !exprVal.isNull() )
229  {
230  const Shape decoded = decodeShape( exprVal.toString(), &ok );
231  if ( ok )
232  {
233  symbol = decoded;
234 
235  if ( !prepareMarkerShape( symbol ) ) // drawing as a polygon
236  {
237  prepareMarkerPath( symbol ); // drawing as a painter path
238  }
239  createdNewPath = true;
240  }
241  }
242  else
243  {
244  symbol = mShape;
245  }
246  }
247 
248  QTransform transform;
249 
250  // move to the desired position
251  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
252 
253  // resize if necessary
254  if ( hasDataDefinedSize || createdNewPath )
255  {
256  double s = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
258  {
259  // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
260  // and clamp it to a reasonable range. It's the best we can do in this situation!
261  s = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, QgsUnitTypes::RenderMillimeters ), 3.0 ), 100.0 );
262  }
263  const double half = s / 2.0;
264  transform.scale( half, half );
265  }
266 
267  if ( !qgsDoubleNear( angle, 0.0 ) && ( hasDataDefinedRotation || createdNewPath ) )
268  {
269  transform.rotate( angle );
270  }
271 
272  //need to pass: symbol, polygon, path
273 
274  QPolygonF polygon;
275  QPainterPath path;
276  if ( !mPolygon.isEmpty() )
277  {
278  polygon = transform.map( mPolygon );
279  }
280  else
281  {
282  path = transform.map( mPath );
283  }
284  draw( context, symbol, polygon, path );
285 }
286 
288 {
289  bool hasDataDefinedSize = false;
290  double scaledSize = calculateSize( context, hasDataDefinedSize );
291 
292  bool hasDataDefinedRotation = false;
293  QPointF offset;
294  double angle = 0;
295  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
296 
297  scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
298 
299  QTransform transform;
300 
301  // move to the desired position
302  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
303 
304  if ( !qgsDoubleNear( angle, 0.0 ) )
305  transform.rotate( angle );
306 
307  return transform.mapRect( QRectF( -scaledSize / 2.0,
308  -scaledSize / 2.0,
309  scaledSize,
310  scaledSize ) );
311 }
312 
314 {
315  if ( ok )
316  *ok = true;
317  const QString cleaned = name.toLower().trimmed();
318 
319  if ( cleaned == QLatin1String( "square" ) || cleaned == QLatin1String( "rectangle" ) )
320  return Square;
321  else if ( cleaned == QLatin1String( "square_with_corners" ) )
322  return SquareWithCorners;
323  else if ( cleaned == QLatin1String( "diamond" ) )
324  return Diamond;
325  else if ( cleaned == QLatin1String( "pentagon" ) )
326  return Pentagon;
327  else if ( cleaned == QLatin1String( "hexagon" ) )
328  return Hexagon;
329  else if ( cleaned == QLatin1String( "octagon" ) )
330  return Octagon;
331  else if ( cleaned == QLatin1String( "triangle" ) )
332  return Triangle;
333  else if ( cleaned == QLatin1String( "equilateral_triangle" ) )
334  return EquilateralTriangle;
335  else if ( cleaned == QLatin1String( "star" ) || cleaned == QLatin1String( "regular_star" ) )
336  return Star;
337  else if ( cleaned == QLatin1String( "arrow" ) )
338  return Arrow;
339  else if ( cleaned == QLatin1String( "circle" ) )
340  return Circle;
341  else if ( cleaned == QLatin1String( "cross" ) )
342  return Cross;
343  else if ( cleaned == QLatin1String( "cross_fill" ) )
344  return CrossFill;
345  else if ( cleaned == QLatin1String( "cross2" ) || cleaned == QLatin1String( "x" ) )
346  return Cross2;
347  else if ( cleaned == QLatin1String( "line" ) )
348  return Line;
349  else if ( cleaned == QLatin1String( "arrowhead" ) )
350  return ArrowHead;
351  else if ( cleaned == QLatin1String( "filled_arrowhead" ) )
352  return ArrowHeadFilled;
353  else if ( cleaned == QLatin1String( "semi_circle" ) )
354  return SemiCircle;
355  else if ( cleaned == QLatin1String( "third_circle" ) )
356  return ThirdCircle;
357  else if ( cleaned == QLatin1String( "quarter_circle" ) )
358  return QuarterCircle;
359  else if ( cleaned == QLatin1String( "quarter_square" ) )
360  return QuarterSquare;
361  else if ( cleaned == QLatin1String( "half_square" ) )
362  return HalfSquare;
363  else if ( cleaned == QLatin1String( "diagonal_half_square" ) )
364  return DiagonalHalfSquare;
365  else if ( cleaned == QLatin1String( "right_half_triangle" ) )
366  return RightHalfTriangle;
367  else if ( cleaned == QLatin1String( "left_half_triangle" ) )
368  return LeftHalfTriangle;
369  else if ( cleaned == QLatin1String( "asterisk_fill" ) )
370  return AsteriskFill;
371  else if ( cleaned == QLatin1String( "half_arc" ) )
372  return HalfArc;
373  else if ( cleaned == QLatin1String( "third_arc" ) )
374  return ThirdArc;
375  else if ( cleaned == QLatin1String( "quarter_arc" ) )
376  return QuarterArc;
377 
378  if ( ok )
379  *ok = false;
380  return Circle;
381 }
382 
384 {
385  switch ( shape )
386  {
387  case Square:
388  return QStringLiteral( "square" );
389  case QuarterSquare:
390  return QStringLiteral( "quarter_square" );
391  case HalfSquare:
392  return QStringLiteral( "half_square" );
393  case DiagonalHalfSquare:
394  return QStringLiteral( "diagonal_half_square" );
395  case Diamond:
396  return QStringLiteral( "diamond" );
397  case Pentagon:
398  return QStringLiteral( "pentagon" );
399  case Hexagon:
400  return QStringLiteral( "hexagon" );
401  case Octagon:
402  return QStringLiteral( "octagon" );
403  case SquareWithCorners:
404  return QStringLiteral( "square_with_corners" );
405  case Triangle:
406  return QStringLiteral( "triangle" );
407  case EquilateralTriangle:
408  return QStringLiteral( "equilateral_triangle" );
409  case LeftHalfTriangle:
410  return QStringLiteral( "left_half_triangle" );
411  case RightHalfTriangle:
412  return QStringLiteral( "right_half_triangle" );
413  case Star:
414  return QStringLiteral( "star" );
415  case Arrow:
416  return QStringLiteral( "arrow" );
417  case ArrowHeadFilled:
418  return QStringLiteral( "filled_arrowhead" );
419  case CrossFill:
420  return QStringLiteral( "cross_fill" );
421  case Circle:
422  return QStringLiteral( "circle" );
423  case Cross:
424  return QStringLiteral( "cross" );
425  case Cross2:
426  return QStringLiteral( "cross2" );
427  case Line:
428  return QStringLiteral( "line" );
429  case ArrowHead:
430  return QStringLiteral( "arrowhead" );
431  case SemiCircle:
432  return QStringLiteral( "semi_circle" );
433  case ThirdCircle:
434  return QStringLiteral( "third_circle" );
435  case QuarterCircle:
436  return QStringLiteral( "quarter_circle" );
437  case AsteriskFill:
438  return QStringLiteral( "asterisk_fill" );
439  case HalfArc:
440  return QStringLiteral( "half_arc" );
441  case ThirdArc:
442  return QStringLiteral( "third_arc" );
443  case QuarterArc:
444  return QStringLiteral( "quarter_arc" );
445  }
446  return QString();
447 }
448 
450 {
451  return shapeToPolygon( shape, mPolygon );
452 }
453 
455 {
456  polygon.clear();
457 
458  switch ( shape )
459  {
460  case Square:
461  polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 1, 1 ) ) );
462  return true;
463 
464  case SquareWithCorners:
465  {
466  static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 0.6072;
467 
468  polygon << QPointF( - VERTEX_OFFSET_FROM_ORIGIN, 1 )
469  << QPointF( VERTEX_OFFSET_FROM_ORIGIN, 1 )
470  << QPointF( 1, VERTEX_OFFSET_FROM_ORIGIN )
471  << QPointF( 1, -VERTEX_OFFSET_FROM_ORIGIN )
472  << QPointF( VERTEX_OFFSET_FROM_ORIGIN, -1 )
473  << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, -1 )
474  << QPointF( -1, -VERTEX_OFFSET_FROM_ORIGIN )
475  << QPointF( -1, VERTEX_OFFSET_FROM_ORIGIN )
476  << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 );
477  return true;
478  }
479 
480  case QuarterSquare:
481  polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 0 ) ) );
482  return true;
483 
484  case HalfSquare:
485  polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 1 ) ) );
486  return true;
487 
488  case DiagonalHalfSquare:
489  polygon << QPointF( -1, -1 ) << QPointF( 1, 1 ) << QPointF( -1, 1 ) << QPointF( -1, -1 );
490  return true;
491 
492  case Diamond:
493  polygon << QPointF( -1, 0 ) << QPointF( 0, 1 )
494  << QPointF( 1, 0 ) << QPointF( 0, -1 ) << QPointF( -1, 0 );
495  return true;
496 
497  case Pentagon:
498  /* angular-representation of hardcoded values used
499  polygon << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288.0 ) ) )
500  << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) )
501  << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) )
502  << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) )
503  << QPointF( 0, -1 ); */
504  polygon << QPointF( -0.9511, -0.3090 )
505  << QPointF( -0.5878, 0.8090 )
506  << QPointF( 0.5878, 0.8090 )
507  << QPointF( 0.9511, -0.3090 )
508  << QPointF( 0, -1 )
509  << QPointF( -0.9511, -0.3090 );
510  return true;
511 
512  case Hexagon:
513  /* angular-representation of hardcoded values used
514  polygon << QPointF( std::sin( DEG2RAD( 300.0 ) ), - std::cos( DEG2RAD( 300.0 ) ) )
515  << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
516  << QPointF( std::sin( DEG2RAD( 180.0 ) ), - std::cos( DEG2RAD( 180.0 ) ) )
517  << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
518  << QPointF( std::sin( DEG2RAD( 60.0 ) ), - std::cos( DEG2RAD( 60.0 ) ) )
519  << QPointF( 0, -1 ); */
520  polygon << QPointF( -0.8660, -0.5 )
521  << QPointF( -0.8660, 0.5 )
522  << QPointF( 0, 1 )
523  << QPointF( 0.8660, 0.5 )
524  << QPointF( 0.8660, -0.5 )
525  << QPointF( 0, -1 )
526  << QPointF( -0.8660, -0.5 );
527  return true;
528 
529  case Octagon:
530  {
531  static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 1.0 / ( 1 + M_SQRT2 );
532 
533  polygon << QPointF( - VERTEX_OFFSET_FROM_ORIGIN, 1 )
534  << QPointF( VERTEX_OFFSET_FROM_ORIGIN, 1 )
535  << QPointF( 1, VERTEX_OFFSET_FROM_ORIGIN )
536  << QPointF( 1, -VERTEX_OFFSET_FROM_ORIGIN )
537  << QPointF( VERTEX_OFFSET_FROM_ORIGIN, -1 )
538  << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, -1 )
539  << QPointF( -1, -VERTEX_OFFSET_FROM_ORIGIN )
540  << QPointF( -1, VERTEX_OFFSET_FROM_ORIGIN )
541  << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 );
542  return true;
543  }
544 
545  case Triangle:
546  polygon << QPointF( -1, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
547  return true;
548 
549  case EquilateralTriangle:
550  /* angular-representation of hardcoded values used
551  polygon << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
552  << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
553  << QPointF( 0, -1 ); */
554  polygon << QPointF( -0.8660, 0.5 )
555  << QPointF( 0.8660, 0.5 )
556  << QPointF( 0, -1 )
557  << QPointF( -0.8660, 0.5 );
558  return true;
559 
560  case LeftHalfTriangle:
561  polygon << QPointF( 0, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( 0, 1 );
562  return true;
563 
564  case RightHalfTriangle:
565  polygon << QPointF( -1, 1 ) << QPointF( 0, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
566  return true;
567 
568  case Star:
569  {
570  const double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
571 
572  polygon << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ) // 324
573  << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288 ) ) ) // 288
574  << QPointF( inner_r * std::sin( DEG2RAD( 252.0 ) ), - inner_r * std::cos( DEG2RAD( 252.0 ) ) ) // 252
575  << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) ) // 216
576  << QPointF( 0, inner_r ) // 180
577  << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) ) // 144
578  << QPointF( inner_r * std::sin( DEG2RAD( 108.0 ) ), - inner_r * std::cos( DEG2RAD( 108.0 ) ) ) // 108
579  << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) ) // 72
580  << QPointF( inner_r * std::sin( DEG2RAD( 36.0 ) ), - inner_r * std::cos( DEG2RAD( 36.0 ) ) ) // 36
581  << QPointF( 0, -1 )
582  << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ); // 324; // 0
583  return true;
584  }
585 
586  case Arrow:
587  polygon << QPointF( 0, -1 )
588  << QPointF( 0.5, -0.5 )
589  << QPointF( 0.25, -0.5 )
590  << QPointF( 0.25, 1 )
591  << QPointF( -0.25, 1 )
592  << QPointF( -0.25, -0.5 )
593  << QPointF( -0.5, -0.5 )
594  << QPointF( 0, -1 );
595  return true;
596 
597  case ArrowHeadFilled:
598  polygon << QPointF( 0, 0 ) << QPointF( -1, 1 ) << QPointF( -1, -1 ) << QPointF( 0, 0 );
599  return true;
600 
601  case CrossFill:
602  polygon << QPointF( -1, -0.2 )
603  << QPointF( -1, -0.2 )
604  << QPointF( -1, 0.2 )
605  << QPointF( -0.2, 0.2 )
606  << QPointF( -0.2, 1 )
607  << QPointF( 0.2, 1 )
608  << QPointF( 0.2, 0.2 )
609  << QPointF( 1, 0.2 )
610  << QPointF( 1, -0.2 )
611  << QPointF( 0.2, -0.2 )
612  << QPointF( 0.2, -1 )
613  << QPointF( -0.2, -1 )
614  << QPointF( -0.2, -0.2 )
615  << QPointF( -1, -0.2 );
616  return true;
617 
618  case AsteriskFill:
619  {
620  static constexpr double THICKNESS = 0.3;
621  static constexpr double HALF_THICKNESS = THICKNESS / 2.0;
622  static constexpr double INTERSECTION_POINT = THICKNESS / M_SQRT2;
623  static constexpr double DIAGONAL1 = M_SQRT1_2 - INTERSECTION_POINT * 0.5;
624  static constexpr double DIAGONAL2 = M_SQRT1_2 + INTERSECTION_POINT * 0.5;
625 
626  polygon << QPointF( -HALF_THICKNESS, -1 )
627  << QPointF( HALF_THICKNESS, -1 )
628  << QPointF( HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
629  << QPointF( DIAGONAL1, -DIAGONAL2 )
630  << QPointF( DIAGONAL2, -DIAGONAL1 )
631  << QPointF( HALF_THICKNESS + INTERSECTION_POINT, -HALF_THICKNESS )
632  << QPointF( 1, -HALF_THICKNESS )
633  << QPointF( 1, HALF_THICKNESS )
634  << QPointF( HALF_THICKNESS + INTERSECTION_POINT, HALF_THICKNESS )
635  << QPointF( DIAGONAL2, DIAGONAL1 )
636  << QPointF( DIAGONAL1, DIAGONAL2 )
637  << QPointF( HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
638  << QPointF( HALF_THICKNESS, 1 )
639  << QPointF( -HALF_THICKNESS, 1 )
640  << QPointF( -HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
641  << QPointF( -DIAGONAL1, DIAGONAL2 )
642  << QPointF( -DIAGONAL2, DIAGONAL1 )
643  << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, HALF_THICKNESS )
644  << QPointF( -1, HALF_THICKNESS )
645  << QPointF( -1, -HALF_THICKNESS )
646  << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, -HALF_THICKNESS )
647  << QPointF( -DIAGONAL2, -DIAGONAL1 )
648  << QPointF( -DIAGONAL1, -DIAGONAL2 )
649  << QPointF( -HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
650  << QPointF( -HALF_THICKNESS, -1 );
651  return true;
652  }
653 
654  case Circle:
655  case Cross:
656  case Cross2:
657  case Line:
658  case ArrowHead:
659  case SemiCircle:
660  case ThirdCircle:
661  case QuarterCircle:
662  case HalfArc:
663  case ThirdArc:
664  case QuarterArc:
665  return false;
666  }
667 
668  return false;
669 }
670 
672 {
673  mPath = QPainterPath();
674 
675  switch ( symbol )
676  {
677  case Circle:
678 
679  mPath.addEllipse( QRectF( -1, -1, 2, 2 ) ); // x,y,w,h
680  return true;
681 
682  case SemiCircle:
683  mPath.arcTo( -1, -1, 2, 2, 0, 180 );
684  mPath.lineTo( 0, 0 );
685  return true;
686 
687  case ThirdCircle:
688  mPath.arcTo( -1, -1, 2, 2, 90, 120 );
689  mPath.lineTo( 0, 0 );
690  return true;
691 
692  case QuarterCircle:
693  mPath.arcTo( -1, -1, 2, 2, 90, 90 );
694  mPath.lineTo( 0, 0 );
695  return true;
696 
697  case HalfArc:
698  mPath.moveTo( 1, 0 );
699  mPath.arcTo( -1, -1, 2, 2, 0, 180 );
700  return true;
701 
702  case ThirdArc:
703  mPath.moveTo( 0, -1 );
704  mPath.arcTo( -1, -1, 2, 2, 90, 120 );
705  return true;
706 
707  case QuarterArc:
708  mPath.moveTo( 0, -1 );
709  mPath.arcTo( -1, -1, 2, 2, 90, 90 );
710  return true;
711 
712  case Cross:
713  mPath.moveTo( -1, 0 );
714  mPath.lineTo( 1, 0 ); // horizontal
715  mPath.moveTo( 0, -1 );
716  mPath.lineTo( 0, 1 ); // vertical
717  return true;
718 
719  case Cross2:
720  mPath.moveTo( -1, -1 );
721  mPath.lineTo( 1, 1 );
722  mPath.moveTo( 1, -1 );
723  mPath.lineTo( -1, 1 );
724  return true;
725 
726  case Line:
727  mPath.moveTo( 0, -1 );
728  mPath.lineTo( 0, 1 ); // vertical line
729  return true;
730 
731  case ArrowHead:
732  mPath.moveTo( -1, -1 );
733  mPath.lineTo( 0, 0 );
734  mPath.lineTo( -1, 1 );
735  return true;
736 
737  case Square:
738  case SquareWithCorners:
739  case QuarterSquare:
740  case HalfSquare:
741  case DiagonalHalfSquare:
742  case Diamond:
743  case Pentagon:
744  case Hexagon:
745  case Octagon:
746  case Triangle:
747  case EquilateralTriangle:
748  case LeftHalfTriangle:
749  case RightHalfTriangle:
750  case Star:
751  case Arrow:
752  case ArrowHeadFilled:
753  case CrossFill:
754  case AsteriskFill:
755  return false;
756  }
757  return false;
758 }
759 
760 double QgsSimpleMarkerSymbolLayerBase::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
761 {
762  double scaledSize = mSize;
763 
765  bool ok = true;
766  if ( hasDataDefinedSize )
767  {
768  context.setOriginalValueVariable( mSize );
770  mSize, &ok );
771  }
772 
773  if ( hasDataDefinedSize && ok )
774  {
775  switch ( mScaleMethod )
776  {
778  scaledSize = std::sqrt( scaledSize );
779  break;
781  break;
782  }
783  }
784 
785  return scaledSize;
786 }
787 
788 void QgsSimpleMarkerSymbolLayerBase::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle ) const
789 {
790  //offset
791  double offsetX = 0;
792  double offsetY = 0;
793  markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
794  offset = QPointF( offsetX, offsetY );
795 
796  hasDataDefinedRotation = false;
797  //angle
798  bool ok = true;
799  angle = mAngle + mLineAngle;
801  {
802  context.setOriginalValueVariable( angle );
804 
805  // If the expression evaluation was not successful, fallback to static value
806  if ( !ok )
807  angle = mAngle + mLineAngle;
808 
809  hasDataDefinedRotation = true;
810  }
811 
812  hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || hasDataDefinedRotation;
813 
814  if ( hasDataDefinedRotation )
815  {
816  // For non-point markers, "dataDefinedRotation" means following the
817  // shape (shape-data defined). For them, "field-data defined" does
818  // not work at all. TODO: if "field-data defined" ever gets implemented
819  // we'll need a way to distinguish here between the two, possibly
820  // using another flag in renderHints()
821  const QgsFeature *f = context.feature();
822  if ( f )
823  {
824  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
825  {
826  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
827  angle += m2p.mapRotation();
828  }
829  }
830  }
831 
832  if ( angle )
834 }
835 
836 
837 //
838 // QgsSimpleMarkerSymbolLayer
839 //
840 
841 QgsSimpleMarkerSymbolLayer::QgsSimpleMarkerSymbolLayer( QgsSimpleMarkerSymbolLayerBase::Shape shape, double size, double angle, Qgis::ScaleMethod scaleMethod, const QColor &color, const QColor &strokeColor, Qt::PenJoinStyle penJoinStyle )
842  : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
843  , mStrokeColor( strokeColor )
844  , mPenJoinStyle( penJoinStyle )
845 {
846  mColor = color;
847 }
848 
850 
852 {
853  Shape shape = Circle;
856  Qt::PenJoinStyle penJoinStyle = DEFAULT_SIMPLEMARKER_JOINSTYLE;
860 
861  if ( props.contains( QStringLiteral( "name" ) ) )
862  {
863  shape = decodeShape( props[QStringLiteral( "name" )].toString() );
864  }
865  if ( props.contains( QStringLiteral( "color" ) ) )
866  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )].toString() );
867  if ( props.contains( QStringLiteral( "color_border" ) ) )
868  {
869  //pre 2.5 projects use "color_border"
870  strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color_border" )].toString() );
871  }
872  else if ( props.contains( QStringLiteral( "outline_color" ) ) )
873  {
874  strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )].toString() );
875  }
876  else if ( props.contains( QStringLiteral( "line_color" ) ) )
877  {
878  strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )].toString() );
879  }
880  if ( props.contains( QStringLiteral( "joinstyle" ) ) )
881  {
882  penJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() );
883  }
884  if ( props.contains( QStringLiteral( "size" ) ) )
885  size = props[QStringLiteral( "size" )].toDouble();
886  if ( props.contains( QStringLiteral( "angle" ) ) )
887  angle = props[QStringLiteral( "angle" )].toDouble();
888  if ( props.contains( QStringLiteral( "scale_method" ) ) )
889  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
890 
892  if ( props.contains( QStringLiteral( "offset" ) ) )
893  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
894  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
895  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
896  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
897  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
898  if ( props.contains( QStringLiteral( "size_unit" ) ) )
899  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
900  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
901  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
902 
903  if ( props.contains( QStringLiteral( "outline_style" ) ) )
904  {
905  m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "outline_style" )].toString() ) );
906  }
907  else if ( props.contains( QStringLiteral( "line_style" ) ) )
908  {
909  m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "line_style" )].toString() ) );
910  }
911  if ( props.contains( QStringLiteral( "outline_width" ) ) )
912  {
913  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
914  }
915  else if ( props.contains( QStringLiteral( "line_width" ) ) )
916  {
917  m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
918  }
919  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
920  {
921  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
922  }
923  if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
924  {
925  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
926  }
927  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
928  {
929  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
930  }
931 
932  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
933  {
934  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
935  }
936  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
937  {
938  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
939  }
940 
941  if ( props.contains( QStringLiteral( "cap_style" ) ) )
942  {
943  m->setPenCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( props[QStringLiteral( "cap_style" )].toString() ) );
944  }
945 
947 
948  return m;
949 }
950 
951 
953 {
954  return QStringLiteral( "SimpleMarker" );
955 }
956 
958 {
960 
961  QColor brushColor = mColor;
962  QColor penColor = mStrokeColor;
963 
964  brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
965  penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
966 
967  mBrush = QBrush( brushColor );
968  mPen = QPen( penColor );
969  mPen.setStyle( mStrokeStyle );
970  mPen.setCapStyle( mPenCapStyle );
971  mPen.setJoinStyle( mPenJoinStyle );
973 
974  QColor selBrushColor = context.renderContext().selectionColor();
975  QColor selPenColor = selBrushColor == mColor ? selBrushColor : mStrokeColor;
976  if ( context.opacity() < 1 && !SELECTION_IS_OPAQUE )
977  {
978  selBrushColor.setAlphaF( context.opacity() );
979  selPenColor.setAlphaF( context.opacity() );
980  }
981  mSelBrush = QBrush( selBrushColor );
982  mSelPen = QPen( selPenColor );
983  mSelPen.setStyle( mStrokeStyle );
985 
987  const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
988 
989  // use caching only when:
990  // - size, rotation, shape, color, stroke color is not data-defined
991  // - drawing to screen (not printer)
992  mUsingCache = !hasDataDefinedRotation && !hasDataDefinedSize && !context.renderContext().forceVectorOutput()
996 
997  if ( mUsingCache )
998  mCachedOpacity = context.opacity();
999 
1000  if ( !shapeIsFilled( mShape ) )
1001  {
1002  // some markers can't be drawn as a polygon (circle, cross)
1003  // For these set the selected stroke color to the selected color
1004  mSelPen.setColor( selBrushColor );
1005  }
1006 
1007 
1008  if ( mUsingCache )
1009  {
1010  if ( !prepareCache( context ) )
1011  {
1012  mUsingCache = false;
1013  }
1014  }
1015  else
1016  {
1017  mCache = QImage();
1018  mSelCache = QImage();
1019  }
1020 }
1021 
1022 
1024 {
1025  double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
1027  {
1028  // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
1029  // and clamp it to a reasonable range. It's the best we can do in this situation!
1030  scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, QgsUnitTypes::RenderMillimeters ), 3.0 ), 100.0 );
1031  }
1032 
1033  // take into account angle (which is not data-defined otherwise cache wouldn't be used)
1034  if ( !qgsDoubleNear( mAngle, 0.0 ) )
1035  {
1036  scaledSize = ( std::abs( std::sin( mAngle * M_PI / 180 ) ) + std::abs( std::cos( mAngle * M_PI / 180 ) ) ) * scaledSize;
1037  }
1038  // calculate necessary image size for the cache
1039  const double pw = static_cast< int >( std::round( ( ( qgsDoubleNear( mPen.widthF(), 0.0 ) ? 1 : mPen.widthF() * 4 ) + 1 ) ) ) / 2 * 2; // make even (round up); handle cosmetic pen
1040  const int imageSize = ( static_cast< int >( scaledSize ) + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width
1041  const double center = imageSize / 2.0;
1042  if ( imageSize > MAXIMUM_CACHE_WIDTH )
1043  {
1044  return false;
1045  }
1046 
1047  mCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
1048  mCache.fill( 0 );
1049 
1050  const bool needsBrush = shapeIsFilled( mShape );
1051 
1052  QPainter p;
1053  p.begin( &mCache );
1054  p.setRenderHint( QPainter::Antialiasing );
1055  p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1056  p.setPen( mPen );
1057  p.translate( QPointF( center, center ) );
1058  drawMarker( &p, context );
1059  p.end();
1060 
1061  // Construct the selected version of the Cache
1062 
1063  const QColor selColor = context.renderContext().selectionColor();
1064 
1065  mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
1066  mSelCache.fill( 0 );
1067 
1068  p.begin( &mSelCache );
1069  p.setRenderHint( QPainter::Antialiasing );
1070  p.setBrush( needsBrush ? mSelBrush : Qt::NoBrush );
1071  p.setPen( mSelPen );
1072  p.translate( QPointF( center, center ) );
1073  drawMarker( &p, context );
1074  p.end();
1075 
1076  // Check that the selected version is different. If not, then re-render,
1077  // filling the background with the selection color and using the normal
1078  // colors for the symbol .. could be ugly!
1079 
1080  if ( mSelCache == mCache )
1081  {
1082  p.begin( &mSelCache );
1083  p.setRenderHint( QPainter::Antialiasing );
1084  p.fillRect( 0, 0, imageSize, imageSize, selColor );
1085  p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1086  p.setPen( mPen );
1087  p.translate( QPointF( center, center ) );
1088  drawMarker( &p, context );
1089  p.end();
1090  }
1091 
1092  return true;
1093 }
1094 
1095 void QgsSimpleMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, QgsSimpleMarkerSymbolLayerBase::Shape shape, const QPolygonF &polygon, const QPainterPath &path )
1096 {
1097  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1098  //of the rendered point!
1099 
1100  QPainter *p = context.renderContext().painter();
1101  if ( !p )
1102  {
1103  return;
1104  }
1105 
1106  QColor brushColor = mColor;
1107  brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
1108  mBrush.setColor( brushColor );
1109 
1110  QColor penColor = mStrokeColor;
1111  penColor.setAlphaF( penColor.alphaF() * context.opacity() );
1112  mPen.setColor( penColor );
1113 
1114  bool ok = true;
1116  {
1119  if ( ok )
1120  {
1121  c.setAlphaF( c.alphaF() * context.opacity() );
1122  mBrush.setColor( c );
1123  }
1124  }
1126  {
1129  if ( ok )
1130  {
1131  c.setAlphaF( c.alphaF() * context.opacity() );
1132  mPen.setColor( c );
1133  mSelPen.setColor( c );
1134  }
1135  }
1137  {
1140  if ( ok )
1141  {
1144  }
1145  }
1147  {
1150  if ( ok )
1151  {
1154  }
1155  }
1157  {
1159  const QString style = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyJoinStyle, context.renderContext().expressionContext(), QString(), &ok );
1160  if ( ok )
1161  {
1162  mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1163  mSelPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1164  }
1165  }
1167  {
1169  const QString style = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyCapStyle, context.renderContext().expressionContext(), QString(), &ok );
1170  if ( ok )
1171  {
1172  mPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1173  mSelPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1174  }
1175  }
1176 
1177  if ( shapeIsFilled( shape ) )
1178  {
1179  p->setBrush( context.selected() ? mSelBrush : mBrush );
1180  }
1181  else
1182  {
1183  p->setBrush( Qt::NoBrush );
1184  }
1185  p->setPen( context.selected() ? mSelPen : mPen );
1186 
1187  if ( !polygon.isEmpty() )
1188  p->drawPolygon( polygon );
1189  else
1190  p->drawPath( path );
1191 }
1192 
1194 {
1195  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1196  //of the rendered point!
1197 
1198  QPainter *p = context.renderContext().painter();
1199  if ( !p )
1200  {
1201  return;
1202  }
1203 
1204  if ( mUsingCache && qgsDoubleNear( mCachedOpacity, context.opacity() ) )
1205  {
1206  const QImage &img = context.selected() ? mSelCache : mCache;
1207  const double s = img.width();
1208 
1209  bool hasDataDefinedSize = false;
1210  const double scaledSize = calculateSize( context, hasDataDefinedSize );
1211 
1212  bool hasDataDefinedRotation = false;
1213  QPointF offset;
1214  double angle = 0;
1215  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
1216 
1217  p->drawImage( QRectF( point.x() - s / 2.0 + offset.x(),
1218  point.y() - s / 2.0 + offset.y(),
1219  s, s ), img );
1220  }
1221  else
1222  {
1224  }
1225 }
1226 
1228 {
1229  QVariantMap map;
1230  map[QStringLiteral( "name" )] = encodeShape( mShape );
1231  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
1232  map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
1233  map[QStringLiteral( "size" )] = QString::number( mSize );
1234  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1235  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1236  map[QStringLiteral( "angle" )] = QString::number( mAngle );
1237  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1238  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1239  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1240  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1241  map[QStringLiteral( "outline_style" )] = QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle );
1242  map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
1243  map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
1244  map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
1245  map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
1246  map[QStringLiteral( "cap_style" )] = QgsSymbolLayerUtils::encodePenCapStyle( mPenCapStyle );
1247  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1248  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1249  return map;
1250 }
1251 
1253 {
1255  m->setOffset( mOffset );
1256  m->setSizeUnit( mSizeUnit );
1258  m->setOffsetUnit( mOffsetUnit );
1268  copyPaintEffect( m );
1269  return m;
1270 }
1271 
1272 void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1273 {
1274  // <Graphic>
1275  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
1276  element.appendChild( graphicElem );
1277 
1279  const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
1281 
1282  // <Rotation>
1283  QString angleFunc;
1284  bool ok;
1285  const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
1286  if ( !ok )
1287  {
1288  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
1289  }
1290  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
1291  {
1292  angleFunc = QString::number( angle + mAngle );
1293  }
1294  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
1295 
1296  // <Displacement>
1297  const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
1299 }
1300 
1301 QString QgsSimpleMarkerSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
1302 {
1303  Q_UNUSED( mmScaleFactor )
1304  Q_UNUSED( mapUnitScaleFactor )
1305 #if 0
1306  QString ogrType = "3"; //default is circle
1307  if ( mName == "square" )
1308  {
1309  ogrType = "5";
1310  }
1311  else if ( mName == "triangle" )
1312  {
1313  ogrType = "7";
1314  }
1315  else if ( mName == "star" )
1316  {
1317  ogrType = "9";
1318  }
1319  else if ( mName == "circle" )
1320  {
1321  ogrType = "3";
1322  }
1323  else if ( mName == "cross" )
1324  {
1325  ogrType = "0";
1326  }
1327  else if ( mName == "x" || mName == "cross2" )
1328  {
1329  ogrType = "1";
1330  }
1331  else if ( mName == "line" )
1332  {
1333  ogrType = "10";
1334  }
1335 
1336  QString ogrString;
1337  ogrString.append( "SYMBOL(" );
1338  ogrString.append( "id:" );
1339  ogrString.append( '\"' );
1340  ogrString.append( "ogr-sym-" );
1341  ogrString.append( ogrType );
1342  ogrString.append( '\"' );
1343  ogrString.append( ",c:" );
1344  ogrString.append( mColor.name() );
1345  ogrString.append( ",o:" );
1346  ogrString.append( mStrokeColor.name() );
1347  ogrString.append( QString( ",s:%1mm" ).arg( mSize ) );
1348  ogrString.append( ')' );
1349  return ogrString;
1350 #endif //0
1351 
1352  QString ogrString;
1353  ogrString.append( "PEN(" );
1354  ogrString.append( "c:" );
1355  ogrString.append( mColor.name() );
1356  ogrString.append( ",w:" );
1357  ogrString.append( QString::number( mSize ) );
1358  ogrString.append( "mm" );
1359  ogrString.append( ")" );
1360  return ogrString;
1361 }
1362 
1364 {
1365  QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
1366 
1367  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
1368  if ( graphicElem.isNull() )
1369  return nullptr;
1370 
1371  QString name = QStringLiteral( "square" );
1372  QColor color, strokeColor;
1373  double strokeWidth, size;
1374  Qt::PenStyle strokeStyle;
1375 
1377  return nullptr;
1378 
1379  double angle = 0.0;
1380  QString angleFunc;
1381  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
1382  {
1383  bool ok;
1384  const double d = angleFunc.toDouble( &ok );
1385  if ( ok )
1386  angle = d;
1387  }
1388 
1389  QPointF offset;
1391 
1392  const Shape shape = decodeShape( name );
1393 
1394  const QString uom = element.attribute( QStringLiteral( "uom" ) );
1398 
1400  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
1401  m->setColor( color );
1403  m->setAngle( angle );
1404  m->setOffset( offset );
1407  return m;
1408 }
1409 
1411 {
1412  Q_UNUSED( context )
1413 
1414  if ( mPolygon.count() != 0 )
1415  {
1416  p->drawPolygon( mPolygon );
1417  }
1418  else
1419  {
1420  p->drawPath( mPath );
1421  }
1422 }
1423 
1424 bool QgsSimpleMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
1425 {
1426  //data defined size?
1427  double size = mSize;
1428 
1429  const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
1430 
1431  //data defined size
1432  bool ok = true;
1433  if ( hasDataDefinedSize )
1434  {
1436 
1437  if ( ok )
1438  {
1439  switch ( mScaleMethod )
1440  {
1442  size = std::sqrt( size );
1443  break;
1445  break;
1446  }
1447  }
1448 
1450  }
1451 
1453  {
1454  size *= mmMapUnitScaleFactor;
1455  }
1456 
1458  {
1460  }
1461  const double halfSize = size / 2.0;
1462 
1463  //strokeWidth
1464  double strokeWidth = mStrokeWidth;
1465 
1467  {
1470  }
1473  {
1475  }
1476 
1477  //color
1478  QColor pc = mPen.color();
1479  QColor bc = mBrush.color();
1481  {
1484  }
1486  {
1489  }
1490 
1491  //offset
1492  double offsetX = 0;
1493  double offsetY = 0;
1494  markerOffset( context, offsetX, offsetY );
1495  offsetX *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1496  offsetY *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1497 
1498 
1499  QPointF off( offsetX, offsetY );
1500 
1501  //angle
1502  double angle = mAngle + mLineAngle;
1504  {
1505  context.setOriginalValueVariable( mAngle );
1507  }
1508 
1509  Shape shape = mShape;
1511  {
1513  const QString shapeName = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyName, context.renderContext().expressionContext(), QString(), &ok );
1514  if ( ok )
1515  {
1516  shape = decodeShape( shapeName, &ok );
1517  if ( !ok )
1518  shape = mShape;
1519  }
1520  }
1521 
1522  if ( angle )
1523  off = _rotatedOffset( off, angle );
1524 
1526 
1527  QTransform t;
1528  t.translate( shift.x() + off.x(), shift.y() - off.y() );
1529 
1530  if ( !qgsDoubleNear( angle, 0.0 ) )
1531  t.rotate( angle );
1532 
1533  QPolygonF polygon;
1534  if ( shapeToPolygon( shape, polygon ) )
1535  {
1536  t.scale( halfSize, -halfSize );
1537 
1538  polygon = t.map( polygon );
1539 
1540  QgsPointSequence p;
1541  p.reserve( polygon.size() );
1542  for ( int i = 0; i < polygon.size(); i++ )
1543  {
1544  p << QgsPoint( polygon[i] );
1545  }
1546 
1547  if ( mBrush.style() != Qt::NoBrush )
1548  e.writePolygon( QgsRingSequence() << p, layerName, QStringLiteral( "SOLID" ), bc );
1549  if ( mPen.style() != Qt::NoPen )
1550  e.writePolyline( p, layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1551  }
1552  else if ( shape == Circle )
1553  {
1554  shift += QPointF( off.x(), -off.y() );
1555  if ( mBrush.style() != Qt::NoBrush )
1556  e.writeFilledCircle( layerName, bc, QgsPoint( shift ), halfSize );
1557  if ( mPen.style() != Qt::NoPen )
1558  e.writeCircle( layerName, pc, QgsPoint( shift ), halfSize, QStringLiteral( "CONTINUOUS" ), strokeWidth );
1559  }
1560  else if ( shape == Line )
1561  {
1562  const QPointF pt1 = t.map( QPointF( 0, -halfSize ) );
1563  const QPointF pt2 = t.map( QPointF( 0, halfSize ) );
1564 
1565  if ( mPen.style() != Qt::NoPen )
1566  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1567  }
1568  else if ( shape == Cross )
1569  {
1570  if ( mPen.style() != Qt::NoPen )
1571  {
1572  const QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
1573  const QPointF pt2 = t.map( QPointF( halfSize, 0 ) );
1574  const QPointF pt3 = t.map( QPointF( 0, -halfSize ) );
1575  const QPointF pt4 = t.map( QPointF( 0, halfSize ) );
1576 
1577  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1578  e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1579  }
1580  }
1581  else if ( shape == Cross2 )
1582  {
1583  if ( mPen.style() != Qt::NoPen )
1584  {
1585  const QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
1586  const QPointF pt2 = t.map( QPointF( halfSize, halfSize ) );
1587  const QPointF pt3 = t.map( QPointF( halfSize, -halfSize ) );
1588  const QPointF pt4 = t.map( QPointF( -halfSize, halfSize ) );
1589 
1590  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1591  e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1592  }
1593  }
1594  else if ( shape == ArrowHead )
1595  {
1596  if ( mPen.style() != Qt::NoPen )
1597  {
1598  const QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
1599  const QPointF pt2 = t.map( QPointF( 0, 0 ) );
1600  const QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
1601 
1602  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1603  e.writeLine( QgsPoint( pt3 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1604  }
1605  }
1606  else
1607  {
1608  QgsDebugMsg( QStringLiteral( "Unsupported dxf marker name %1" ).arg( encodeShape( shape ) ) );
1609  return false;
1610  }
1611 
1612  return true;
1613 }
1614 
1615 
1617 {
1619  mStrokeWidthUnit = unit;
1620 }
1621 
1623 {
1625  {
1626  return mStrokeWidthUnit;
1627  }
1629 }
1630 
1632 {
1634  mStrokeWidthMapUnitScale = scale;
1635 }
1636 
1638 {
1640  {
1641  return mStrokeWidthMapUnitScale;
1642  }
1643  return QgsMapUnitScale();
1644 }
1645 
1647 {
1651 }
1652 
1654 {
1655  QRectF symbolBounds = QgsSimpleMarkerSymbolLayerBase::bounds( point, context );
1656 
1657  // need to account for stroke width
1658  double penWidth = mStrokeWidth;
1659  bool ok = true;
1661  {
1664  if ( ok )
1665  {
1666  penWidth = strokeWidth;
1667  }
1668  }
1671  {
1674  if ( ok && strokeStyle == QLatin1String( "no" ) )
1675  {
1676  penWidth = 0.0;
1677  }
1678  }
1679  else if ( mStrokeStyle == Qt::NoPen )
1680  penWidth = 0;
1681 
1682  //antialiasing, add 1 pixel
1683  penWidth += 1;
1684 
1685  //extend bounds by pen width / 2.0
1686  symbolBounds.adjust( -penWidth / 2.0, -penWidth / 2.0,
1687  penWidth / 2.0, penWidth / 2.0 );
1688 
1689  return symbolBounds;
1690 }
1691 
1692 void QgsSimpleMarkerSymbolLayer::setColor( const QColor &color )
1693 {
1694  if ( shapeIsFilled( mShape ) )
1695  {
1696  setFillColor( color );
1697  }
1698  else
1699  {
1700  setStrokeColor( color );
1701  }
1702 }
1703 
1705 {
1706  if ( shapeIsFilled( mShape ) )
1707  {
1708  return fillColor();
1709  }
1710  else
1711  {
1712  return strokeColor();
1713  }
1714 }
1715 
1716 
1717 
1718 
1719 //
1720 // QgsFilledMarkerSymbolLayer
1721 //
1722 
1724  : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
1725 {
1726  mFill.reset( static_cast<QgsFillSymbol *>( QgsFillSymbol::createSimple( QVariantMap() ) ) );
1727 }
1728 
1730 
1732 {
1733  QString name = DEFAULT_SIMPLEMARKER_NAME;
1737 
1738  if ( props.contains( QStringLiteral( "name" ) ) )
1739  name = props[QStringLiteral( "name" )].toString();
1740  if ( props.contains( QStringLiteral( "size" ) ) )
1741  size = props[QStringLiteral( "size" )].toDouble();
1742  if ( props.contains( QStringLiteral( "angle" ) ) )
1743  angle = props[QStringLiteral( "angle" )].toDouble();
1744  if ( props.contains( QStringLiteral( "scale_method" ) ) )
1745  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1746 
1748  if ( props.contains( QStringLiteral( "offset" ) ) )
1749  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1750  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1751  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1752  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1753  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1754  if ( props.contains( QStringLiteral( "size_unit" ) ) )
1755  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1756  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1757  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1758  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1759  {
1760  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1761  }
1762  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1763  {
1764  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1765  }
1766 
1768 
1769  m->restoreOldDataDefinedProperties( props );
1770 
1771  return m;
1772 }
1773 
1775 {
1776  return QStringLiteral( "FilledMarker" );
1777 }
1778 
1780 {
1781  if ( mFill )
1782  {
1783  mFill->startRender( context.renderContext(), context.fields() );
1784  }
1785 
1787 }
1788 
1790 {
1791  if ( mFill )
1792  {
1793  mFill->stopRender( context.renderContext() );
1794  }
1795 }
1796 
1798 {
1799  QVariantMap map;
1800  map[QStringLiteral( "name" )] = encodeShape( mShape );
1801  map[QStringLiteral( "size" )] = QString::number( mSize );
1802  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1803  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1804  map[QStringLiteral( "angle" )] = QString::number( mAngle );
1805  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1806  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1807  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1808  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1809  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1810  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1811 
1812  if ( mFill )
1813  {
1814  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mFill->color() );
1815  }
1816  return map;
1817 }
1818 
1820 {
1822  copyPaintEffect( m );
1824  m->setSubSymbol( mFill->clone() );
1825  return m;
1826 }
1827 
1829 {
1830  return mFill.get();
1831 }
1832 
1834 {
1835  if ( symbol && symbol->type() == Qgis::SymbolType::Fill )
1836  {
1837  mFill.reset( static_cast<QgsFillSymbol *>( symbol ) );
1838  return true;
1839  }
1840  else
1841  {
1842  delete symbol;
1843  return false;
1844  }
1845 }
1846 
1848 {
1849  if ( mFill )
1850  {
1851  return QgsSymbolLayerUtils::estimateMaxSymbolBleed( mFill.get(), context );
1852  }
1853  return 0;
1854 }
1855 
1857 {
1858  QSet<QString> attr = QgsSimpleMarkerSymbolLayerBase::usedAttributes( context );
1859  if ( mFill )
1860  attr.unite( mFill->usedAttributes( context ) );
1861  return attr;
1862 }
1863 
1865 {
1867  return true;
1868  if ( mFill && mFill->hasDataDefinedProperties() )
1869  return true;
1870  return false;
1871 }
1872 
1874 {
1875  mColor = c;
1876  if ( mFill )
1877  mFill->setColor( c );
1878 }
1879 
1881 {
1882  return mFill ? mFill->color() : mColor;
1883 }
1884 
1886 {
1889  || ( mFill && mFill->usesMapUnits() );
1890 }
1891 
1892 void QgsFilledMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, QgsSimpleMarkerSymbolLayerBase::Shape shape, const QPolygonF &polygon, const QPainterPath &path )
1893 {
1894  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1895  //of the rendered point!
1896 
1897  QPainter *p = context.renderContext().painter();
1898  if ( !p )
1899  {
1900  return;
1901  }
1902 
1903  const double prevOpacity = mFill->opacity();
1904  mFill->setOpacity( mFill->opacity() * context.opacity() );
1905 
1906  if ( shapeIsFilled( shape ) )
1907  {
1908  p->setBrush( Qt::red );
1909  }
1910  else
1911  {
1912  p->setBrush( Qt::NoBrush );
1913  }
1914  p->setPen( Qt::black );
1915 
1916  if ( !polygon.isEmpty() )
1917  {
1918  mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
1919  }
1920  else
1921  {
1922  const QPolygonF poly = path.toFillPolygon();
1923  mFill->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
1924  }
1925 
1926  mFill->setOpacity( prevOpacity );
1927 }
1928 
1929 
1931 
1932 
1933 QgsSvgMarkerSymbolLayer::QgsSvgMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
1934 {
1935  mSize = size;
1936  mAngle = angle;
1937  mOffset = QPointF( 0, 0 );
1939  mStrokeWidth = 0.2;
1941  mColor = QColor( 35, 35, 35 );
1942  mStrokeColor = QColor( 35, 35, 35 );
1943  setPath( path );
1944 }
1945 
1947 
1949 {
1950  QString name;
1951  double size = DEFAULT_SVGMARKER_SIZE;
1952  double angle = DEFAULT_SVGMARKER_ANGLE;
1954 
1955  if ( props.contains( QStringLiteral( "name" ) ) )
1956  name = props[QStringLiteral( "name" )].toString();
1957  if ( props.contains( QStringLiteral( "size" ) ) )
1958  size = props[QStringLiteral( "size" )].toDouble();
1959  if ( props.contains( QStringLiteral( "angle" ) ) )
1960  angle = props[QStringLiteral( "angle" )].toDouble();
1961  if ( props.contains( QStringLiteral( "scale_method" ) ) )
1962  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1963 
1965 
1966  if ( props.contains( QStringLiteral( "size_unit" ) ) )
1967  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1968  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1969  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1970  if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
1971  m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
1972  if ( props.contains( QStringLiteral( "offset" ) ) )
1973  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1974  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1975  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1976  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1977  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1978  if ( props.contains( QStringLiteral( "fill" ) ) )
1979  {
1980  //pre 2.5 projects used "fill"
1981  m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "fill" )].toString() ) );
1982  }
1983  else if ( props.contains( QStringLiteral( "color" ) ) )
1984  {
1985  m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )].toString() ) );
1986  }
1987  if ( props.contains( QStringLiteral( "outline" ) ) )
1988  {
1989  //pre 2.5 projects used "outline"
1990  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline" )].toString() ) );
1991  }
1992  else if ( props.contains( QStringLiteral( "outline_color" ) ) )
1993  {
1994  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )].toString() ) );
1995  }
1996  else if ( props.contains( QStringLiteral( "line_color" ) ) )
1997  {
1998  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )].toString() ) );
1999  }
2000 
2001  if ( props.contains( QStringLiteral( "outline-width" ) ) )
2002  {
2003  //pre 2.5 projects used "outline-width"
2004  m->setStrokeWidth( props[QStringLiteral( "outline-width" )].toDouble() );
2005  }
2006  else if ( props.contains( QStringLiteral( "outline_width" ) ) )
2007  {
2008  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
2009  }
2010  else if ( props.contains( QStringLiteral( "line_width" ) ) )
2011  {
2012  m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
2013  }
2014 
2015  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
2016  {
2017  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
2018  }
2019  else if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
2020  {
2021  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
2022  }
2023  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
2024  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
2025 
2026  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2027  {
2028  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2029  }
2030  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2031  {
2032  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2033  }
2034 
2035  m->restoreOldDataDefinedProperties( props );
2036 
2038 
2039  if ( props.contains( QStringLiteral( "parameters" ) ) )
2040  {
2041  const QVariantMap parameters = props[QStringLiteral( "parameters" )].toMap();
2043  }
2044 
2045  return m;
2046 }
2047 
2048 void QgsSvgMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
2049 {
2050  const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
2051  if ( it != properties.end() )
2052  {
2053  if ( saving )
2054  {
2055  it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
2056  }
2057  else
2058  {
2059  it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
2060  }
2061  }
2062 }
2063 
2064 void QgsSvgMarkerSymbolLayer::setPath( const QString &path )
2065 {
2066  mDefaultAspectRatio = 0;
2067  mHasFillParam = false;
2068  mPath = path;
2069  QColor defaultFillColor, defaultStrokeColor;
2070  double strokeWidth, fillOpacity, strokeOpacity;
2071  bool hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
2072  bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
2073  QgsApplication::svgCache()->containsParams( path, mHasFillParam, hasDefaultFillColor, defaultFillColor,
2074  hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
2075  hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2076  hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
2077  hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
2078 
2079  const double newFillOpacity = hasFillOpacityParam ? fillColor().alphaF() : 1.0;
2080  const double newStrokeOpacity = hasStrokeOpacityParam ? strokeColor().alphaF() : 1.0;
2081 
2082  if ( hasDefaultFillColor )
2083  {
2084  defaultFillColor.setAlphaF( newFillOpacity );
2085  setFillColor( defaultFillColor );
2086  }
2087  if ( hasDefaultFillOpacity )
2088  {
2089  QColor c = fillColor();
2090  c.setAlphaF( fillOpacity );
2091  setFillColor( c );
2092  }
2093  if ( hasDefaultStrokeColor )
2094  {
2095  defaultStrokeColor.setAlphaF( newStrokeOpacity );
2096  setStrokeColor( defaultStrokeColor );
2097  }
2098  if ( hasDefaultStrokeWidth )
2099  {
2101  }
2102  if ( hasDefaultStrokeOpacity )
2103  {
2104  QColor c = strokeColor();
2105  c.setAlphaF( strokeOpacity );
2106  setStrokeColor( c );
2107  }
2108 
2110 }
2111 
2113 {
2114  if ( mDefaultAspectRatio == 0.0 )
2115  {
2116  //size
2117  const double size = mSize;
2118  //assume 88 dpi as standard value
2119  const double widthScaleFactor = 3.465;
2120  const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( mPath, size, mColor, mStrokeColor, mStrokeWidth, widthScaleFactor );
2121  // set default aspect ratio
2122  mDefaultAspectRatio = svgViewbox.isValid() ? svgViewbox.height() / svgViewbox.width() : 0.0;
2123  }
2124  return mDefaultAspectRatio;
2125 }
2126 
2128 {
2129  const bool aPreservedAspectRatio = preservedAspectRatio();
2130  if ( aPreservedAspectRatio && !par )
2131  {
2133  }
2134  else if ( !aPreservedAspectRatio && par )
2135  {
2136  mFixedAspectRatio = 0.0;
2137  }
2138  return preservedAspectRatio();
2139 }
2140 
2141 void QgsSvgMarkerSymbolLayer::setParameters( const QMap<QString, QgsProperty> &parameters )
2142 {
2144 }
2145 
2146 
2148 {
2149  return QStringLiteral( "SvgMarker" );
2150 }
2151 
2153 {
2154  QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
2155  Q_UNUSED( context )
2156 }
2157 
2159 {
2160  Q_UNUSED( context )
2161 }
2162 
2164 {
2165  QPainter *p = context.renderContext().painter();
2166  if ( !p )
2167  return;
2168 
2169  bool hasDataDefinedSize = false;
2170  const double scaledWidth = calculateSize( context, hasDataDefinedSize );
2171  const double width = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2172 
2173  //don't render symbols with a width below one or above 10,000 pixels
2174  if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2175  {
2176  return;
2177  }
2178 
2179  const QgsScopedQPainterState painterState( p );
2180 
2181  bool hasDataDefinedAspectRatio = false;
2182  const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2183  double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2184 
2186 
2187  double strokeWidth = mStrokeWidth;
2189  {
2192  }
2194 
2195  QColor fillColor = mColor;
2196  if ( context.selected() && mHasFillParam )
2197  {
2198  fillColor = context.renderContext().selectionColor();
2199  }
2201  {
2204  }
2205 
2206  QColor strokeColor = mStrokeColor;
2208  {
2211  }
2212 
2213  QString path = mPath;
2215  {
2216  context.setOriginalValueVariable( mPath );
2218  context.renderContext().pathResolver() );
2220  {
2221  // adjust height of data defined path
2222  const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2223  context.renderContext().scaleFactor(), aspectRatio,
2224  ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2225  scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2226  }
2227  }
2228 
2229  QPointF outputOffset;
2230  double angle = 0.0;
2231  calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2232 
2233  p->translate( point + outputOffset );
2234 
2235  const bool rotated = !qgsDoubleNear( angle, 0 );
2236  if ( rotated )
2237  p->rotate( angle );
2238 
2239  bool fitsInCache = true;
2240  bool usePict = true;
2241  const bool rasterizeSelected = !mHasFillParam || mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyName );
2242  if ( ( !context.renderContext().forceVectorOutput() && !rotated ) || ( context.selected() && rasterizeSelected ) )
2243  {
2245  context.renderContext().scaleFactor(), fitsInCache, aspectRatio,
2246  ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2247  if ( fitsInCache && img.width() > 1 )
2248  {
2249  usePict = false;
2250 
2251  if ( context.selected() )
2253 
2254  //consider transparency
2255  if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2256  {
2257  QImage transparentImage = img.copy();
2258  QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2259  p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
2260  }
2261  else
2262  {
2263  p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2264  }
2265  }
2266  }
2267 
2268  if ( usePict || !fitsInCache )
2269  {
2270  p->setOpacity( context.opacity() );
2271  const QPicture pct = QgsApplication::svgCache()->svgAsPicture( path, width, fillColor, strokeColor, strokeWidth,
2272  context.renderContext().scaleFactor(), context.renderContext().forceVectorOutput(), aspectRatio,
2273  ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2274  if ( pct.width() > 1 )
2275  {
2276  const QgsScopedQPainterState painterPictureState( p );
2277  _fixQPictureDPI( p );
2278  p->drawPicture( 0, 0, pct );
2279  }
2280  }
2281 
2282  // workaround issue with nested QPictures forgetting antialiasing flag - see https://github.com/qgis/QGIS/issues/22909
2284 }
2285 
2286 double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2287 {
2288  double scaledSize = mSize;
2290 
2291  bool ok = true;
2292  if ( hasDataDefinedSize )
2293  {
2294  context.setOriginalValueVariable( mSize );
2296  }
2297  else
2298  {
2300  if ( hasDataDefinedSize )
2301  {
2302  context.setOriginalValueVariable( mSize );
2304  }
2305  }
2306 
2307  if ( hasDataDefinedSize && ok )
2308  {
2309  switch ( mScaleMethod )
2310  {
2312  scaledSize = std::sqrt( scaledSize );
2313  break;
2315  break;
2316  }
2317  }
2318 
2319  return scaledSize;
2320 }
2321 
2322 double QgsSvgMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2323 {
2325  if ( !hasDataDefinedAspectRatio )
2326  return mFixedAspectRatio;
2327 
2329  return 0.0;
2330 
2331  double scaledAspectRatio = mDefaultAspectRatio;
2332  if ( mFixedAspectRatio > 0.0 )
2333  scaledAspectRatio = mFixedAspectRatio;
2334 
2335  const double defaultHeight = mSize * scaledAspectRatio;
2336  scaledAspectRatio = defaultHeight / scaledSize;
2337 
2338  bool ok = true;
2339  double scaledHeight = scaledSize * scaledAspectRatio;
2341  {
2342  context.setOriginalValueVariable( defaultHeight );
2343  scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyHeight, context.renderContext().expressionContext(), defaultHeight, &ok );
2344  }
2345 
2346  if ( hasDataDefinedAspectRatio && ok )
2347  {
2348  switch ( mScaleMethod )
2349  {
2351  scaledHeight = sqrt( scaledHeight );
2352  break;
2354  break;
2355  }
2356  }
2357 
2358  scaledAspectRatio = scaledHeight / scaledSize;
2359 
2360  return scaledAspectRatio;
2361 }
2362 
2363 void QgsSvgMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
2364 {
2365  //offset
2366  double offsetX = 0;
2367  double offsetY = 0;
2368  markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
2369  offset = QPointF( offsetX, offsetY );
2370 
2371  angle = mAngle + mLineAngle;
2373  {
2374  context.setOriginalValueVariable( mAngle );
2376  }
2377 
2379  if ( hasDataDefinedRotation )
2380  {
2381  // For non-point markers, "dataDefinedRotation" means following the
2382  // shape (shape-data defined). For them, "field-data defined" does
2383  // not work at all. TODO: if "field-data defined" ever gets implemented
2384  // we'll need a way to distinguish here between the two, possibly
2385  // using another flag in renderHints()
2386  const QgsFeature *f = context.feature();
2387  if ( f )
2388  {
2389  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
2390  {
2391  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2392  angle += m2p.mapRotation();
2393  }
2394  }
2395  }
2396 
2397  if ( angle )
2399 }
2400 
2401 
2403 {
2404  QVariantMap map;
2405  map[QStringLiteral( "name" )] = mPath;
2406  map[QStringLiteral( "size" )] = QString::number( mSize );
2407  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2408  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2409  map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2410  map[QStringLiteral( "angle" )] = QString::number( mAngle );
2411  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2412  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2413  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2414  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2415  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
2416  map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
2417  map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
2418  map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2419  map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2420  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
2421  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
2422 
2423  map[QStringLiteral( "parameters" )] = QgsProperty::propertyMapToVariantMap( mParameters );
2424 
2425  return map;
2426 }
2427 
2429 {
2433 }
2434 
2436 {
2439  m->setColor( mColor );
2444  m->setOffset( mOffset );
2445  m->setOffsetUnit( mOffsetUnit );
2447  m->setSizeUnit( mSizeUnit );
2451  m->setParameters( mParameters );
2452 
2454  copyPaintEffect( m );
2455  return m;
2456 }
2457 
2459 {
2461  mStrokeWidthUnit = unit;
2462 }
2463 
2465 {
2467  if ( unit != mStrokeWidthUnit )
2468  {
2470  }
2471  return unit;
2472 }
2473 
2475 {
2477  mStrokeWidthMapUnitScale = scale;
2478 }
2479 
2481 {
2483  {
2484  return mStrokeWidthMapUnitScale;
2485  }
2486  return QgsMapUnitScale();
2487 }
2488 
2489 void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
2490 {
2491  // <Graphic>
2492  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2493  element.appendChild( graphicElem );
2494 
2495  // encode a parametric SVG reference
2496  const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
2499 
2500  // <Rotation>
2501  QString angleFunc;
2502  bool ok;
2503  const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2504  if ( !ok )
2505  {
2506  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
2507  }
2508  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2509  {
2510  angleFunc = QString::number( angle + mAngle );
2511  }
2512 
2513  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2514 
2515  // <Displacement>
2516  const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2518 }
2519 
2521 {
2522  QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
2523 
2524  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
2525  if ( graphicElem.isNull() )
2526  return nullptr;
2527 
2528  QString path, mimeType;
2529  QColor fillColor;
2530  double size;
2531 
2532  if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
2533  return nullptr;
2534 
2535  const QString uom = element.attribute( QStringLiteral( "uom" ) );
2537 
2538  if ( mimeType != QLatin1String( "image/svg+xml" ) )
2539  return nullptr;
2540 
2541  double angle = 0.0;
2542  QString angleFunc;
2543  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2544  {
2545  bool ok;
2546  const double d = angleFunc.toDouble( &ok );
2547  if ( ok )
2548  angle = d;
2549  }
2550 
2551  QPointF offset;
2553 
2555  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
2556  m->setFillColor( fillColor );
2557  //m->setStrokeColor( strokeColor );
2558  //m->setStrokeWidth( strokeWidth );
2559  m->setAngle( angle );
2560  m->setOffset( offset );
2561  return m;
2562 }
2563 
2564 bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
2565 {
2566  //size
2567  double size = mSize;
2568 
2569  const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
2570 
2571  bool ok = true;
2572  if ( hasDataDefinedSize )
2573  {
2574  context.setOriginalValueVariable( mSize );
2576  }
2577 
2578  if ( hasDataDefinedSize && ok )
2579  {
2580  switch ( mScaleMethod )
2581  {
2583  size = std::sqrt( size );
2584  break;
2586  break;
2587  }
2588  }
2589 
2591  {
2592  size *= mmMapUnitScaleFactor;
2593  }
2594 
2595  //offset, angle
2596  QPointF offset = mOffset;
2597 
2599  {
2601  const QVariant val = mDataDefinedProperties.value( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext(), QString() );
2602  const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
2603  if ( ok )
2604  offset = res;
2605  }
2606  const double offsetX = offset.x();
2607  const double offsetY = offset.y();
2608 
2609  QPointF outputOffset( offsetX, offsetY );
2610 
2611  double angle = mAngle + mLineAngle;
2613  {
2614  context.setOriginalValueVariable( mAngle );
2616  }
2617 
2618  if ( angle )
2619  outputOffset = _rotatedOffset( outputOffset, angle );
2620 
2621  outputOffset *= e.mapUnitScaleFactor( e.symbologyScale(), mOffsetUnit, e.mapUnits(), context.renderContext().mapToPixel().mapUnitsPerPixel() );
2622 
2623  QString path = mPath;
2625  {
2626  context.setOriginalValueVariable( mPath );
2628  context.renderContext().pathResolver() );
2629  }
2630 
2631  double strokeWidth = mStrokeWidth;
2633  {
2636  }
2638 
2639  QColor fillColor = mColor;
2641  {
2644  }
2645 
2646  QColor strokeColor = mStrokeColor;
2648  {
2651  }
2652 
2654 
2655  const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, size, fillColor, strokeColor, strokeWidth,
2657  ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2658 
2659  QSvgRenderer r( svgContent );
2660  if ( !r.isValid() )
2661  return false;
2662 
2663  QgsDxfPaintDevice pd( &e );
2664  pd.setDrawingSize( QSizeF( r.defaultSize() ) );
2665 
2666  QSizeF outSize( r.defaultSize() );
2667  outSize.scale( size, size, Qt::KeepAspectRatio );
2668 
2669  QPainter p;
2670  p.begin( &pd );
2671  if ( !qgsDoubleNear( angle, 0.0 ) )
2672  {
2673  p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
2674  p.rotate( angle );
2675  p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
2676  }
2677  pd.setShift( shift + QPointF( outputOffset.x(), -outputOffset.y() ) );
2678  pd.setOutputSize( QRectF( -outSize.width() / 2.0, -outSize.height() / 2.0, outSize.width(), outSize.height() ) );
2679  pd.setLayer( layerName );
2680  r.render( &p );
2681  p.end();
2682  return true;
2683 }
2684 
2686 {
2687  bool hasDataDefinedSize = false;
2688  double scaledWidth = calculateSize( context, hasDataDefinedSize );
2689 
2690  bool hasDataDefinedAspectRatio = false;
2691  const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2692  double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2693 
2694  scaledWidth = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2695  scaledHeight = context.renderContext().convertToPainterUnits( scaledHeight, mSizeUnit, mSizeMapUnitScale );
2696 
2697  //don't render symbols with size below one or above 10,000 pixels
2698  if ( static_cast< int >( scaledWidth ) < 1 || 10000.0 < scaledWidth )
2699  {
2700  return QRectF();
2701  }
2702 
2703  QPointF outputOffset;
2704  double angle = 0.0;
2705  calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2706 
2707  double strokeWidth = mStrokeWidth;
2709  {
2712  }
2714 
2715  QString path = mPath;
2717  {
2718  context.setOriginalValueVariable( mPath );
2720  context.renderContext().pathResolver() );
2722  {
2723  // need to get colors to take advantage of cached SVGs
2724  QColor fillColor = mColor;
2726  {
2729  }
2730 
2731  const QColor strokeColor = mStrokeColor;
2733  {
2736  }
2737 
2739 
2740  // adjust height of data defined path
2741  const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2742  context.renderContext().scaleFactor(), aspectRatio,
2743  ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2744  scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2745  }
2746  }
2747 
2748  QTransform transform;
2749  // move to the desired position
2750  transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
2751 
2752  if ( !qgsDoubleNear( angle, 0.0 ) )
2753  transform.rotate( angle );
2754 
2755  //antialiasing
2756  strokeWidth += 1.0 / 2.0;
2757 
2758  QRectF symbolBounds = transform.mapRect( QRectF( -scaledWidth / 2.0,
2759  -scaledHeight / 2.0,
2760  scaledWidth,
2761  scaledHeight ) );
2762 
2763  //extend bounds by pen width / 2.0
2764  symbolBounds.adjust( -strokeWidth / 2.0, -strokeWidth / 2.0,
2765  strokeWidth / 2.0, strokeWidth / 2.0 );
2766 
2767  return symbolBounds;
2768 }
2769 
2771 
2772 QgsRasterMarkerSymbolLayer::QgsRasterMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
2773  : mPath( path )
2774 {
2775  mSize = size;
2776  mAngle = angle;
2777  mOffset = QPointF( 0, 0 );
2780 }
2781 
2783 
2785 {
2786  QString path;
2790 
2791  if ( props.contains( QStringLiteral( "imageFile" ) ) )
2792  path = props[QStringLiteral( "imageFile" )].toString();
2793  if ( props.contains( QStringLiteral( "size" ) ) )
2794  size = props[QStringLiteral( "size" )].toDouble();
2795  if ( props.contains( QStringLiteral( "angle" ) ) )
2796  angle = props[QStringLiteral( "angle" )].toDouble();
2797  if ( props.contains( QStringLiteral( "scale_method" ) ) )
2798  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
2799 
2801 
2802  if ( props.contains( QStringLiteral( "alpha" ) ) )
2803  {
2804  m->setOpacity( props[QStringLiteral( "alpha" )].toDouble() );
2805  }
2806 
2807  if ( props.contains( QStringLiteral( "size_unit" ) ) )
2808  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
2809  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
2810  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
2811  if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
2812  m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
2813 
2814  if ( props.contains( QStringLiteral( "offset" ) ) )
2815  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
2816  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
2817  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
2818  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2819  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
2820 
2821  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2822  {
2823  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2824  }
2825  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2826  {
2827  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2828  }
2829 
2830  m->restoreOldDataDefinedProperties( props );
2832 
2833  return m;
2834 }
2835 
2836 void QgsRasterMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
2837 {
2838  const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
2839  if ( it != properties.end() && it.value().type() == QVariant::String )
2840  {
2841  if ( saving )
2842  it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
2843  else
2844  it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
2845  }
2846 }
2847 
2848 void QgsRasterMarkerSymbolLayer::setPath( const QString &path )
2849 {
2850  mPath = path;
2852 }
2853 
2855 {
2856  const bool aPreservedAspectRatio = preservedAspectRatio();
2857  if ( aPreservedAspectRatio && !par )
2858  {
2860  }
2861  else if ( !aPreservedAspectRatio && par )
2862  {
2863  mFixedAspectRatio = 0.0;
2864  }
2865  return preservedAspectRatio();
2866 }
2867 
2869 {
2870  if ( mDefaultAspectRatio == 0.0 )
2871  {
2872  const QSize size = QgsApplication::imageCache()->originalSize( mPath );
2873  mDefaultAspectRatio = ( !size.isNull() && size.isValid() && size.width() > 0 ) ? static_cast< double >( size.height() ) / static_cast< double >( size.width() ) : 0.0;
2874  }
2875  return mDefaultAspectRatio;
2876 }
2877 
2879 {
2880  return QStringLiteral( "RasterMarker" );
2881 }
2882 
2884 {
2885  QPainter *p = context.renderContext().painter();
2886  if ( !p )
2887  return;
2888 
2889  QString path = mPath;
2891  {
2892  context.setOriginalValueVariable( mPath );
2894  }
2895 
2896  if ( path.isEmpty() )
2897  return;
2898 
2899  double width = 0.0;
2900  double height = 0.0;
2901 
2902  bool hasDataDefinedSize = false;
2903  const double scaledSize = calculateSize( context, hasDataDefinedSize );
2904 
2905  bool hasDataDefinedAspectRatio = false;
2906  const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
2907 
2908  QPointF outputOffset;
2909  double angle = 0.0;
2910 
2911  // RenderPercentage Unit Type takes original image size
2913  {
2914  const QSize size = QgsApplication::imageCache()->originalSize( path );
2915  if ( size.isEmpty() )
2916  return;
2917 
2918  width = ( scaledSize * static_cast< double >( size.width() ) ) / 100.0;
2919  height = ( scaledSize * static_cast< double >( size.height() ) ) / 100.0;
2920 
2921  // don't render symbols with size below one or above 10,000 pixels
2922  if ( static_cast< int >( width ) < 1 || 10000.0 < width || static_cast< int >( height ) < 1 || 10000.0 < height )
2923  return;
2924 
2925  calculateOffsetAndRotation( context, width, height, outputOffset, angle );
2926  }
2927  else
2928  {
2929  width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
2930  height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
2931 
2932  if ( preservedAspectRatio() && path != mPath )
2933  {
2934  const QSize size = QgsApplication::imageCache()->originalSize( path );
2935  if ( !size.isNull() && size.isValid() && size.width() > 0 )
2936  {
2937  height = width * ( static_cast< double >( size.height() ) / static_cast< double >( size.width() ) );
2938  }
2939  }
2940 
2941  // don't render symbols with size below one or above 10,000 pixels
2942  if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2943  return;
2944 
2945  calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
2946  }
2947 
2948  const QgsScopedQPainterState painterState( p );
2949  p->translate( point + outputOffset );
2950 
2951  const bool rotated = !qgsDoubleNear( angle, 0 );
2952  if ( rotated )
2953  p->rotate( angle );
2954 
2955  double opacity = mOpacity;
2957  {
2960  }
2961  opacity *= context.opacity();
2962 
2963  bool cached;
2964  QImage img = QgsApplication::imageCache()->pathAsImage( path, QSize( width, preservedAspectRatio() ? 0 : width * aspectRatio ), preservedAspectRatio(), opacity, cached, ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ) );
2965  if ( !img.isNull() )
2966  {
2967  if ( context.selected() )
2969 
2970  p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2971  }
2972 }
2973 
2974 double QgsRasterMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2975 {
2976  double scaledSize = mSize;
2978 
2979  bool ok = true;
2980  if ( hasDataDefinedSize )
2981  {
2982  context.setOriginalValueVariable( mSize );
2984  }
2985  else
2986  {
2988  if ( hasDataDefinedSize )
2989  {
2990  context.setOriginalValueVariable( mSize );
2992  }
2993  }
2994 
2995  if ( hasDataDefinedSize && ok )
2996  {
2997  switch ( mScaleMethod )
2998  {
3000  scaledSize = std::sqrt( scaledSize );
3001  break;
3003  break;
3004  }
3005  }
3006 
3007  return scaledSize;
3008 }
3009 
3010 double QgsRasterMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
3011 {
3013  if ( !hasDataDefinedAspectRatio )
3014  return mFixedAspectRatio;
3015 
3017  return 0.0;
3018 
3019  double scaledAspectRatio = mDefaultAspectRatio;
3020  if ( mFixedAspectRatio > 0.0 )
3021  scaledAspectRatio = mFixedAspectRatio;
3022 
3023  const double defaultHeight = mSize * scaledAspectRatio;
3024  scaledAspectRatio = defaultHeight / scaledSize;
3025 
3026  bool ok = true;
3027  double scaledHeight = scaledSize * scaledAspectRatio;
3029  {
3030  context.setOriginalValueVariable( defaultHeight );
3031  scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyHeight, context.renderContext().expressionContext(), defaultHeight, &ok );
3032  }
3033 
3034  if ( hasDataDefinedAspectRatio && ok )
3035  {
3036  switch ( mScaleMethod )
3037  {
3039  scaledHeight = sqrt( scaledHeight );
3040  break;
3042  break;
3043  }
3044  }
3045 
3046  scaledAspectRatio = scaledHeight / scaledSize;
3047 
3048  return scaledAspectRatio;
3049 }
3050 
3051 void QgsRasterMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
3052 {
3053  //offset
3054  double offsetX = 0;
3055  double offsetY = 0;
3056  markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
3057  offset = QPointF( offsetX, offsetY );
3058 
3059  angle = mAngle + mLineAngle;
3061  {
3062  context.setOriginalValueVariable( mAngle );
3064  }
3065 
3067  if ( hasDataDefinedRotation )
3068  {
3069  const QgsFeature *f = context.feature();
3070  if ( f )
3071  {
3072  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
3073  {
3074  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3075  angle += m2p.mapRotation();
3076  }
3077  }
3078  }
3079 
3080  if ( angle )
3082 }
3083 
3084 
3086 {
3087  QVariantMap map;
3088  map[QStringLiteral( "imageFile" )] = mPath;
3089  map[QStringLiteral( "size" )] = QString::number( mSize );
3090  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3091  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3092  map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
3093  map[QStringLiteral( "angle" )] = QString::number( mAngle );
3094  map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
3095  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3096  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3097  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3098  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
3099  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3100  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3101  return map;
3102 }
3103 
3105 {
3108  m->setOpacity( mOpacity );
3109  m->setOffset( mOffset );
3110  m->setOffsetUnit( mOffsetUnit );
3112  m->setSizeUnit( mSizeUnit );
3117  copyPaintEffect( m );
3118  return m;
3119 }
3120 
3122 {
3125 }
3126 
3128 {
3130 }
3131 
3133 {
3135 }
3136 
3138 {
3139  bool hasDataDefinedSize = false;
3140  const double scaledSize = calculateSize( context, hasDataDefinedSize );
3141  const double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3142  bool hasDataDefinedAspectRatio = false;
3143  const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3144  const double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3145 
3146  //don't render symbols with size below one or above 10,000 pixels
3147  if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
3148  {
3149  return QRectF();
3150  }
3151 
3152  QPointF outputOffset;
3153  double angle = 0.0;
3154  calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3155 
3156  QTransform transform;
3157 
3158  // move to the desired position
3159  transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3160 
3161  if ( !qgsDoubleNear( angle, 0.0 ) )
3162  transform.rotate( angle );
3163 
3164  QRectF symbolBounds = transform.mapRect( QRectF( -width / 2.0,
3165  -height / 2.0,
3166  width,
3167  height ) );
3168 
3169  return symbolBounds;
3170 }
3171 
3173 
3174 QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QString chr, double pointSize, const QColor &color, double angle )
3175 {
3176  mFontFamily = fontFamily;
3177  mString = chr;
3178  mColor = color;
3179  mAngle = angle;
3180  mSize = pointSize;
3181  mOrigSize = pointSize;
3183  mOffset = QPointF( 0, 0 );
3185  mStrokeColor = DEFAULT_FONTMARKER_BORDERCOLOR;
3186  mStrokeWidth = 0.0;
3187  mStrokeWidthUnit = QgsUnitTypes::RenderMillimeters;
3188  mPenJoinStyle = DEFAULT_FONTMARKER_JOINSTYLE;
3189 }
3190 
3192 
3194 {
3196  const QString fontStyle = DEFAULT_FONTMARKER_FONT;
3197  QString string = DEFAULT_FONTMARKER_CHR;
3198  double pointSize = DEFAULT_FONTMARKER_SIZE;
3201 
3202  if ( props.contains( QStringLiteral( "font" ) ) )
3203  fontFamily = props[QStringLiteral( "font" )].toString();
3204  if ( props.contains( QStringLiteral( "chr" ) ) && props[QStringLiteral( "chr" )].toString().length() > 0 )
3205  string = props[QStringLiteral( "chr" )].toString();
3206  if ( props.contains( QStringLiteral( "size" ) ) )
3207  pointSize = props[QStringLiteral( "size" )].toDouble();
3208  if ( props.contains( QStringLiteral( "color" ) ) )
3209  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )].toString() );
3210  if ( props.contains( QStringLiteral( "angle" ) ) )
3211  angle = props[QStringLiteral( "angle" )].toDouble();
3212 
3213  QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( fontFamily, string, pointSize, color, angle );
3214 
3215  if ( props.contains( QStringLiteral( "font_style" ) ) )
3216  m->setFontStyle( props[QStringLiteral( "font_style" )].toString() );
3217  if ( props.contains( QStringLiteral( "outline_color" ) ) )
3218  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )].toString() ) );
3219  if ( props.contains( QStringLiteral( "outline_width" ) ) )
3220  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
3221  if ( props.contains( QStringLiteral( "offset" ) ) )
3222  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
3223  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
3224  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
3225  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3226  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3227  if ( props.contains( QStringLiteral( "size_unit" ) ) )
3228  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
3229  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3230  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3231  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
3232  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
3233  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
3234  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
3235  if ( props.contains( QStringLiteral( "joinstyle" ) ) )
3236  m->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() ) );
3237  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3238  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3239  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3240  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3241 
3242  m->restoreOldDataDefinedProperties( props );
3243 
3244  return m;
3245 }
3246 
3248 {
3249  return QStringLiteral( "FontMarker" );
3250 }
3251 
3253 {
3254  QColor brushColor = mColor;
3255  QColor penColor = mStrokeColor;
3256 
3257  brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
3258  penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
3259 
3260  mBrush = QBrush( brushColor );
3261  mPen = QPen( penColor );
3262  mPen.setJoinStyle( mPenJoinStyle );
3263  mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
3264 
3265  mFont = QFont( mFontFamily );
3266  if ( !mFontStyle.isEmpty() )
3267  {
3268  mFont.setStyleName( QgsFontUtils::translateNamedStyle( mFontStyle ) );
3269  }
3270 
3271  double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3272  mNonZeroFontSize = !qgsDoubleNear( sizePixels, 0.0 );
3273 
3274  if ( mNonZeroFontSize && sizePixels > MAX_FONT_CHARACTER_SIZE_IN_PIXELS )
3275  {
3276  // if font is too large (e.g using map units and map is very zoomed in), then we limit
3277  // the font size and instead scale up the painter.
3278  // this avoids issues with massive font sizes (eg https://github.com/qgis/QGIS/issues/42270)
3279  mFontSizeScale = sizePixels / MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3280  sizePixels = MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3281  }
3282  else
3283  mFontSizeScale = 1.0;
3284 
3285  // if a non zero, but small pixel size results, round up to 2 pixels so that a "dot" is at least visible
3286  // (if we set a <=1 pixel size here Qt will reset the font to a default size, leading to much too large symbols)
3287  mFont.setPixelSize( std::max( 2, static_cast< int >( std::round( sizePixels ) ) ) );
3288  mFontMetrics.reset( new QFontMetrics( mFont ) );
3289  mChrWidth = mFontMetrics->horizontalAdvance( mString );
3290  mChrOffset = QPointF( mChrWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3291  mOrigSize = mSize; // save in case the size would be data defined
3292 
3293  // use caching only when not using a data defined character
3297  if ( mUseCachedPath )
3298  {
3299  QPointF chrOffset = mChrOffset;
3300  double chrWidth;
3301  const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3302  mCachedPath = QPainterPath();
3303  mCachedPath.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3304  }
3305 }
3306 
3308 {
3309  Q_UNUSED( context )
3310 }
3311 
3312 QString QgsFontMarkerSymbolLayer::characterToRender( QgsSymbolRenderContext &context, QPointF &charOffset, double &charWidth )
3313 {
3314  charOffset = mChrOffset;
3315  QString stringToRender = mString;
3317  {
3318  context.setOriginalValueVariable( mString );
3320  if ( stringToRender != mString )
3321  {
3322  charWidth = mFontMetrics->horizontalAdvance( stringToRender );
3323  charOffset = QPointF( charWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3324  }
3325  }
3326  return stringToRender;
3327 }
3328 
3329 void QgsFontMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
3330  double scaledSize,
3331  bool &hasDataDefinedRotation,
3332  QPointF &offset,
3333  double &angle ) const
3334 {
3335  //offset
3336  double offsetX = 0;
3337  double offsetY = 0;
3338  markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
3339  offset = QPointF( offsetX, offsetY );
3340 
3341  //angle
3342  bool ok = true;
3343  angle = mAngle + mLineAngle;
3345  {
3346  context.setOriginalValueVariable( angle );
3348 
3349  // If the expression evaluation was not successful, fallback to static value
3350  if ( !ok )
3351  angle = mAngle + mLineAngle;
3352  }
3353 
3354  hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation;
3355  if ( hasDataDefinedRotation )
3356  {
3357  // For non-point markers, "dataDefinedRotation" means following the
3358  // shape (shape-data defined). For them, "field-data defined" does
3359  // not work at all. TODO: if "field-data defined" ever gets implemented
3360  // we'll need a way to distinguish here between the two, possibly
3361  // using another flag in renderHints()
3362  const QgsFeature *f = context.feature();
3363  if ( f )
3364  {
3365  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
3366  {
3367  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3368  angle += m2p.mapRotation();
3369  }
3370  }
3371  }
3372 
3373  if ( angle )
3375 }
3376 
3377 double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context )
3378 {
3379  double scaledSize = mSize;
3380  const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
3381 
3382  bool ok = true;
3383  if ( hasDataDefinedSize )
3384  {
3385  context.setOriginalValueVariable( mSize );
3387  }
3388 
3389  if ( hasDataDefinedSize && ok )
3390  {
3391  switch ( mScaleMethod )
3392  {
3394  scaledSize = std::sqrt( scaledSize );
3395  break;
3397  break;
3398  }
3399  }
3400  return scaledSize;
3401 }
3402 
3404 {
3405  QPainter *p = context.renderContext().painter();
3406  if ( !p || !mNonZeroFontSize )
3407  return;
3408 
3409  QTransform transform;
3410 
3411  bool ok;
3412  QColor brushColor = mColor;
3414  {
3417  }
3418  brushColor = context.selected() ? context.renderContext().selectionColor() : brushColor;
3419  if ( !context.selected() || !SELECTION_IS_OPAQUE )
3420  {
3421  brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
3422  }
3423  mBrush.setColor( brushColor );
3424 
3425  QColor penColor = mStrokeColor;
3427  {
3430  }
3431  penColor.setAlphaF( penColor.alphaF() * context.opacity() );
3432 
3433  double penWidth = context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3435  {
3436  context.setOriginalValueVariable( mStrokeWidth );
3438  if ( ok )
3439  {
3440  penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3441  }
3442  }
3443 
3445  {
3447  const QString style = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyJoinStyle, context.renderContext().expressionContext(), QString(), &ok );
3448  if ( ok )
3449  {
3450  mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
3451  }
3452  }
3453 
3454  const QgsScopedQPainterState painterState( p );
3455  p->setBrush( mBrush );
3456  if ( !qgsDoubleNear( penWidth, 0.0 ) )
3457  {
3458  mPen.setColor( penColor );
3459  mPen.setWidthF( penWidth );
3460  p->setPen( mPen );
3461  }
3462  else
3463  {
3464  p->setPen( Qt::NoPen );
3465  }
3466 
3468  {
3469  context.setOriginalValueVariable( mFontFamily );
3471  mFont.setFamily( ok ? fontFamily : mFontFamily );
3472  }
3474  {
3475  context.setOriginalValueVariable( mFontStyle );
3478  }
3480  {
3481  mFontMetrics.reset( new QFontMetrics( mFont ) );
3482  }
3483 
3484  QPointF chrOffset = mChrOffset;
3485  double chrWidth;
3486  const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3487 
3488  const double sizeToRender = calculateSize( context );
3489 
3490  bool hasDataDefinedRotation = false;
3491  QPointF offset;
3492  double angle = 0;
3493  calculateOffsetAndRotation( context, sizeToRender, hasDataDefinedRotation, offset, angle );
3494 
3495  p->translate( point.x() + offset.x(), point.y() + offset.y() );
3496 
3497  if ( !qgsDoubleNear( angle, 0.0 ) )
3498  transform.rotate( angle );
3499 
3500  if ( !qgsDoubleNear( sizeToRender, mOrigSize ) )
3501  {
3502  const double s = sizeToRender / mOrigSize;
3503  transform.scale( s, s );
3504  }
3505 
3506  if ( !qgsDoubleNear( mFontSizeScale, 1.0 ) )
3507  transform.scale( mFontSizeScale, mFontSizeScale );
3508 
3509  if ( mUseCachedPath )
3510  {
3511  p->drawPath( transform.map( mCachedPath ) );
3512  }
3513  else
3514  {
3515  QPainterPath path;
3516  path.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3517  p->drawPath( transform.map( path ) );
3518  }
3519 }
3520 
3522 {
3523  QVariantMap props;
3524  props[QStringLiteral( "font" )] = mFontFamily;
3525  props[QStringLiteral( "font_style" )] = mFontStyle;
3526  props[QStringLiteral( "chr" )] = mString;
3527  props[QStringLiteral( "size" )] = QString::number( mSize );
3528  props[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3529  props[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3530  props[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
3531  props[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
3532  props[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
3533  props[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
3534  props[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
3535  props[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
3536  props[QStringLiteral( "angle" )] = QString::number( mAngle );
3537  props[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3538  props[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3539  props[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3540  props[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3541  props[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3542  return props;
3543 }
3544 
3546 {
3547  QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( mFontFamily, mString, mSize, mColor, mAngle );
3548  m->setFontStyle( mFontStyle );
3549  m->setStrokeColor( mStrokeColor );
3550  m->setStrokeWidth( mStrokeWidth );
3551  m->setStrokeWidthUnit( mStrokeWidthUnit );
3552  m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
3553  m->setPenJoinStyle( mPenJoinStyle );
3554  m->setOffset( mOffset );
3555  m->setOffsetUnit( mOffsetUnit );
3557  m->setSizeUnit( mSizeUnit );
3562  copyPaintEffect( m );
3563  return m;
3564 }
3565 
3566 void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
3567 {
3568  // <Graphic>
3569  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
3570  element.appendChild( graphicElem );
3571 
3572  const QString fontPath = QStringLiteral( "ttf://%1" ).arg( mFontFamily );
3573  int markIndex = !mString.isEmpty() ? mString.at( 0 ).unicode() : 0;
3574  const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
3575  QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, QStringLiteral( "ttf" ), &markIndex, mColor, size );
3576 
3577  // <Rotation>
3578  QString angleFunc;
3579  bool ok;
3580  const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
3581  if ( !ok )
3582  {
3583  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
3584  }
3585  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
3586  {
3587  angleFunc = QString::number( angle + mAngle );
3588  }
3589  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
3590 
3591  // <Displacement>
3592  const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
3594 }
3595 
3597 {
3599  || mStrokeWidthUnit == QgsUnitTypes::RenderMapUnits || mStrokeWidthUnit == QgsUnitTypes::RenderMetersInMapUnits
3601 }
3602 
3604 {
3605  QPointF chrOffset = mChrOffset;
3606  double chrWidth = mChrWidth;
3607  //calculate width of rendered character
3608  ( void )characterToRender( context, chrOffset, chrWidth );
3609 
3610  if ( !mFontMetrics )
3611  mFontMetrics.reset( new QFontMetrics( mFont ) );
3612 
3613  double scaledSize = calculateSize( context );
3614  if ( !qgsDoubleNear( scaledSize, mOrigSize ) )
3615  {
3616  chrWidth *= scaledSize / mOrigSize;
3617  }
3618  chrWidth *= mFontSizeScale;
3619 
3620  bool hasDataDefinedRotation = false;
3621  QPointF offset;
3622  double angle = 0;
3623  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
3624  scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3625 
3626  QTransform transform;
3627 
3628  // move to the desired position
3629  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
3630 
3631  if ( !qgsDoubleNear( angle, 0.0 ) )
3632  transform.rotate( angle );
3633 
3634  QRectF symbolBounds = transform.mapRect( QRectF( -chrWidth / 2.0,
3635  -scaledSize / 2.0,
3636  chrWidth,
3637  scaledSize ) );
3638  return symbolBounds;
3639 }
3640 
3642 {
3643  QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
3644 
3645  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
3646  if ( graphicElem.isNull() )
3647  return nullptr;
3648 
3649  QString name, format;
3650  QColor color;
3651  double size;
3652  int chr;
3653 
3654  if ( !QgsSymbolLayerUtils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
3655  return nullptr;
3656 
3657  if ( !name.startsWith( QLatin1String( "ttf://" ) ) || format != QLatin1String( "ttf" ) )
3658  return nullptr;
3659 
3660  const QString fontFamily = name.mid( 6 );
3661 
3662  double angle = 0.0;
3663  QString angleFunc;
3664  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
3665  {
3666  bool ok;
3667  const double d = angleFunc.toDouble( &ok );
3668  if ( ok )
3669  angle = d;
3670  }
3671 
3672  QPointF offset;
3674 
3675  const QString uom = element.attribute( QStringLiteral( "uom" ) );
3679 
3681  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
3682  m->setAngle( angle );
3683  m->setOffset( offset );
3684  return m;
3685 }
3686 
3687 void QgsFontMarkerSymbolLayer::resolveFonts( const QVariantMap &properties, const QgsReadWriteContext &context )
3688 {
3689  const QString fontFamily = properties.value( QStringLiteral( "font" ), DEFAULT_FONTMARKER_FONT ).toString();
3691  {
3692  context.pushMessage( QObject::tr( "Font “%1” not available on system" ).arg( fontFamily ) );
3693  }
3694 }
3695 
3697 {
3698  QMap<QString, QgsProperty>::iterator it = mParameters.begin();
3699  for ( ; it != mParameters.end(); ++it )
3700  it.value().prepare( context.renderContext().expressionContext() );
3701 
3703 }
3704 
3705 
3706 QSet<QString> QgsSvgMarkerSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
3707 {
3708  QSet<QString> attrs = QgsMarkerSymbolLayer::usedAttributes( context );
3709 
3710  QMap<QString, QgsProperty>::const_iterator it = mParameters.constBegin();
3711  for ( ; it != mParameters.constEnd(); ++it )
3712  {
3713  attrs.unite( it.value().referencedFields( context.expressionContext(), true ) );
3714  }
3715 
3716  return attrs;
3717 }
@ DynamicRotation
Rotation of symbol may be changed during rendering and symbol should not be cached.
ScaleMethod
Scale methods.
Definition: qgis.h:183
@ ScaleDiameter
Calculate scale by the diameter.
@ ScaleArea
Calculate scale by the area.
@ RenderSymbolPreview
The render is for a symbol preview only and map based properties may not be available,...
@ RenderBlocking
Render and load remote sources in the same thread to ensure rendering remote sources (svg and images)...
@ Fill
Fill symbol.
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.
static QgsImageCache * imageCache()
Returns the application's image cache, used for caching resampled versions of raster images.
static QgsSvgCache * svgCache()
Returns the application's SVG cache, used for caching SVG images and handling parameter replacement w...
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 writeCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius, const QString &lineStyleName, double width)
Write circle (as polyline)
void writeLine(const QgsPoint &pt1, const QgsPoint &pt2, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Write line (as a polyline)
static double mapUnitScaleFactor(double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits, double mapUnitsPerPixel=1.0)
Returns scale factor for conversion to map units.
QgsUnitTypes::DistanceUnit mapUnits() const
Retrieve map units.
void writePolygon(const QgsRingSequence &polygon, const QString &layer, const QString &hatchPattern, const QColor &color)
Draw dxf filled polygon (HATCH)
double symbologyScale() const
Returns the reference scale for output.
Definition: qgsdxfexport.h:229
void clipValueToMapUnitScale(double &value, const QgsMapUnitScale &scale, double pixelToMMFactor) const
Clips value to scale minimum/maximum.
void writePolyline(const QgsPointSequence &line, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Draw dxf primitives (LWPOLYLINE)
A paint device for drawing into dxf files.
void setShift(QPointF shift)
void setLayer(const QString &layer)
void setOutputSize(const QRectF &r)
void setDrawingSize(QSizeF size)
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
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:205
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
Definition: qgsfillsymbol.h:30
static QgsFillSymbol * createSimple(const QVariantMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties.
Filled marker symbol layer, consisting of a shape which is rendered using a QgsFillSymbol.
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QColor color() const override
The fill color.
QgsFilledMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QgsSymbol * subSymbol() override
Returns the symbol's sub symbol, if present.
~QgsFilledMarkerSymbolLayer() override
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer's subsymbol. takes ownership of the passed symbol.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
void setColor(const QColor &c) override
The fill color.
QString layerType() const override
Returns a string that represents this layer type.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsFilledMarkerSymbolLayer.
QgsFilledMarkerSymbolLayer(QgsSimpleMarkerSymbolLayerBase::Shape shape=Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructor for QgsFilledMarkerSymbolLayer.
~QgsFontMarkerSymbolLayer() override
void setStrokeColor(const QColor &color) override
Set stroke color.
QgsFontMarkerSymbolLayer(const QString &fontFamily=DEFAULT_FONTMARKER_FONT, QString chr=DEFAULT_FONTMARKER_CHR, double pointSize=DEFAULT_FONTMARKER_SIZE, const QColor &color=DEFAULT_FONTMARKER_COLOR, double angle=DEFAULT_FONTMARKER_ANGLE)
Constructs a font marker symbol layer.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
Sets the stroke width map unit scale.
double strokeWidth() const
Returns the marker's stroke width.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
void setFontStyle(const QString &style)
Sets the font style for the font which will be used to render the point.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QString fontStyle() const
Returns the font style for the associated font which will be used to render the point.
QString fontFamily() const
Returns the font family name for the associated font which will be used to render the point.
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit unit)
Sets the stroke width unit.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
void writeSldMarker(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Writes the symbol layer definition as a SLD XML element.
void setStrokeWidth(double width)
Set's the marker's stroke width.
static void resolveFonts(const QVariantMap &properties, const QgsReadWriteContext &context)
Resolves fonts from a properties map, raising warnings in the specified context if the required fonts...
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
void setPenJoinStyle(Qt::PenJoinStyle style)
Sets the stroke join style.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsFontMarkerSymbolLayer from an SLD XML element.
QgsFontMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
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.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsFontMarkerSymbolLayer from a property map (see properties())
static QString translateNamedStyle(const QString &namedStyle)
Returns the localized named style of a font, if such a translation is available.
static bool fontFamilyMatchOnSystem(const QString &family, QString *chosen=nullptr, bool *match=nullptr)
Check whether font family is on system.
static bool updateFontViaStyle(QFont &f, const QString &fontstyle, bool fallback=false)
Updates font with named style and retain all font properties.
QgsWkbTypes::GeometryType type
Definition: qgsgeometry.h:127
QSize originalSize(const QString &path, bool blocking=false) const
Returns the original size (in pixels) of the image at the specified path.
QImage pathAsImage(const QString &path, const QSize size, const bool keepAspectRatio, const double opacity, bool &fitsInCache, bool blocking=false, bool *isMissing=nullptr)
Returns the specified path rendered as an image.
static void adjustHueSaturation(QImage &image, double saturation, const QColor &colorizeColor=QColor(), double colorizeStrength=1.0, QgsFeedback *feedback=nullptr)
Alter the hue or saturation of a QImage.
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
double mapUnitsPerPixel() const
Returns the current map units per pixel.
double mapRotation() const
Returns the current map rotation in degrees (clockwise).
Struct for storing maximum and minimum scales for measurements in map units.
Abstract base class for marker symbol layers.
double mSize
Marker 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.
Qgis::ScaleMethod scaleMethod() const
Returns the method to use for scaling the marker's size.
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.
Qgis::ScaleMethod mScaleMethod
Marker size scaling method.
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
Resolves relative paths into absolute paths and vice versa.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
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.
static QVariantMap propertyMapToVariantMap(const QMap< QString, QgsProperty > &propertyMap)
Convert a map of QgsProperty to a map of QVariant This is useful to save a map of properties.
static QMap< QString, QgsProperty > variantMapToPropertyMap(const QVariantMap &variantMap)
Convert a map of QVariant to a map of QgsProperty This is useful to restore a map of properties.
Raster marker symbol layer class.
double mFixedAspectRatio
The marker fixed aspect ratio.
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.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsMapUnitScale mapUnitScale() const override
void setOpacity(double opacity)
Set the marker opacity.
QString path() const
Returns the marker raster image path.
double calculateAspectRatio(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio) const
Calculates the marker aspect ratio between width and height.
QgsRasterMarkerSymbolLayer(const QString &path=QString(), double size=DEFAULT_SVGMARKER_SIZE, double angle=DEFAULT_SVGMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructs raster marker symbol layer with picture from given absolute path to a raster image file.
void setPath(const QString &path)
Set the marker raster image path.
double defaultAspectRatio() const
Returns the default marker aspect ratio between width and height, 0 if not yet calculated.
void setFixedAspectRatio(double ratio)
Set the marker aspect ratio between width and height to be used in rendering, if the value set is low...
void setMapUnitScale(const QgsMapUnitScale &scale) override
QgsRasterMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
bool preservedAspectRatio() const
Returns the preserved aspect ratio value, true if fixed aspect ratio has been lower or equal to 0.
static void resolvePaths(QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving)
Turns relative paths in properties map to absolute when reading and vice versa when writing.
~QgsRasterMarkerSymbolLayer() override
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a raster marker symbol layer from a string map of properties.
double mOpacity
The marker default opacity.
double updateDefaultAspectRatio()
Calculates the default marker aspect ratio between width and height.
double mDefaultAspectRatio
The marker default aspect ratio.
bool setPreservedAspectRatio(bool par)
Set preserved the marker aspect ratio between width and height.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QString layerType() const override
Returns a string that represents this layer type.
double opacity() const
Returns the marker opacity.
The class is used as a container of context for various read/write operations on other objects.
void pushMessage(const QString &message, Qgis::MessageLevel level=Qgis::MessageLevel::Warning) const
Append a message to the context.
Contains information about the context of a rendering operation.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
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...
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
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).
bool forceVectorOutput() const
Returns true if rendering operations should use vector operations instead of any faster raster shortc...
QColor selectionColor() const
Returns the color to use when rendering selected features.
QgsFeedback * feedback() const
Returns the feedback object that can be queried regularly during rendering to check if rendering shou...
Qgis::RenderContextFlags flags() const
Returns combination of flags used for rendering.
const QgsPathResolver & pathResolver() const
Returns the path resolver for conversion between relative and absolute paths during rendering operati...
Scoped object for saving and restoring a QPainter object's state.
Abstract base class for simple marker symbol layers.
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
void calculateOffsetAndRotation(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle) const
Calculates the marker offset and rotation.
bool prepareMarkerShape(Shape shape)
Prepares the layer for drawing the specified shape (QPolygonF version)
QPainterPath mPath
Painter path representing shape. If mPolygon is empty then the shape is stored in mPath.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
static bool shapeIsFilled(QgsSimpleMarkerSymbolLayerBase::Shape shape)
Returns true if a symbol shape has a fill.
static QList< QgsSimpleMarkerSymbolLayerBase::Shape > availableShapes()
Returns a list of all available shape types.
QgsSimpleMarkerSymbolLayerBase::Shape shape() const
Returns the shape for the rendered marker symbol.
~QgsSimpleMarkerSymbolLayerBase() override
bool shapeToPolygon(Shape shape, QPolygonF &polygon) const
Creates a polygon representing the specified shape.
bool prepareMarkerPath(Shape symbol)
Prepares the layer for drawing the specified shape (QPainterPath version)
QPolygonF mPolygon
Polygon of points in shape. If polygon is empty then shape is using mPath.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
QgsSimpleMarkerSymbolLayerBase(QgsSimpleMarkerSymbolLayerBase::Shape shape=Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructor for QgsSimpleMarkerSymbolLayerBase.
static QgsSimpleMarkerSymbolLayerBase::Shape decodeShape(const QString &name, bool *ok=nullptr)
Attempts to decode a string representation of a shape name to the corresponding shape.
static QString encodeShape(QgsSimpleMarkerSymbolLayerBase::Shape shape)
Encodes a shape to its string representation.
double calculateSize(QgsSymbolRenderContext &context, bool &hasDataDefinedSize) const
Calculates the desired size of the marker, considering data defined size overrides.
@ ArrowHead
Right facing arrow head (unfilled, lines only)
@ QuarterArc
A line-only one quarter arc (since QGIS 3.20)
@ Octagon
Octagon (since QGIS 3.18)
@ ThirdCircle
One third circle (top left third)
@ RightHalfTriangle
Right half of triangle.
@ SquareWithCorners
A square with diagonal corners (since QGIS 3.18)
@ LeftHalfTriangle
Left half of triangle.
@ QuarterSquare
Quarter square (top left quarter)
@ ArrowHeadFilled
Right facing filled arrow head.
@ Cross2
Rotated cross (lines only), "x" shape.
@ EquilateralTriangle
Equilateral triangle.
@ HalfSquare
Half square (left half)
@ ThirdArc
A line-only one third arc (since QGIS 3.20)
@ QuarterCircle
Quarter circle (top left quarter)
@ SemiCircle
Semi circle (top half)
@ DiagonalHalfSquare
Diagonal half square (bottom left half)
@ AsteriskFill
A filled asterisk shape (since QGIS 3.18)
@ HalfArc
A line-only half arc (since QGIS 3.20)
Simple marker symbol layer, consisting of a rendered shape with solid fill color and an stroke.
QPen mSelPen
QPen to use as stroke of selected symbols.
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit u)
Sets the unit for the width of the marker's stroke.
void setColor(const QColor &color) override
The fill color.
QColor mStrokeColor
Stroke color.
QImage mSelCache
Cached image of selected marker, if using cached version.
QImage mCache
Cached image of marker, if using cached version.
QBrush mSelBrush
QBrush to use as fill of selected symbols.
void setFillColor(const QColor &color) override
Set fill color.
Qt::PenJoinStyle penJoinStyle() const
Returns the marker's stroke join style (e.g., miter, bevel, etc).
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
void drawMarker(QPainter *p, QgsSymbolRenderContext &context)
Draws the marker shape in the specified painter.
QPen mPen
QPen corresponding to marker's stroke style.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsSimpleMarkerSymbolLayer.
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 setMapUnitScale(const QgsMapUnitScale &scale) override
QColor color() const override
The fill color.
QgsMapUnitScale mapUnitScale() const override
Qt::PenStyle mStrokeStyle
Stroke style.
QgsSimpleMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsSimpleMarkerSymbolLayer from an SLD XML element.
QgsSimpleMarkerSymbolLayer(QgsSimpleMarkerSymbolLayerBase::Shape shape=Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD, const QColor &color=DEFAULT_SIMPLEMARKER_COLOR, const QColor &strokeColor=DEFAULT_SIMPLEMARKER_BORDERCOLOR, Qt::PenJoinStyle penJoinStyle=DEFAULT_SIMPLEMARKER_JOINSTYLE)
Constructor for QgsSimpleMarkerSymbolLayer.
~QgsSimpleMarkerSymbolLayer() override
Qt::PenCapStyle mPenCapStyle
Stroke pen cap style.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QString layerType() const override
Returns a string that represents this layer type.
double mStrokeWidth
Stroke width.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
Sets the map scale for the width of the marker's stroke.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
void setStrokeStyle(Qt::PenStyle strokeStyle)
Sets the marker's stroke style (e.g., solid, dashed, etc)
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QColor fillColor() const override
Gets fill color.
QColor strokeColor() const override
Returns the marker's stroke color.
QBrush mBrush
QBrush corresponding to marker's fill style.
void setStrokeWidth(double w)
Sets the width of the marker's stroke.
void setStrokeColor(const QColor &color) override
Sets the marker's stroke color.
Qt::PenStyle strokeStyle() const
Returns the marker's stroke style (e.g., solid, dashed, etc)
QgsUnitTypes::RenderUnit mStrokeWidthUnit
Stroke width units.
bool mUsingCache
true if using cached images of markers for drawing.
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 writeSldMarker(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Writes the symbol layer definition as a SLD XML element.
static const int MAXIMUM_CACHE_WIDTH
Maximum width/height of cache image.
QString ogrFeatureStyle(double mmScaleFactor, double mapUnitScaleFactor) const override
bool prepareCache(QgsSymbolRenderContext &context)
Prepares cache image.
QgsMapUnitScale mStrokeWidthMapUnitScale
Stroke width map unit scale.
double strokeWidth() const
Returns the width of the marker's stroke.
Qt::PenJoinStyle mPenJoinStyle
Stroke pen join style.
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
QSizeF svgViewboxSize(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >())
Calculates the viewbox size of a (possibly cached) SVG file.
QPicture svgAsPicture(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool forceVectorOutput=false, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >())
Returns an SVG drawing as a QPicture.
void containsParams(const QString &path, bool &hasFillParam, QColor &defaultFillColor, bool &hasStrokeParam, QColor &defaultStrokeColor, bool &hasStrokeWidthParam, double &defaultStrokeWidth, bool blocking=false) const
Tests if an SVG file contains parameters for fill, stroke color, stroke width.
QImage svgAsImage(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool &fitsInCache, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >())
Returns an SVG drawing as a QImage.
QByteArray svgContent(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >(), bool *isMissingImage=nullptr)
Gets the SVG content corresponding to the given path.
QgsSvgMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
QColor fillColor() const override
Gets fill color.
QgsMapUnitScale mapUnitScale() const override
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates the symbol.
double mDefaultAspectRatio
The marker default aspect ratio.
QgsUnitTypes::RenderUnit mStrokeWidthUnit
QString layerType() const override
Returns a string that represents this layer type.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QString path() const
Returns the marker SVG path.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
bool preservedAspectRatio() const
Returns the preserved aspect ratio value, true if fixed aspect ratio has been lower or equal to 0.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
void prepareExpressions(const QgsSymbolRenderContext &context) override
Prepares all data defined property expressions for evaluation.
QMap< QString, QgsProperty > mParameters
bool setPreservedAspectRatio(bool par)
Set preserved the marker aspect ratio between width and height.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the stroke width.
double calculateAspectRatio(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio) const
Calculates the marker aspect ratio between width and height.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
static QgsSymbolLayer * createFromSld(QDomElement &element)
void setStrokeColor(const QColor &c) override
Set stroke color.
void setMapUnitScale(const QgsMapUnitScale &scale) override
double updateDefaultAspectRatio()
Calculates the default marker aspect ratio between width and height.
QColor strokeColor() const override
Gets stroke color.
void setFillColor(const QColor &color) override
Set fill color.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
QMap< QString, QgsProperty > parameters() const
Returns the dynamic SVG parameters.
QgsSvgMarkerSymbolLayer(const QString &path, double size=DEFAULT_SVGMARKER_SIZE, double angle=DEFAULT_SVGMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructs SVG marker symbol layer with picture from given absolute path to a SVG file.
void writeSldMarker(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Writes the symbol layer definition as a SLD XML element.
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
~QgsSvgMarkerSymbolLayer() override
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
static void resolvePaths(QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving)
Turns relative paths in properties map to absolute when reading and vice versa when writing.
QgsMapUnitScale mStrokeWidthMapUnitScale
void setParameters(const QMap< QString, QgsProperty > &parameters)
Sets the dynamic SVG parameters.
double mFixedAspectRatio
The marker fixed aspect ratio.
bool writeDxf(QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift=QPointF(0.0, 0.0)) const override
write as DXF
void setFixedAspectRatio(double ratio)
Set the marker aspect ratio between width and height to be used in rendering, if the value set is low...
void setPath(const QString &path)
Set the marker SVG path.
static bool externalMarkerFromSld(QDomElement &element, QString &path, QString &format, int &markIndex, QColor &color, double &size)
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 QgsStringMap evaluatePropertiesMap(const QMap< QString, QgsProperty > &propertiesMap, const QgsExpressionContext &context)
Evaluates a map of properties using the given context and returns a variant map with evaluated expres...
static bool displacementFromSldElement(QDomElement &element, QPointF &offset)
static QColor decodeColor(const QString &str)
static QString svgSymbolPathToName(const QString &path, const QgsPathResolver &pathResolver)
Determines an SVG symbol's name from its path.
static QPointF toPoint(const QVariant &value, bool *ok=nullptr)
Converts a value to a point.
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 void multiplyImageOpacity(QImage *image, qreal opacity)
Multiplies opacity of image pixel values with a (global) transparency value.
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
static Qt::PenCapStyle decodePenCapStyle(const QString &str)
static bool externalGraphicFromSld(QDomElement &element, QString &path, QString &mime, QColor &color, double &size)
static void parametricSvgToSld(QDomDocument &doc, QDomElement &graphicElem, const QString &path, const QColor &fillColor, double size, const QColor &strokeColor, double strokeWidth)
Encodes a reference to a parametric SVG into SLD, as a succession of parametric SVG using URL paramet...
static Qgis::ScaleMethod decodeScaleMethod(const QString &str)
Decodes a symbol scale method from a string.
static QString encodePenCapStyle(Qt::PenCapStyle style)
static void externalMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &path, const QString &format, int *markIndex=nullptr, const QColor &color=QColor(), double size=-1)
static double sizeInPixelsFromSldUom(const QString &uom, double size)
Returns the size scaled in pixels according to the uom attribute.
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 svgSymbolNameToPath(const QString &name, const QgsPathResolver &pathResolver)
Determines an SVG symbol's path from its name.
static QString encodeColor(const QColor &color)
static double estimateMaxSymbolBleed(QgsSymbol *symbol, const QgsRenderContext &context)
Returns the maximum estimated bleed for the symbol.
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 QString encodeScaleMethod(Qgis::ScaleMethod scaleMethod)
Encodes a symbol scale method to a string.
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 QPointF decodePoint(const QString &string)
Decodes a QSizeF from a string.
@ PropertyStrokeStyle
Stroke style (eg solid, dashed)
@ PropertyCapStyle
Line cap style.
@ PropertyAngle
Symbol angle.
@ PropertySize
Symbol size.
@ PropertyJoinStyle
Line join style.
@ PropertyOpacity
Opacity.
@ PropertyCharacter
Character, eg for font marker symbol layers.
@ PropertyOffset
Symbol offset.
@ PropertyStrokeWidth
Stroke width.
@ PropertyFillColor
Fill color.
@ PropertyFontStyle
Font style.
@ PropertyHeight
Symbol height.
@ PropertyFontFamily
Font family.
@ 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.
virtual QColor color() const
The fill color.
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns the set of attributes referenced by the layer.
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 prepareExpressions(const QgsSymbolRenderContext &context)
Prepares all data defined property expressions for evaluation.
virtual void setColor(const QColor &color)
The fill color.
void copyPaintEffect(QgsSymbolLayer *destLayer) const
Copies paint effect of this layer to another symbol layer.
QgsPropertyCollection mDataDefinedProperties
virtual bool hasDataDefinedProperties() const
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
QgsFields fields() const
Fields of the layer.
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.
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:38
Qgis::SymbolType type() const
Returns the symbol's type.
Definition: qgssymbol.h:97
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
@ RenderPercentage
Percentage of another measurement (e.g., canvas size, feature size)
Definition: qgsunittypes.h:172
@ RenderMillimeters
Millimeters.
Definition: qgsunittypes.h:169
@ RenderMapUnits
Map units.
Definition: qgsunittypes.h:170
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
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:1234
QMap< QString, QString > QgsStringMap
Definition: qgis.h:1691
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
#define DEG2RAD(x)
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
Q_GUI_EXPORT int qt_defaultDpiX()
Q_GUI_EXPORT int qt_defaultDpiY()
#define DEFAULT_FONTMARKER_JOINSTYLE
#define DEFAULT_RASTERMARKER_ANGLE
#define DEFAULT_RASTERMARKER_SIZE
#define DEFAULT_SVGMARKER_ANGLE
#define DEFAULT_SIMPLEMARKER_JOINSTYLE
#define DEFAULT_FONTMARKER_CHR
#define DEFAULT_SIMPLEMARKER_BORDERCOLOR
#define DEFAULT_SIMPLEMARKER_SIZE
#define DEFAULT_SIMPLEMARKER_NAME
#define DEFAULT_SIMPLEMARKER_ANGLE
#define DEFAULT_SVGMARKER_SIZE
#define DEFAULT_FONTMARKER_FONT
#define DEFAULT_FONTMARKER_BORDERCOLOR
#define DEFAULT_FONTMARKER_ANGLE
#define DEFAULT_FONTMARKER_COLOR
#define DEFAULT_FONTMARKER_SIZE
#define DEFAULT_SIMPLEMARKER_COLOR
#define DEFAULT_SCALE_METHOD