QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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  const bool prevIsSubsymbol = context.renderContext().flags() & Qgis::RenderContextFlag::RenderingSubSymbol;
1918 
1919  if ( !polygon.isEmpty() )
1920  {
1921  mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
1922  }
1923  else
1924  {
1925  const QPolygonF poly = path.toFillPolygon();
1926  mFill->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
1927  }
1928 
1930 
1931  mFill->setOpacity( prevOpacity );
1932 }
1933 
1934 
1936 
1937 
1938 QgsSvgMarkerSymbolLayer::QgsSvgMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
1939 {
1940  mSize = size;
1941  mAngle = angle;
1942  mOffset = QPointF( 0, 0 );
1944  mStrokeWidth = 0.2;
1946  mColor = QColor( 35, 35, 35 );
1947  mStrokeColor = QColor( 35, 35, 35 );
1948  setPath( path );
1949 }
1950 
1952 
1954 {
1955  QString name;
1956  double size = DEFAULT_SVGMARKER_SIZE;
1957  double angle = DEFAULT_SVGMARKER_ANGLE;
1959 
1960  if ( props.contains( QStringLiteral( "name" ) ) )
1961  name = props[QStringLiteral( "name" )].toString();
1962  if ( props.contains( QStringLiteral( "size" ) ) )
1963  size = props[QStringLiteral( "size" )].toDouble();
1964  if ( props.contains( QStringLiteral( "angle" ) ) )
1965  angle = props[QStringLiteral( "angle" )].toDouble();
1966  if ( props.contains( QStringLiteral( "scale_method" ) ) )
1967  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1968 
1970 
1971  if ( props.contains( QStringLiteral( "size_unit" ) ) )
1972  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1973  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1974  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1975  if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
1976  m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
1977  if ( props.contains( QStringLiteral( "offset" ) ) )
1978  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1979  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1980  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1981  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1982  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1983  if ( props.contains( QStringLiteral( "fill" ) ) )
1984  {
1985  //pre 2.5 projects used "fill"
1986  m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "fill" )].toString() ) );
1987  }
1988  else if ( props.contains( QStringLiteral( "color" ) ) )
1989  {
1990  m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )].toString() ) );
1991  }
1992  if ( props.contains( QStringLiteral( "outline" ) ) )
1993  {
1994  //pre 2.5 projects used "outline"
1995  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline" )].toString() ) );
1996  }
1997  else if ( props.contains( QStringLiteral( "outline_color" ) ) )
1998  {
1999  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )].toString() ) );
2000  }
2001  else if ( props.contains( QStringLiteral( "line_color" ) ) )
2002  {
2003  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )].toString() ) );
2004  }
2005 
2006  if ( props.contains( QStringLiteral( "outline-width" ) ) )
2007  {
2008  //pre 2.5 projects used "outline-width"
2009  m->setStrokeWidth( props[QStringLiteral( "outline-width" )].toDouble() );
2010  }
2011  else if ( props.contains( QStringLiteral( "outline_width" ) ) )
2012  {
2013  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
2014  }
2015  else if ( props.contains( QStringLiteral( "line_width" ) ) )
2016  {
2017  m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
2018  }
2019 
2020  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
2021  {
2022  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
2023  }
2024  else if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
2025  {
2026  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
2027  }
2028  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
2029  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
2030 
2031  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2032  {
2033  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2034  }
2035  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2036  {
2037  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2038  }
2039 
2040  m->restoreOldDataDefinedProperties( props );
2041 
2043 
2044  if ( props.contains( QStringLiteral( "parameters" ) ) )
2045  {
2046  const QVariantMap parameters = props[QStringLiteral( "parameters" )].toMap();
2048  }
2049 
2050  return m;
2051 }
2052 
2053 void QgsSvgMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
2054 {
2055  const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
2056  if ( it != properties.end() )
2057  {
2058  if ( saving )
2059  {
2060  it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
2061  }
2062  else
2063  {
2064  it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
2065  }
2066  }
2067 }
2068 
2069 void QgsSvgMarkerSymbolLayer::setPath( const QString &path )
2070 {
2071  mDefaultAspectRatio = 0;
2072  mHasFillParam = false;
2073  mPath = path;
2074  QColor defaultFillColor, defaultStrokeColor;
2075  double strokeWidth, fillOpacity, strokeOpacity;
2076  bool hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
2077  bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
2078  QgsApplication::svgCache()->containsParams( path, mHasFillParam, hasDefaultFillColor, defaultFillColor,
2079  hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
2080  hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2081  hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
2082  hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
2083 
2084  const double newFillOpacity = hasFillOpacityParam ? fillColor().alphaF() : 1.0;
2085  const double newStrokeOpacity = hasStrokeOpacityParam ? strokeColor().alphaF() : 1.0;
2086 
2087  if ( hasDefaultFillColor )
2088  {
2089  defaultFillColor.setAlphaF( newFillOpacity );
2090  setFillColor( defaultFillColor );
2091  }
2092  if ( hasDefaultFillOpacity )
2093  {
2094  QColor c = fillColor();
2095  c.setAlphaF( fillOpacity );
2096  setFillColor( c );
2097  }
2098  if ( hasDefaultStrokeColor )
2099  {
2100  defaultStrokeColor.setAlphaF( newStrokeOpacity );
2101  setStrokeColor( defaultStrokeColor );
2102  }
2103  if ( hasDefaultStrokeWidth )
2104  {
2106  }
2107  if ( hasDefaultStrokeOpacity )
2108  {
2109  QColor c = strokeColor();
2110  c.setAlphaF( strokeOpacity );
2111  setStrokeColor( c );
2112  }
2113 
2115 }
2116 
2118 {
2119  if ( mDefaultAspectRatio == 0.0 )
2120  {
2121  //size
2122  const double size = mSize;
2123  //assume 88 dpi as standard value
2124  const double widthScaleFactor = 3.465;
2125  const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( mPath, size, mColor, mStrokeColor, mStrokeWidth, widthScaleFactor );
2126  // set default aspect ratio
2127  mDefaultAspectRatio = svgViewbox.isValid() ? svgViewbox.height() / svgViewbox.width() : 0.0;
2128  }
2129  return mDefaultAspectRatio;
2130 }
2131 
2133 {
2134  const bool aPreservedAspectRatio = preservedAspectRatio();
2135  if ( aPreservedAspectRatio && !par )
2136  {
2138  }
2139  else if ( !aPreservedAspectRatio && par )
2140  {
2141  mFixedAspectRatio = 0.0;
2142  }
2143  return preservedAspectRatio();
2144 }
2145 
2146 void QgsSvgMarkerSymbolLayer::setParameters( const QMap<QString, QgsProperty> &parameters )
2147 {
2149 }
2150 
2151 
2153 {
2154  return QStringLiteral( "SvgMarker" );
2155 }
2156 
2158 {
2159  QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
2160  Q_UNUSED( context )
2161 }
2162 
2164 {
2165  Q_UNUSED( context )
2166 }
2167 
2169 {
2170  QPainter *p = context.renderContext().painter();
2171  if ( !p )
2172  return;
2173 
2174  bool hasDataDefinedSize = false;
2175  const double scaledWidth = calculateSize( context, hasDataDefinedSize );
2176  const double width = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2177 
2178  //don't render symbols with a width below one or above 10,000 pixels
2179  if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2180  {
2181  return;
2182  }
2183 
2184  const QgsScopedQPainterState painterState( p );
2185 
2186  bool hasDataDefinedAspectRatio = false;
2187  const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2188  double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2189 
2191 
2192  double strokeWidth = mStrokeWidth;
2194  {
2197  }
2199 
2200  QColor fillColor = mColor;
2201  if ( context.selected() && mHasFillParam )
2202  {
2203  fillColor = context.renderContext().selectionColor();
2204  }
2206  {
2209  }
2210 
2211  QColor strokeColor = mStrokeColor;
2213  {
2216  }
2217 
2218  QString path = mPath;
2220  {
2221  context.setOriginalValueVariable( mPath );
2223  context.renderContext().pathResolver() );
2225  {
2226  // adjust height of data defined path
2227  const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2228  context.renderContext().scaleFactor(), aspectRatio,
2229  ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2230  scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2231  }
2232  }
2233 
2234  QPointF outputOffset;
2235  double angle = 0.0;
2236  calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2237 
2238  p->translate( point + outputOffset );
2239 
2240  const bool rotated = !qgsDoubleNear( angle, 0 );
2241  if ( rotated )
2242  p->rotate( angle );
2243 
2244  bool fitsInCache = true;
2245  bool usePict = true;
2246  const bool rasterizeSelected = !mHasFillParam || mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyName );
2247  if ( ( !context.renderContext().forceVectorOutput() && !rotated ) || ( context.selected() && rasterizeSelected ) )
2248  {
2250  context.renderContext().scaleFactor(), fitsInCache, aspectRatio,
2251  ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2252  if ( fitsInCache && img.width() > 1 )
2253  {
2254  usePict = false;
2255 
2256  if ( context.selected() )
2258 
2259  //consider transparency
2260  if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2261  {
2262  QImage transparentImage = img.copy();
2263  QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2264  p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
2265  }
2266  else
2267  {
2268  p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2269  }
2270  }
2271  }
2272 
2273  if ( usePict || !fitsInCache )
2274  {
2275  p->setOpacity( context.opacity() );
2276  const QPicture pct = QgsApplication::svgCache()->svgAsPicture( path, width, fillColor, strokeColor, strokeWidth,
2277  context.renderContext().scaleFactor(), context.renderContext().forceVectorOutput(), aspectRatio,
2278  ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2279  if ( pct.width() > 1 )
2280  {
2281  const QgsScopedQPainterState painterPictureState( p );
2282  _fixQPictureDPI( p );
2283  p->drawPicture( 0, 0, pct );
2284  }
2285  }
2286 
2287  // workaround issue with nested QPictures forgetting antialiasing flag - see https://github.com/qgis/QGIS/issues/22909
2289 }
2290 
2291 double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2292 {
2293  double scaledSize = mSize;
2295 
2296  bool ok = true;
2297  if ( hasDataDefinedSize )
2298  {
2299  context.setOriginalValueVariable( mSize );
2301  }
2302  else
2303  {
2305  if ( hasDataDefinedSize )
2306  {
2307  context.setOriginalValueVariable( mSize );
2309  }
2310  }
2311 
2312  if ( hasDataDefinedSize && ok )
2313  {
2314  switch ( mScaleMethod )
2315  {
2317  scaledSize = std::sqrt( scaledSize );
2318  break;
2320  break;
2321  }
2322  }
2323 
2324  return scaledSize;
2325 }
2326 
2327 double QgsSvgMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2328 {
2330  if ( !hasDataDefinedAspectRatio )
2331  return mFixedAspectRatio;
2332 
2334  return 0.0;
2335 
2336  double scaledAspectRatio = mDefaultAspectRatio;
2337  if ( mFixedAspectRatio > 0.0 )
2338  scaledAspectRatio = mFixedAspectRatio;
2339 
2340  const double defaultHeight = mSize * scaledAspectRatio;
2341  scaledAspectRatio = defaultHeight / scaledSize;
2342 
2343  bool ok = true;
2344  double scaledHeight = scaledSize * scaledAspectRatio;
2346  {
2347  context.setOriginalValueVariable( defaultHeight );
2348  scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyHeight, context.renderContext().expressionContext(), defaultHeight, &ok );
2349  }
2350 
2351  if ( hasDataDefinedAspectRatio && ok )
2352  {
2353  switch ( mScaleMethod )
2354  {
2356  scaledHeight = sqrt( scaledHeight );
2357  break;
2359  break;
2360  }
2361  }
2362 
2363  scaledAspectRatio = scaledHeight / scaledSize;
2364 
2365  return scaledAspectRatio;
2366 }
2367 
2368 void QgsSvgMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
2369 {
2370  //offset
2371  double offsetX = 0;
2372  double offsetY = 0;
2373  markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
2374  offset = QPointF( offsetX, offsetY );
2375 
2376  angle = mAngle + mLineAngle;
2378  {
2379  context.setOriginalValueVariable( mAngle );
2381  }
2382 
2384  if ( hasDataDefinedRotation )
2385  {
2386  // For non-point markers, "dataDefinedRotation" means following the
2387  // shape (shape-data defined). For them, "field-data defined" does
2388  // not work at all. TODO: if "field-data defined" ever gets implemented
2389  // we'll need a way to distinguish here between the two, possibly
2390  // using another flag in renderHints()
2391  const QgsFeature *f = context.feature();
2392  if ( f )
2393  {
2394  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
2395  {
2396  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2397  angle += m2p.mapRotation();
2398  }
2399  }
2400  }
2401 
2402  if ( angle )
2404 }
2405 
2406 
2408 {
2409  QVariantMap map;
2410  map[QStringLiteral( "name" )] = mPath;
2411  map[QStringLiteral( "size" )] = QString::number( mSize );
2412  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2413  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2414  map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2415  map[QStringLiteral( "angle" )] = QString::number( mAngle );
2416  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2417  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2418  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2419  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2420  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
2421  map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
2422  map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
2423  map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2424  map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2425  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
2426  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
2427 
2428  map[QStringLiteral( "parameters" )] = QgsProperty::propertyMapToVariantMap( mParameters );
2429 
2430  return map;
2431 }
2432 
2434 {
2438 }
2439 
2441 {
2444  m->setColor( mColor );
2449  m->setOffset( mOffset );
2450  m->setOffsetUnit( mOffsetUnit );
2452  m->setSizeUnit( mSizeUnit );
2456  m->setParameters( mParameters );
2457 
2459  copyPaintEffect( m );
2460  return m;
2461 }
2462 
2464 {
2466  mStrokeWidthUnit = unit;
2467 }
2468 
2470 {
2472  if ( unit != mStrokeWidthUnit )
2473  {
2475  }
2476  return unit;
2477 }
2478 
2480 {
2482  mStrokeWidthMapUnitScale = scale;
2483 }
2484 
2486 {
2488  {
2489  return mStrokeWidthMapUnitScale;
2490  }
2491  return QgsMapUnitScale();
2492 }
2493 
2494 void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
2495 {
2496  // <Graphic>
2497  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2498  element.appendChild( graphicElem );
2499 
2500  // encode a parametric SVG reference
2501  const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
2504 
2505  // <Rotation>
2506  QString angleFunc;
2507  bool ok;
2508  const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2509  if ( !ok )
2510  {
2511  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
2512  }
2513  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2514  {
2515  angleFunc = QString::number( angle + mAngle );
2516  }
2517 
2518  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2519 
2520  // <Displacement>
2521  const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2523 }
2524 
2526 {
2527  QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
2528 
2529  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
2530  if ( graphicElem.isNull() )
2531  return nullptr;
2532 
2533  QString path, mimeType;
2534  QColor fillColor;
2535  double size;
2536 
2537  if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
2538  return nullptr;
2539 
2540  const QString uom = element.attribute( QStringLiteral( "uom" ) );
2542 
2543  if ( mimeType != QLatin1String( "image/svg+xml" ) )
2544  return nullptr;
2545 
2546  double angle = 0.0;
2547  QString angleFunc;
2548  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2549  {
2550  bool ok;
2551  const double d = angleFunc.toDouble( &ok );
2552  if ( ok )
2553  angle = d;
2554  }
2555 
2556  QPointF offset;
2558 
2560  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
2561  m->setFillColor( fillColor );
2562  //m->setStrokeColor( strokeColor );
2563  //m->setStrokeWidth( strokeWidth );
2564  m->setAngle( angle );
2565  m->setOffset( offset );
2566  return m;
2567 }
2568 
2569 bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
2570 {
2571  //size
2572  double size = mSize;
2573 
2574  const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
2575 
2576  bool ok = true;
2577  if ( hasDataDefinedSize )
2578  {
2579  context.setOriginalValueVariable( mSize );
2581  }
2582 
2583  if ( hasDataDefinedSize && ok )
2584  {
2585  switch ( mScaleMethod )
2586  {
2588  size = std::sqrt( size );
2589  break;
2591  break;
2592  }
2593  }
2594 
2596  {
2597  size *= mmMapUnitScaleFactor;
2598  }
2599 
2600  //offset, angle
2601  QPointF offset = mOffset;
2602 
2604  {
2606  const QVariant val = mDataDefinedProperties.value( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext(), QString() );
2607  const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
2608  if ( ok )
2609  offset = res;
2610  }
2611  const double offsetX = offset.x();
2612  const double offsetY = offset.y();
2613 
2614  QPointF outputOffset( offsetX, offsetY );
2615 
2616  double angle = mAngle + mLineAngle;
2618  {
2619  context.setOriginalValueVariable( mAngle );
2621  }
2622 
2623  if ( angle )
2624  outputOffset = _rotatedOffset( outputOffset, angle );
2625 
2626  outputOffset *= e.mapUnitScaleFactor( e.symbologyScale(), mOffsetUnit, e.mapUnits(), context.renderContext().mapToPixel().mapUnitsPerPixel() );
2627 
2628  QString path = mPath;
2630  {
2631  context.setOriginalValueVariable( mPath );
2633  context.renderContext().pathResolver() );
2634  }
2635 
2636  double strokeWidth = mStrokeWidth;
2638  {
2641  }
2643 
2644  QColor fillColor = mColor;
2646  {
2649  }
2650 
2651  QColor strokeColor = mStrokeColor;
2653  {
2656  }
2657 
2659 
2660  const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, size, fillColor, strokeColor, strokeWidth,
2662  ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2663 
2664  QSvgRenderer r( svgContent );
2665  if ( !r.isValid() )
2666  return false;
2667 
2668  QgsDxfPaintDevice pd( &e );
2669  pd.setDrawingSize( QSizeF( r.defaultSize() ) );
2670 
2671  QSizeF outSize( r.defaultSize() );
2672  outSize.scale( size, size, Qt::KeepAspectRatio );
2673 
2674  QPainter p;
2675  p.begin( &pd );
2676  if ( !qgsDoubleNear( angle, 0.0 ) )
2677  {
2678  p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
2679  p.rotate( angle );
2680  p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
2681  }
2682  pd.setShift( shift + QPointF( outputOffset.x(), -outputOffset.y() ) );
2683  pd.setOutputSize( QRectF( -outSize.width() / 2.0, -outSize.height() / 2.0, outSize.width(), outSize.height() ) );
2684  pd.setLayer( layerName );
2685  r.render( &p );
2686  p.end();
2687  return true;
2688 }
2689 
2691 {
2692  bool hasDataDefinedSize = false;
2693  double scaledWidth = calculateSize( context, hasDataDefinedSize );
2694 
2695  bool hasDataDefinedAspectRatio = false;
2696  const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2697  double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2698 
2699  scaledWidth = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2700  scaledHeight = context.renderContext().convertToPainterUnits( scaledHeight, mSizeUnit, mSizeMapUnitScale );
2701 
2702  //don't render symbols with size below one or above 10,000 pixels
2703  if ( static_cast< int >( scaledWidth ) < 1 || 10000.0 < scaledWidth )
2704  {
2705  return QRectF();
2706  }
2707 
2708  QPointF outputOffset;
2709  double angle = 0.0;
2710  calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2711 
2712  double strokeWidth = mStrokeWidth;
2714  {
2717  }
2719 
2720  QString path = mPath;
2722  {
2723  context.setOriginalValueVariable( mPath );
2725  context.renderContext().pathResolver() );
2727  {
2728  // need to get colors to take advantage of cached SVGs
2729  QColor fillColor = mColor;
2731  {
2734  }
2735 
2736  const QColor strokeColor = mStrokeColor;
2738  {
2741  }
2742 
2744 
2745  // adjust height of data defined path
2746  const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2747  context.renderContext().scaleFactor(), aspectRatio,
2748  ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2749  scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2750  }
2751  }
2752 
2753  QTransform transform;
2754  // move to the desired position
2755  transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
2756 
2757  if ( !qgsDoubleNear( angle, 0.0 ) )
2758  transform.rotate( angle );
2759 
2760  //antialiasing
2761  strokeWidth += 1.0 / 2.0;
2762 
2763  QRectF symbolBounds = transform.mapRect( QRectF( -scaledWidth / 2.0,
2764  -scaledHeight / 2.0,
2765  scaledWidth,
2766  scaledHeight ) );
2767 
2768  //extend bounds by pen width / 2.0
2769  symbolBounds.adjust( -strokeWidth / 2.0, -strokeWidth / 2.0,
2770  strokeWidth / 2.0, strokeWidth / 2.0 );
2771 
2772  return symbolBounds;
2773 }
2774 
2776 
2777 QgsRasterMarkerSymbolLayer::QgsRasterMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
2778  : mPath( path )
2779 {
2780  mSize = size;
2781  mAngle = angle;
2782  mOffset = QPointF( 0, 0 );
2785 }
2786 
2788 
2790 {
2791  QString path;
2795 
2796  if ( props.contains( QStringLiteral( "imageFile" ) ) )
2797  path = props[QStringLiteral( "imageFile" )].toString();
2798  if ( props.contains( QStringLiteral( "size" ) ) )
2799  size = props[QStringLiteral( "size" )].toDouble();
2800  if ( props.contains( QStringLiteral( "angle" ) ) )
2801  angle = props[QStringLiteral( "angle" )].toDouble();
2802  if ( props.contains( QStringLiteral( "scale_method" ) ) )
2803  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
2804 
2806 
2807  if ( props.contains( QStringLiteral( "alpha" ) ) )
2808  {
2809  m->setOpacity( props[QStringLiteral( "alpha" )].toDouble() );
2810  }
2811 
2812  if ( props.contains( QStringLiteral( "size_unit" ) ) )
2813  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
2814  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
2815  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
2816  if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
2817  m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
2818 
2819  if ( props.contains( QStringLiteral( "offset" ) ) )
2820  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
2821  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
2822  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
2823  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2824  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
2825 
2826  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2827  {
2828  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2829  }
2830  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2831  {
2832  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2833  }
2834 
2835  m->restoreOldDataDefinedProperties( props );
2837 
2838  return m;
2839 }
2840 
2841 void QgsRasterMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
2842 {
2843  const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
2844  if ( it != properties.end() && it.value().type() == QVariant::String )
2845  {
2846  if ( saving )
2847  it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
2848  else
2849  it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
2850  }
2851 }
2852 
2853 void QgsRasterMarkerSymbolLayer::setPath( const QString &path )
2854 {
2855  mPath = path;
2857 }
2858 
2860 {
2861  const bool aPreservedAspectRatio = preservedAspectRatio();
2862  if ( aPreservedAspectRatio && !par )
2863  {
2865  }
2866  else if ( !aPreservedAspectRatio && par )
2867  {
2868  mFixedAspectRatio = 0.0;
2869  }
2870  return preservedAspectRatio();
2871 }
2872 
2874 {
2875  if ( mDefaultAspectRatio == 0.0 )
2876  {
2877  const QSize size = QgsApplication::imageCache()->originalSize( mPath );
2878  mDefaultAspectRatio = ( !size.isNull() && size.isValid() && size.width() > 0 ) ? static_cast< double >( size.height() ) / static_cast< double >( size.width() ) : 0.0;
2879  }
2880  return mDefaultAspectRatio;
2881 }
2882 
2884 {
2885  return QStringLiteral( "RasterMarker" );
2886 }
2887 
2889 {
2890  QPainter *p = context.renderContext().painter();
2891  if ( !p )
2892  return;
2893 
2894  QString path = mPath;
2896  {
2897  context.setOriginalValueVariable( mPath );
2899  }
2900 
2901  if ( path.isEmpty() )
2902  return;
2903 
2904  double width = 0.0;
2905  double height = 0.0;
2906 
2907  bool hasDataDefinedSize = false;
2908  const double scaledSize = calculateSize( context, hasDataDefinedSize );
2909 
2910  bool hasDataDefinedAspectRatio = false;
2911  const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
2912 
2913  QPointF outputOffset;
2914  double angle = 0.0;
2915 
2916  // RenderPercentage Unit Type takes original image size
2918  {
2919  const QSize size = QgsApplication::imageCache()->originalSize( path );
2920  if ( size.isEmpty() )
2921  return;
2922 
2923  width = ( scaledSize * static_cast< double >( size.width() ) ) / 100.0;
2924  height = ( scaledSize * static_cast< double >( size.height() ) ) / 100.0;
2925 
2926  // don't render symbols with size below one or above 10,000 pixels
2927  if ( static_cast< int >( width ) < 1 || 10000.0 < width || static_cast< int >( height ) < 1 || 10000.0 < height )
2928  return;
2929 
2930  calculateOffsetAndRotation( context, width, height, outputOffset, angle );
2931  }
2932  else
2933  {
2934  width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
2935  height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
2936 
2937  if ( preservedAspectRatio() && path != mPath )
2938  {
2939  const QSize size = QgsApplication::imageCache()->originalSize( path );
2940  if ( !size.isNull() && size.isValid() && size.width() > 0 )
2941  {
2942  height = width * ( static_cast< double >( size.height() ) / static_cast< double >( size.width() ) );
2943  }
2944  }
2945 
2946  // don't render symbols with size below one or above 10,000 pixels
2947  if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2948  return;
2949 
2950  calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
2951  }
2952 
2953  const QgsScopedQPainterState painterState( p );
2954  p->translate( point + outputOffset );
2955 
2956  const bool rotated = !qgsDoubleNear( angle, 0 );
2957  if ( rotated )
2958  p->rotate( angle );
2959 
2960  double opacity = mOpacity;
2962  {
2965  }
2966  opacity *= context.opacity();
2967 
2968  bool cached;
2969  QImage img = QgsApplication::imageCache()->pathAsImage( path, QSize( width, preservedAspectRatio() ? 0 : width * aspectRatio ), preservedAspectRatio(), opacity, cached, ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ) );
2970  if ( !img.isNull() )
2971  {
2972  if ( context.selected() )
2973  {
2975  }
2976 
2977  const bool prevSmoothTransform = p->testRenderHint( QPainter::RenderHint::SmoothPixmapTransform );
2978  p->setRenderHint( QPainter::RenderHint::SmoothPixmapTransform, true );
2979  p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2980  p->setRenderHint( QPainter::RenderHint::SmoothPixmapTransform, prevSmoothTransform );
2981  }
2982 }
2983 
2984 double QgsRasterMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2985 {
2986  double scaledSize = mSize;
2988 
2989  bool ok = true;
2990  if ( hasDataDefinedSize )
2991  {
2992  context.setOriginalValueVariable( mSize );
2994  }
2995  else
2996  {
2998  if ( hasDataDefinedSize )
2999  {
3000  context.setOriginalValueVariable( mSize );
3002  }
3003  }
3004 
3005  if ( hasDataDefinedSize && ok )
3006  {
3007  switch ( mScaleMethod )
3008  {
3010  scaledSize = std::sqrt( scaledSize );
3011  break;
3013  break;
3014  }
3015  }
3016 
3017  return scaledSize;
3018 }
3019 
3020 double QgsRasterMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
3021 {
3023  if ( !hasDataDefinedAspectRatio )
3024  return mFixedAspectRatio;
3025 
3027  return 0.0;
3028 
3029  double scaledAspectRatio = mDefaultAspectRatio;
3030  if ( mFixedAspectRatio > 0.0 )
3031  scaledAspectRatio = mFixedAspectRatio;
3032 
3033  const double defaultHeight = mSize * scaledAspectRatio;
3034  scaledAspectRatio = defaultHeight / scaledSize;
3035 
3036  bool ok = true;
3037  double scaledHeight = scaledSize * scaledAspectRatio;
3039  {
3040  context.setOriginalValueVariable( defaultHeight );
3041  scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyHeight, context.renderContext().expressionContext(), defaultHeight, &ok );
3042  }
3043 
3044  if ( hasDataDefinedAspectRatio && ok )
3045  {
3046  switch ( mScaleMethod )
3047  {
3049  scaledHeight = sqrt( scaledHeight );
3050  break;
3052  break;
3053  }
3054  }
3055 
3056  scaledAspectRatio = scaledHeight / scaledSize;
3057 
3058  return scaledAspectRatio;
3059 }
3060 
3061 void QgsRasterMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
3062 {
3063  //offset
3064  double offsetX = 0;
3065  double offsetY = 0;
3066  markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
3067  offset = QPointF( offsetX, offsetY );
3068 
3069  angle = mAngle + mLineAngle;
3071  {
3072  context.setOriginalValueVariable( mAngle );
3074  }
3075 
3077  if ( hasDataDefinedRotation )
3078  {
3079  const QgsFeature *f = context.feature();
3080  if ( f )
3081  {
3082  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
3083  {
3084  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3085  angle += m2p.mapRotation();
3086  }
3087  }
3088  }
3089 
3090  if ( angle )
3092 }
3093 
3094 
3096 {
3097  QVariantMap map;
3098  map[QStringLiteral( "imageFile" )] = mPath;
3099  map[QStringLiteral( "size" )] = QString::number( mSize );
3100  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3101  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3102  map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
3103  map[QStringLiteral( "angle" )] = QString::number( mAngle );
3104  map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
3105  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3106  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3107  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3108  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
3109  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3110  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3111  return map;
3112 }
3113 
3115 {
3118  m->setOpacity( mOpacity );
3119  m->setOffset( mOffset );
3120  m->setOffsetUnit( mOffsetUnit );
3122  m->setSizeUnit( mSizeUnit );
3127  copyPaintEffect( m );
3128  return m;
3129 }
3130 
3132 {
3135 }
3136 
3138 {
3140 }
3141 
3143 {
3145 }
3146 
3148 {
3149  bool hasDataDefinedSize = false;
3150  const double scaledSize = calculateSize( context, hasDataDefinedSize );
3151  const double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3152  bool hasDataDefinedAspectRatio = false;
3153  const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3154  const double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3155 
3156  //don't render symbols with size below one or above 10,000 pixels
3157  if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
3158  {
3159  return QRectF();
3160  }
3161 
3162  QPointF outputOffset;
3163  double angle = 0.0;
3164  calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3165 
3166  QTransform transform;
3167 
3168  // move to the desired position
3169  transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3170 
3171  if ( !qgsDoubleNear( angle, 0.0 ) )
3172  transform.rotate( angle );
3173 
3174  QRectF symbolBounds = transform.mapRect( QRectF( -width / 2.0,
3175  -height / 2.0,
3176  width,
3177  height ) );
3178 
3179  return symbolBounds;
3180 }
3181 
3183 
3184 QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QString chr, double pointSize, const QColor &color, double angle )
3185 {
3186  mFontFamily = fontFamily;
3187  mString = chr;
3188  mColor = color;
3189  mAngle = angle;
3190  mSize = pointSize;
3191  mOrigSize = pointSize;
3193  mOffset = QPointF( 0, 0 );
3195  mStrokeColor = DEFAULT_FONTMARKER_BORDERCOLOR;
3196  mStrokeWidth = 0.0;
3197  mStrokeWidthUnit = QgsUnitTypes::RenderMillimeters;
3198  mPenJoinStyle = DEFAULT_FONTMARKER_JOINSTYLE;
3199 }
3200 
3202 
3204 {
3206  const QString fontStyle = DEFAULT_FONTMARKER_FONT;
3207  QString string = DEFAULT_FONTMARKER_CHR;
3208  double pointSize = DEFAULT_FONTMARKER_SIZE;
3211 
3212  if ( props.contains( QStringLiteral( "font" ) ) )
3213  fontFamily = props[QStringLiteral( "font" )].toString();
3214  if ( props.contains( QStringLiteral( "chr" ) ) && props[QStringLiteral( "chr" )].toString().length() > 0 )
3215  string = props[QStringLiteral( "chr" )].toString();
3216  if ( props.contains( QStringLiteral( "size" ) ) )
3217  pointSize = props[QStringLiteral( "size" )].toDouble();
3218  if ( props.contains( QStringLiteral( "color" ) ) )
3219  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )].toString() );
3220  if ( props.contains( QStringLiteral( "angle" ) ) )
3221  angle = props[QStringLiteral( "angle" )].toDouble();
3222 
3223  QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( fontFamily, string, pointSize, color, angle );
3224 
3225  if ( props.contains( QStringLiteral( "font_style" ) ) )
3226  m->setFontStyle( props[QStringLiteral( "font_style" )].toString() );
3227  if ( props.contains( QStringLiteral( "outline_color" ) ) )
3228  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )].toString() ) );
3229  if ( props.contains( QStringLiteral( "outline_width" ) ) )
3230  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
3231  if ( props.contains( QStringLiteral( "offset" ) ) )
3232  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
3233  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
3234  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
3235  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3236  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3237  if ( props.contains( QStringLiteral( "size_unit" ) ) )
3238  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
3239  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3240  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3241  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
3242  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
3243  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
3244  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
3245  if ( props.contains( QStringLiteral( "joinstyle" ) ) )
3246  m->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() ) );
3247  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3248  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3249  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3250  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3251 
3252  m->restoreOldDataDefinedProperties( props );
3253 
3254  return m;
3255 }
3256 
3258 {
3259  return QStringLiteral( "FontMarker" );
3260 }
3261 
3263 {
3264  QColor brushColor = mColor;
3265  QColor penColor = mStrokeColor;
3266 
3267  brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
3268  penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
3269 
3270  mBrush = QBrush( brushColor );
3271  mPen = QPen( penColor );
3272  mPen.setJoinStyle( mPenJoinStyle );
3273  mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
3274 
3275  mFont = QFont( mFontFamily );
3276  if ( !mFontStyle.isEmpty() )
3277  {
3278  mFont.setStyleName( QgsFontUtils::translateNamedStyle( mFontStyle ) );
3279  }
3280 
3281  double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3282  mNonZeroFontSize = !qgsDoubleNear( sizePixels, 0.0 );
3283 
3284  if ( mNonZeroFontSize && sizePixels > MAX_FONT_CHARACTER_SIZE_IN_PIXELS )
3285  {
3286  // if font is too large (e.g using map units and map is very zoomed in), then we limit
3287  // the font size and instead scale up the painter.
3288  // this avoids issues with massive font sizes (eg https://github.com/qgis/QGIS/issues/42270)
3289  mFontSizeScale = sizePixels / MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3290  sizePixels = MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3291  }
3292  else
3293  mFontSizeScale = 1.0;
3294 
3295  // if a non zero, but small pixel size results, round up to 2 pixels so that a "dot" is at least visible
3296  // (if we set a <=1 pixel size here Qt will reset the font to a default size, leading to much too large symbols)
3297  mFont.setPixelSize( std::max( 2, static_cast< int >( std::round( sizePixels ) ) ) );
3298  mFontMetrics.reset( new QFontMetrics( mFont ) );
3299  mChrWidth = mFontMetrics->horizontalAdvance( mString );
3300  mChrOffset = QPointF( mChrWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3301  mOrigSize = mSize; // save in case the size would be data defined
3302 
3303  // use caching only when not using a data defined character
3307  if ( mUseCachedPath )
3308  {
3309  QPointF chrOffset = mChrOffset;
3310  double chrWidth;
3311  const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3312  mCachedPath = QPainterPath();
3313  mCachedPath.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3314  }
3315 }
3316 
3318 {
3319  Q_UNUSED( context )
3320 }
3321 
3322 QString QgsFontMarkerSymbolLayer::characterToRender( QgsSymbolRenderContext &context, QPointF &charOffset, double &charWidth )
3323 {
3324  charOffset = mChrOffset;
3325  QString stringToRender = mString;
3327  {
3328  context.setOriginalValueVariable( mString );
3330  if ( stringToRender != mString )
3331  {
3332  charWidth = mFontMetrics->horizontalAdvance( stringToRender );
3333  charOffset = QPointF( charWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3334  }
3335  }
3336  return stringToRender;
3337 }
3338 
3339 void QgsFontMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
3340  double scaledSize,
3341  bool &hasDataDefinedRotation,
3342  QPointF &offset,
3343  double &angle ) const
3344 {
3345  //offset
3346  double offsetX = 0;
3347  double offsetY = 0;
3348  markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
3349  offset = QPointF( offsetX, offsetY );
3350 
3351  //angle
3352  bool ok = true;
3353  angle = mAngle + mLineAngle;
3355  {
3356  context.setOriginalValueVariable( angle );
3358 
3359  // If the expression evaluation was not successful, fallback to static value
3360  if ( !ok )
3361  angle = mAngle + mLineAngle;
3362  }
3363 
3364  hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation;
3365  if ( hasDataDefinedRotation )
3366  {
3367  // For non-point markers, "dataDefinedRotation" means following the
3368  // shape (shape-data defined). For them, "field-data defined" does
3369  // not work at all. TODO: if "field-data defined" ever gets implemented
3370  // we'll need a way to distinguish here between the two, possibly
3371  // using another flag in renderHints()
3372  const QgsFeature *f = context.feature();
3373  if ( f )
3374  {
3375  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
3376  {
3377  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3378  angle += m2p.mapRotation();
3379  }
3380  }
3381  }
3382 
3383  if ( angle )
3385 }
3386 
3387 double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context )
3388 {
3389  double scaledSize = mSize;
3390  const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
3391 
3392  bool ok = true;
3393  if ( hasDataDefinedSize )
3394  {
3395  context.setOriginalValueVariable( mSize );
3397  }
3398 
3399  if ( hasDataDefinedSize && ok )
3400  {
3401  switch ( mScaleMethod )
3402  {
3404  scaledSize = std::sqrt( scaledSize );
3405  break;
3407  break;
3408  }
3409  }
3410  return scaledSize;
3411 }
3412 
3414 {
3415  QPainter *p = context.renderContext().painter();
3416  if ( !p || !mNonZeroFontSize )
3417  return;
3418 
3419  QTransform transform;
3420 
3421  bool ok;
3422  QColor brushColor = mColor;
3424  {
3427  }
3428  brushColor = context.selected() ? context.renderContext().selectionColor() : brushColor;
3429  if ( !context.selected() || !SELECTION_IS_OPAQUE )
3430  {
3431  brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
3432  }
3433  mBrush.setColor( brushColor );
3434 
3435  QColor penColor = mStrokeColor;
3437  {
3440  }
3441  penColor.setAlphaF( penColor.alphaF() * context.opacity() );
3442 
3443  double penWidth = context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3445  {
3446  context.setOriginalValueVariable( mStrokeWidth );
3448  if ( ok )
3449  {
3450  penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3451  }
3452  }
3453 
3455  {
3457  const QString style = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyJoinStyle, context.renderContext().expressionContext(), QString(), &ok );
3458  if ( ok )
3459  {
3460  mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
3461  }
3462  }
3463 
3464  const QgsScopedQPainterState painterState( p );
3465  p->setBrush( mBrush );
3466  if ( !qgsDoubleNear( penWidth, 0.0 ) )
3467  {
3468  mPen.setColor( penColor );
3469  mPen.setWidthF( penWidth );
3470  p->setPen( mPen );
3471  }
3472  else
3473  {
3474  p->setPen( Qt::NoPen );
3475  }
3476 
3478  {
3479  context.setOriginalValueVariable( mFontFamily );
3481  mFont.setFamily( ok ? fontFamily : mFontFamily );
3482  }
3484  {
3485  context.setOriginalValueVariable( mFontStyle );
3488  }
3490  {
3491  mFontMetrics.reset( new QFontMetrics( mFont ) );
3492  }
3493 
3494  QPointF chrOffset = mChrOffset;
3495  double chrWidth;
3496  const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3497 
3498  const double sizeToRender = calculateSize( context );
3499 
3500  bool hasDataDefinedRotation = false;
3501  QPointF offset;
3502  double angle = 0;
3503  calculateOffsetAndRotation( context, sizeToRender, hasDataDefinedRotation, offset, angle );
3504 
3505  p->translate( point.x() + offset.x(), point.y() + offset.y() );
3506 
3507  if ( !qgsDoubleNear( angle, 0.0 ) )
3508  transform.rotate( angle );
3509 
3510  if ( !qgsDoubleNear( sizeToRender, mOrigSize ) )
3511  {
3512  const double s = sizeToRender / mOrigSize;
3513  transform.scale( s, s );
3514  }
3515 
3516  if ( !qgsDoubleNear( mFontSizeScale, 1.0 ) )
3517  transform.scale( mFontSizeScale, mFontSizeScale );
3518 
3519  if ( mUseCachedPath )
3520  {
3521  p->drawPath( transform.map( mCachedPath ) );
3522  }
3523  else
3524  {
3525  QPainterPath path;
3526  path.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3527  p->drawPath( transform.map( path ) );
3528  }
3529 }
3530 
3532 {
3533  QVariantMap props;
3534  props[QStringLiteral( "font" )] = mFontFamily;
3535  props[QStringLiteral( "font_style" )] = mFontStyle;
3536  props[QStringLiteral( "chr" )] = mString;
3537  props[QStringLiteral( "size" )] = QString::number( mSize );
3538  props[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3539  props[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3540  props[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
3541  props[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
3542  props[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
3543  props[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
3544  props[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
3545  props[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
3546  props[QStringLiteral( "angle" )] = QString::number( mAngle );
3547  props[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3548  props[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3549  props[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3550  props[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3551  props[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3552  return props;
3553 }
3554 
3556 {
3557  QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( mFontFamily, mString, mSize, mColor, mAngle );
3558  m->setFontStyle( mFontStyle );
3559  m->setStrokeColor( mStrokeColor );
3560  m->setStrokeWidth( mStrokeWidth );
3561  m->setStrokeWidthUnit( mStrokeWidthUnit );
3562  m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
3563  m->setPenJoinStyle( mPenJoinStyle );
3564  m->setOffset( mOffset );
3565  m->setOffsetUnit( mOffsetUnit );
3567  m->setSizeUnit( mSizeUnit );
3572  copyPaintEffect( m );
3573  return m;
3574 }
3575 
3576 void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
3577 {
3578  // <Graphic>
3579  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
3580  element.appendChild( graphicElem );
3581 
3582  const QString fontPath = QStringLiteral( "ttf://%1" ).arg( mFontFamily );
3583  int markIndex = !mString.isEmpty() ? mString.at( 0 ).unicode() : 0;
3584  const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
3585  QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, QStringLiteral( "ttf" ), &markIndex, mColor, size );
3586 
3587  // <Rotation>
3588  QString angleFunc;
3589  bool ok;
3590  const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
3591  if ( !ok )
3592  {
3593  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
3594  }
3595  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
3596  {
3597  angleFunc = QString::number( angle + mAngle );
3598  }
3599  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
3600 
3601  // <Displacement>
3602  const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
3604 }
3605 
3607 {
3609  || mStrokeWidthUnit == QgsUnitTypes::RenderMapUnits || mStrokeWidthUnit == QgsUnitTypes::RenderMetersInMapUnits
3611 }
3612 
3614 {
3615  QPointF chrOffset = mChrOffset;
3616  double chrWidth = mChrWidth;
3617  //calculate width of rendered character
3618  ( void )characterToRender( context, chrOffset, chrWidth );
3619 
3620  if ( !mFontMetrics )
3621  mFontMetrics.reset( new QFontMetrics( mFont ) );
3622 
3623  double scaledSize = calculateSize( context );
3624  if ( !qgsDoubleNear( scaledSize, mOrigSize ) )
3625  {
3626  chrWidth *= scaledSize / mOrigSize;
3627  }
3628  chrWidth *= mFontSizeScale;
3629 
3630  bool hasDataDefinedRotation = false;
3631  QPointF offset;
3632  double angle = 0;
3633  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
3634  scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3635 
3636  QTransform transform;
3637 
3638  // move to the desired position
3639  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
3640 
3641  if ( !qgsDoubleNear( angle, 0.0 ) )
3642  transform.rotate( angle );
3643 
3644  QRectF symbolBounds = transform.mapRect( QRectF( -chrWidth / 2.0,
3645  -scaledSize / 2.0,
3646  chrWidth,
3647  scaledSize ) );
3648  return symbolBounds;
3649 }
3650 
3652 {
3653  QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
3654 
3655  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
3656  if ( graphicElem.isNull() )
3657  return nullptr;
3658 
3659  QString name, format;
3660  QColor color;
3661  double size;
3662  int chr;
3663 
3664  if ( !QgsSymbolLayerUtils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
3665  return nullptr;
3666 
3667  if ( !name.startsWith( QLatin1String( "ttf://" ) ) || format != QLatin1String( "ttf" ) )
3668  return nullptr;
3669 
3670  const QString fontFamily = name.mid( 6 );
3671 
3672  double angle = 0.0;
3673  QString angleFunc;
3674  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
3675  {
3676  bool ok;
3677  const double d = angleFunc.toDouble( &ok );
3678  if ( ok )
3679  angle = d;
3680  }
3681 
3682  QPointF offset;
3684 
3685  const QString uom = element.attribute( QStringLiteral( "uom" ) );
3689 
3691  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
3692  m->setAngle( angle );
3693  m->setOffset( offset );
3694  return m;
3695 }
3696 
3697 void QgsFontMarkerSymbolLayer::resolveFonts( const QVariantMap &properties, const QgsReadWriteContext &context )
3698 {
3699  const QString fontFamily = properties.value( QStringLiteral( "font" ), DEFAULT_FONTMARKER_FONT ).toString();
3701  {
3702  context.pushMessage( QObject::tr( "Font “%1” not available on system" ).arg( fontFamily ) );
3703  }
3704 }
3705 
3707 {
3708  QMap<QString, QgsProperty>::iterator it = mParameters.begin();
3709  for ( ; it != mParameters.end(); ++it )
3710  it.value().prepare( context.renderContext().expressionContext() );
3711 
3713 }
3714 
3715 
3716 QSet<QString> QgsSvgMarkerSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
3717 {
3718  QSet<QString> attrs = QgsMarkerSymbolLayer::usedAttributes( context );
3719 
3720  QMap<QString, QgsProperty>::const_iterator it = mParameters.constBegin();
3721  for ( ; it != mParameters.constEnd(); ++it )
3722  {
3723  attrs.unite( it.value().referencedFields( context.expressionContext(), true ) );
3724  }
3725 
3726  return attrs;
3727 }
@ 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.
@ RenderingSubSymbol
Set whenever a sub-symbol of a parent symbol is currently being rendered. Can be used during symbol a...
@ 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:223
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:128
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, double targetDpi=96, 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...
void setFlag(Qgis::RenderContextFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
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:1246
QMap< QString, QString > QgsStringMap
Definition: qgis.h:1703
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