QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
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 "qgsimagecache.h"
23 #include "qgsimageoperation.h"
24 #include "qgsrendercontext.h"
25 #include "qgslogger.h"
26 #include "qgssvgcache.h"
27 #include "qgsunittypes.h"
28 
29 #include <QPainter>
30 #include <QSvgRenderer>
31 #include <QFileInfo>
32 #include <QDir>
33 #include <QDomDocument>
34 #include <QDomElement>
35 
36 #include <cmath>
37 
38 Q_GUI_EXPORT extern int qt_defaultDpiX();
39 Q_GUI_EXPORT extern int qt_defaultDpiY();
40 
41 static void _fixQPictureDPI( QPainter *p )
42 {
43  // QPicture makes an assumption that we drawing to it with system DPI.
44  // Then when being drawn, it scales the painter. The following call
45  // negates the effect. There is no way of setting QPicture's DPI.
46  // See QTBUG-20361
47  p->scale( static_cast< double >( qt_defaultDpiX() ) / p->device()->logicalDpiX(),
48  static_cast< double >( qt_defaultDpiY() ) / p->device()->logicalDpiY() );
49 }
50 
51 
53 
54 
55 //
56 // QgsSimpleMarkerSymbolLayerBase
57 //
58 
59 QList<QgsSimpleMarkerSymbolLayerBase::Shape> QgsSimpleMarkerSymbolLayerBase::availableShapes()
60 {
61  QList< Shape > shapes;
62  shapes << Square
63  << Diamond
64  << Pentagon
65  << Hexagon
66  << Triangle
68  << Star
69  << Arrow
70  << Circle
71  << Cross
72  << CrossFill
73  << Cross2
74  << Line
75  << ArrowHead
77  << SemiCircle
78  << ThirdCircle
79  << QuarterCircle
80  << QuarterSquare
81  << HalfSquare
85  return shapes;
86 }
87 
89  : mShape( shape )
90 {
91  mSize = size;
92  mAngle = angle;
93  mOffset = QPointF( 0, 0 );
97 }
98 
100 {
101  switch ( shape )
102  {
103  case Square:
104  case Diamond:
105  case Pentagon:
106  case Hexagon:
107  case Triangle:
108  case EquilateralTriangle:
109  case Star:
110  case Arrow:
111  case Circle:
112  case CrossFill:
113  case ArrowHeadFilled:
114  case SemiCircle:
115  case ThirdCircle:
116  case QuarterCircle:
117  case QuarterSquare:
118  case HalfSquare:
119  case DiagonalHalfSquare:
120  case RightHalfTriangle:
121  case LeftHalfTriangle:
122  return true;
123 
124  case Cross:
125  case Cross2:
126  case Line:
127  case ArrowHead:
128  return false;
129  }
130  return true;
131 }
132 
134 {
135  bool hasDataDefinedRotation = context.renderHints() & QgsSymbol::DynamicRotation
137  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
138 
139  // use either QPolygonF or QPainterPath for drawing
140  if ( !prepareMarkerShape( mShape ) ) // drawing as a polygon
141  {
142  prepareMarkerPath( mShape ); // drawing as a painter path
143  }
144 
145  QTransform transform;
146 
147  // scale the shape (if the size is not going to be modified)
148  if ( !hasDataDefinedSize )
149  {
150  double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
151  double half = scaledSize / 2.0;
152  transform.scale( half, half );
153  }
154 
155  // rotate if the rotation is not going to be changed during the rendering
156  if ( !hasDataDefinedRotation && !qgsDoubleNear( mAngle, 0.0 ) )
157  {
158  transform.rotate( mAngle );
159  }
160 
161  if ( !mPolygon.isEmpty() )
162  mPolygon = transform.map( mPolygon );
163  else
164  mPath = transform.map( mPath );
165 
167 }
168 
170 {
171  Q_UNUSED( context )
172 }
173 
175 {
176  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
177  //of the rendered point!
178 
179  QPainter *p = context.renderContext().painter();
180  if ( !p )
181  {
182  return;
183  }
184 
185  bool hasDataDefinedSize = false;
186  double scaledSize = calculateSize( context, hasDataDefinedSize );
187 
188  bool hasDataDefinedRotation = false;
189  QPointF offset;
190  double angle = 0;
191  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
192 
193  //data defined shape?
194  bool createdNewPath = false;
195  bool ok = true;
196  Shape symbol = mShape;
198  {
199  context.setOriginalValueVariable( encodeShape( symbol ) );
201  if ( exprVal.isValid() )
202  {
203  Shape decoded = decodeShape( exprVal.toString(), &ok );
204  if ( ok )
205  {
206  symbol = decoded;
207 
208  if ( !prepareMarkerShape( symbol ) ) // drawing as a polygon
209  {
210  prepareMarkerPath( symbol ); // drawing as a painter path
211  }
212  createdNewPath = true;
213  }
214  }
215  else
216  {
217  symbol = mShape;
218  }
219  }
220 
221  QTransform transform;
222 
223  // move to the desired position
224  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
225 
226  // resize if necessary
227  if ( hasDataDefinedSize || createdNewPath )
228  {
229  double s = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
230  double half = s / 2.0;
231  transform.scale( half, half );
232  }
233 
234  if ( !qgsDoubleNear( angle, 0.0 ) && ( hasDataDefinedRotation || createdNewPath ) )
235  transform.rotate( angle );
236 
237  //need to pass: symbol, polygon, path
238 
239  QPolygonF polygon;
240  QPainterPath path;
241  if ( !mPolygon.isEmpty() )
242  {
243  polygon = transform.map( mPolygon );
244  }
245  else
246  {
247  path = transform.map( mPath );
248  }
249  draw( context, symbol, polygon, path );
250 }
251 
253 {
254  bool hasDataDefinedSize = false;
255  double scaledSize = calculateSize( context, hasDataDefinedSize );
256 
257  bool hasDataDefinedRotation = false;
258  QPointF offset;
259  double angle = 0;
260  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
261 
262  scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
263 
264  QTransform transform;
265 
266  // move to the desired position
267  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
268 
269  if ( !qgsDoubleNear( angle, 0.0 ) )
270  transform.rotate( angle );
271 
272  return transform.mapRect( QRectF( -scaledSize / 2.0,
273  -scaledSize / 2.0,
274  scaledSize,
275  scaledSize ) );
276 }
277 
279 {
280  if ( ok )
281  *ok = true;
282  QString cleaned = name.toLower().trimmed();
283 
284  if ( cleaned == QLatin1String( "square" ) || cleaned == QLatin1String( "rectangle" ) )
285  return Square;
286  else if ( cleaned == QLatin1String( "diamond" ) )
287  return Diamond;
288  else if ( cleaned == QLatin1String( "pentagon" ) )
289  return Pentagon;
290  else if ( cleaned == QLatin1String( "hexagon" ) )
291  return Hexagon;
292  else if ( cleaned == QLatin1String( "triangle" ) )
293  return Triangle;
294  else if ( cleaned == QLatin1String( "equilateral_triangle" ) )
295  return EquilateralTriangle;
296  else if ( cleaned == QLatin1String( "star" ) || cleaned == QLatin1String( "regular_star" ) )
297  return Star;
298  else if ( cleaned == QLatin1String( "arrow" ) )
299  return Arrow;
300  else if ( cleaned == QLatin1String( "circle" ) )
301  return Circle;
302  else if ( cleaned == QLatin1String( "cross" ) )
303  return Cross;
304  else if ( cleaned == QLatin1String( "cross_fill" ) )
305  return CrossFill;
306  else if ( cleaned == QLatin1String( "cross2" ) || cleaned == QLatin1String( "x" ) )
307  return Cross2;
308  else if ( cleaned == QLatin1String( "line" ) )
309  return Line;
310  else if ( cleaned == QLatin1String( "arrowhead" ) )
311  return ArrowHead;
312  else if ( cleaned == QLatin1String( "filled_arrowhead" ) )
313  return ArrowHeadFilled;
314  else if ( cleaned == QLatin1String( "semi_circle" ) )
315  return SemiCircle;
316  else if ( cleaned == QLatin1String( "third_circle" ) )
317  return ThirdCircle;
318  else if ( cleaned == QLatin1String( "quarter_circle" ) )
319  return QuarterCircle;
320  else if ( cleaned == QLatin1String( "quarter_square" ) )
321  return QuarterSquare;
322  else if ( cleaned == QLatin1String( "half_square" ) )
323  return HalfSquare;
324  else if ( cleaned == QLatin1String( "diagonal_half_square" ) )
325  return DiagonalHalfSquare;
326  else if ( cleaned == QLatin1String( "right_half_triangle" ) )
327  return RightHalfTriangle;
328  else if ( cleaned == QLatin1String( "left_half_triangle" ) )
329  return LeftHalfTriangle;
330 
331  if ( ok )
332  *ok = false;
333  return Circle;
334 }
335 
337 {
338  switch ( shape )
339  {
340  case Square:
341  return QStringLiteral( "square" );
342  case QuarterSquare:
343  return QStringLiteral( "quarter_square" );
344  case HalfSquare:
345  return QStringLiteral( "half_square" );
346  case DiagonalHalfSquare:
347  return QStringLiteral( "diagonal_half_square" );
348  case Diamond:
349  return QStringLiteral( "diamond" );
350  case Pentagon:
351  return QStringLiteral( "pentagon" );
352  case Hexagon:
353  return QStringLiteral( "hexagon" );
354  case Triangle:
355  return QStringLiteral( "triangle" );
356  case EquilateralTriangle:
357  return QStringLiteral( "equilateral_triangle" );
358  case LeftHalfTriangle:
359  return QStringLiteral( "left_half_triangle" );
360  case RightHalfTriangle:
361  return QStringLiteral( "right_half_triangle" );
362  case Star:
363  return QStringLiteral( "star" );
364  case Arrow:
365  return QStringLiteral( "arrow" );
366  case ArrowHeadFilled:
367  return QStringLiteral( "filled_arrowhead" );
368  case CrossFill:
369  return QStringLiteral( "cross_fill" );
370  case Circle:
371  return QStringLiteral( "circle" );
372  case Cross:
373  return QStringLiteral( "cross" );
374  case Cross2:
375  return QStringLiteral( "cross2" );
376  case Line:
377  return QStringLiteral( "line" );
378  case ArrowHead:
379  return QStringLiteral( "arrowhead" );
380  case SemiCircle:
381  return QStringLiteral( "semi_circle" );
382  case ThirdCircle:
383  return QStringLiteral( "third_circle" );
384  case QuarterCircle:
385  return QStringLiteral( "quarter_circle" );
386  }
387  return QString();
388 }
389 
391 {
392  return shapeToPolygon( shape, mPolygon );
393 }
394 
396 {
397  polygon.clear();
398 
399  switch ( shape )
400  {
401  case Square:
402  polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 1, 1 ) ) );
403  return true;
404 
405  case QuarterSquare:
406  polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 0 ) ) );
407  return true;
408 
409  case HalfSquare:
410  polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 1 ) ) );
411  return true;
412 
413  case DiagonalHalfSquare:
414  polygon << QPointF( -1, -1 ) << QPointF( 1, 1 ) << QPointF( -1, 1 ) << QPointF( -1, -1 );
415  return true;
416 
417  case Diamond:
418  polygon << QPointF( -1, 0 ) << QPointF( 0, 1 )
419  << QPointF( 1, 0 ) << QPointF( 0, -1 ) << QPointF( -1, 0 );
420  return true;
421 
422  case Pentagon:
423  /* angular-representation of hardcoded values used
424  polygon << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288.0 ) ) )
425  << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) )
426  << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) )
427  << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) )
428  << QPointF( 0, -1 ); */
429  polygon << QPointF( -0.9511, -0.3090 )
430  << QPointF( -0.5878, 0.8090 )
431  << QPointF( 0.5878, 0.8090 )
432  << QPointF( 0.9511, -0.3090 )
433  << QPointF( 0, -1 )
434  << QPointF( -0.9511, -0.3090 );
435  return true;
436 
437  case Hexagon:
438  /* angular-representation of hardcoded values used
439  polygon << QPointF( std::sin( DEG2RAD( 300.0 ) ), - std::cos( DEG2RAD( 300.0 ) ) )
440  << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
441  << QPointF( std::sin( DEG2RAD( 180.0 ) ), - std::cos( DEG2RAD( 180.0 ) ) )
442  << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
443  << QPointF( std::sin( DEG2RAD( 60.0 ) ), - std::cos( DEG2RAD( 60.0 ) ) )
444  << QPointF( 0, -1 ); */
445  polygon << QPointF( -0.8660, -0.5 )
446  << QPointF( -0.8660, 0.5 )
447  << QPointF( 0, 1 )
448  << QPointF( 0.8660, 0.5 )
449  << QPointF( 0.8660, -0.5 )
450  << QPointF( 0, -1 )
451  << QPointF( -0.8660, -0.5 );
452  return true;
453 
454  case Triangle:
455  polygon << QPointF( -1, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
456  return true;
457 
458  case EquilateralTriangle:
459  /* angular-representation of hardcoded values used
460  polygon << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
461  << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
462  << QPointF( 0, -1 ); */
463  polygon << QPointF( -0.8660, 0.5 )
464  << QPointF( 0.8660, 0.5 )
465  << QPointF( 0, -1 )
466  << QPointF( -0.8660, 0.5 );
467  return true;
468 
469  case LeftHalfTriangle:
470  polygon << QPointF( 0, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( 0, 1 );
471  return true;
472 
473  case RightHalfTriangle:
474  polygon << QPointF( -1, 1 ) << QPointF( 0, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
475  return true;
476 
477  case Star:
478  {
479  double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
480 
481  polygon << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ) // 324
482  << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288 ) ) ) // 288
483  << QPointF( inner_r * std::sin( DEG2RAD( 252.0 ) ), - inner_r * std::cos( DEG2RAD( 252.0 ) ) ) // 252
484  << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) ) // 216
485  << QPointF( 0, inner_r ) // 180
486  << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) ) // 144
487  << QPointF( inner_r * std::sin( DEG2RAD( 108.0 ) ), - inner_r * std::cos( DEG2RAD( 108.0 ) ) ) // 108
488  << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) ) // 72
489  << QPointF( inner_r * std::sin( DEG2RAD( 36.0 ) ), - inner_r * std::cos( DEG2RAD( 36.0 ) ) ) // 36
490  << QPointF( 0, -1 )
491  << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ); // 324; // 0
492  return true;
493  }
494 
495  case Arrow:
496  polygon << QPointF( 0, -1 )
497  << QPointF( 0.5, -0.5 )
498  << QPointF( 0.25, -0.5 )
499  << QPointF( 0.25, 1 )
500  << QPointF( -0.25, 1 )
501  << QPointF( -0.25, -0.5 )
502  << QPointF( -0.5, -0.5 )
503  << QPointF( 0, -1 );
504  return true;
505 
506  case ArrowHeadFilled:
507  polygon << QPointF( 0, 0 ) << QPointF( -1, 1 ) << QPointF( -1, -1 ) << QPointF( 0, 0 );
508  return true;
509 
510  case CrossFill:
511  polygon << QPointF( -1, -0.2 )
512  << QPointF( -1, -0.2 )
513  << QPointF( -1, 0.2 )
514  << QPointF( -0.2, 0.2 )
515  << QPointF( -0.2, 1 )
516  << QPointF( 0.2, 1 )
517  << QPointF( 0.2, 0.2 )
518  << QPointF( 1, 0.2 )
519  << QPointF( 1, -0.2 )
520  << QPointF( 0.2, -0.2 )
521  << QPointF( 0.2, -1 )
522  << QPointF( -0.2, -1 )
523  << QPointF( -0.2, -0.2 )
524  << QPointF( -1, -0.2 );
525  return true;
526 
527  case Circle:
528  case Cross:
529  case Cross2:
530  case Line:
531  case ArrowHead:
532  case SemiCircle:
533  case ThirdCircle:
534  case QuarterCircle:
535  return false;
536  }
537 
538  return false;
539 }
540 
542 {
543  mPath = QPainterPath();
544 
545  switch ( symbol )
546  {
547  case Circle:
548 
549  mPath.addEllipse( QRectF( -1, -1, 2, 2 ) ); // x,y,w,h
550  return true;
551 
552  case SemiCircle:
553  mPath.arcTo( -1, -1, 2, 2, 0, 180 );
554  mPath.lineTo( 0, 0 );
555  return true;
556 
557  case ThirdCircle:
558  mPath.arcTo( -1, -1, 2, 2, 90, 120 );
559  mPath.lineTo( 0, 0 );
560  return true;
561 
562  case QuarterCircle:
563  mPath.arcTo( -1, -1, 2, 2, 90, 90 );
564  mPath.lineTo( 0, 0 );
565  return true;
566 
567  case Cross:
568  mPath.moveTo( -1, 0 );
569  mPath.lineTo( 1, 0 ); // horizontal
570  mPath.moveTo( 0, -1 );
571  mPath.lineTo( 0, 1 ); // vertical
572  return true;
573 
574  case Cross2:
575  mPath.moveTo( -1, -1 );
576  mPath.lineTo( 1, 1 );
577  mPath.moveTo( 1, -1 );
578  mPath.lineTo( -1, 1 );
579  return true;
580 
581  case Line:
582  mPath.moveTo( 0, -1 );
583  mPath.lineTo( 0, 1 ); // vertical line
584  return true;
585 
586  case ArrowHead:
587  mPath.moveTo( -1, -1 );
588  mPath.lineTo( 0, 0 );
589  mPath.lineTo( -1, 1 );
590  return true;
591 
592  case Square:
593  case QuarterSquare:
594  case HalfSquare:
595  case DiagonalHalfSquare:
596  case Diamond:
597  case Pentagon:
598  case Hexagon:
599  case Triangle:
600  case EquilateralTriangle:
601  case LeftHalfTriangle:
602  case RightHalfTriangle:
603  case Star:
604  case Arrow:
605  case ArrowHeadFilled:
606  case CrossFill:
607  return false;
608  }
609  return false;
610 }
611 
612 double QgsSimpleMarkerSymbolLayerBase::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
613 {
614  double scaledSize = mSize;
615 
617  bool ok = true;
618  if ( hasDataDefinedSize )
619  {
620  context.setOriginalValueVariable( mSize );
622  mSize, &ok );
623  }
624 
625  if ( hasDataDefinedSize && ok )
626  {
627  switch ( mScaleMethod )
628  {
630  scaledSize = std::sqrt( scaledSize );
631  break;
633  break;
634  }
635  }
636 
637  return scaledSize;
638 }
639 
640 void QgsSimpleMarkerSymbolLayerBase::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle ) const
641 {
642  //offset
643  double offsetX = 0;
644  double offsetY = 0;
645  markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
646  offset = QPointF( offsetX, offsetY );
647 
648  hasDataDefinedRotation = false;
649  //angle
650  bool ok = true;
651  angle = mAngle + mLineAngle;
653  {
654  context.setOriginalValueVariable( angle );
656 
657  // If the expression evaluation was not successful, fallback to static value
658  if ( !ok )
659  angle = mAngle + mLineAngle;
660 
661  hasDataDefinedRotation = true;
662  }
663 
664  hasDataDefinedRotation = context.renderHints() & QgsSymbol::DynamicRotation || hasDataDefinedRotation;
665 
666  if ( hasDataDefinedRotation )
667  {
668  // For non-point markers, "dataDefinedRotation" means following the
669  // shape (shape-data defined). For them, "field-data defined" does
670  // not work at all. TODO: if "field-data defined" ever gets implemented
671  // we'll need a way to distinguish here between the two, possibly
672  // using another flag in renderHints()
673  const QgsFeature *f = context.feature();
674  if ( f )
675  {
676  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
677  {
678  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
679  angle += m2p.mapRotation();
680  }
681  }
682  }
683 
684  if ( angle )
685  offset = _rotatedOffset( offset, angle );
686 }
687 
688 
689 //
690 // QgsSimpleMarkerSymbolLayer
691 //
692 
694  : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
695  , mStrokeColor( strokeColor )
696  , mPenJoinStyle( penJoinStyle )
697 {
698  mColor = color;
699 }
700 
702 {
703  Shape shape = Circle;
706  Qt::PenJoinStyle penJoinStyle = DEFAULT_SIMPLEMARKER_JOINSTYLE;
710 
711  if ( props.contains( QStringLiteral( "name" ) ) )
712  {
713  shape = decodeShape( props[QStringLiteral( "name" )] );
714  }
715  if ( props.contains( QStringLiteral( "color" ) ) )
716  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] );
717  if ( props.contains( QStringLiteral( "color_border" ) ) )
718  {
719  //pre 2.5 projects use "color_border"
720  strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color_border" )] );
721  }
722  else if ( props.contains( QStringLiteral( "outline_color" ) ) )
723  {
724  strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )] );
725  }
726  else if ( props.contains( QStringLiteral( "line_color" ) ) )
727  {
728  strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )] );
729  }
730  if ( props.contains( QStringLiteral( "joinstyle" ) ) )
731  {
732  penJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )] );
733  }
734  if ( props.contains( QStringLiteral( "size" ) ) )
735  size = props[QStringLiteral( "size" )].toDouble();
736  if ( props.contains( QStringLiteral( "angle" ) ) )
737  angle = props[QStringLiteral( "angle" )].toDouble();
738  if ( props.contains( QStringLiteral( "scale_method" ) ) )
739  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )] );
740 
741  QgsSimpleMarkerSymbolLayer *m = new QgsSimpleMarkerSymbolLayer( shape, size, angle, scaleMethod, color, strokeColor, penJoinStyle );
742  if ( props.contains( QStringLiteral( "offset" ) ) )
743  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
744  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
745  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
746  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
747  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
748  if ( props.contains( QStringLiteral( "size_unit" ) ) )
749  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
750  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
751  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
752 
753  if ( props.contains( QStringLiteral( "outline_style" ) ) )
754  {
755  m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "outline_style" )] ) );
756  }
757  else if ( props.contains( QStringLiteral( "line_style" ) ) )
758  {
759  m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "line_style" )] ) );
760  }
761  if ( props.contains( QStringLiteral( "outline_width" ) ) )
762  {
763  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
764  }
765  else if ( props.contains( QStringLiteral( "line_width" ) ) )
766  {
767  m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
768  }
769  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
770  {
771  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )] ) );
772  }
773  if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
774  {
775  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )] ) );
776  }
777  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
778  {
779  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )] ) );
780  }
781 
782  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
783  {
784  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
785  }
786  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
787  {
788  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
789  }
790 
792 
793  return m;
794 }
795 
796 
798 {
799  return QStringLiteral( "SimpleMarker" );
800 }
801 
803 {
805 
806  QColor brushColor = mColor;
807  QColor penColor = mStrokeColor;
808 
809  brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
810  penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
811 
812  mBrush = QBrush( brushColor );
813  mPen = QPen( penColor );
814  mPen.setStyle( mStrokeStyle );
815  mPen.setJoinStyle( mPenJoinStyle );
817 
818  QColor selBrushColor = context.renderContext().selectionColor();
819  QColor selPenColor = selBrushColor == mColor ? selBrushColor : mStrokeColor;
820  if ( context.opacity() < 1 )
821  {
822  selBrushColor.setAlphaF( context.opacity() );
823  selPenColor.setAlphaF( context.opacity() );
824  }
825  mSelBrush = QBrush( selBrushColor );
826  mSelPen = QPen( selPenColor );
827  mSelPen.setStyle( mStrokeStyle );
829 
831  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
832 
833  // use caching only when:
834  // - size, rotation, shape, color, stroke color is not data-defined
835  // - drawing to screen (not printer)
836  mUsingCache = !hasDataDefinedRotation && !hasDataDefinedSize && !context.renderContext().forceVectorOutput()
840 
841  if ( !shapeIsFilled( mShape ) )
842  {
843  // some markers can't be drawn as a polygon (circle, cross)
844  // For these set the selected stroke color to the selected color
845  mSelPen.setColor( selBrushColor );
846  }
847 
848 
849  if ( mUsingCache )
850  {
851  if ( !prepareCache( context ) )
852  {
853  mUsingCache = false;
854  }
855  }
856  else
857  {
858  mCache = QImage();
859  mSelCache = QImage();
860  }
861 }
862 
863 
865 {
866  double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
867  // take into account angle (which is not data-defined otherwise cache wouldn't be used)
868  if ( !qgsDoubleNear( mAngle, 0.0 ) )
869  {
870  scaledSize = ( std::abs( std::sin( mAngle * M_PI / 180 ) ) + std::abs( std::cos( mAngle * M_PI / 180 ) ) ) * scaledSize;
871  }
872  // calculate necessary image size for the cache
873  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
874  int imageSize = ( static_cast< int >( scaledSize ) + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width
875  double center = imageSize / 2.0;
876  if ( imageSize > MAXIMUM_CACHE_WIDTH )
877  {
878  return false;
879  }
880 
881  mCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
882  mCache.fill( 0 );
883 
884  bool needsBrush = shapeIsFilled( mShape );
885 
886  QPainter p;
887  p.begin( &mCache );
888  p.setRenderHint( QPainter::Antialiasing );
889  p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
890  p.setPen( mPen );
891  p.translate( QPointF( center, center ) );
892  drawMarker( &p, context );
893  p.end();
894 
895  // Construct the selected version of the Cache
896 
897  QColor selColor = context.renderContext().selectionColor();
898 
899  mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
900  mSelCache.fill( 0 );
901 
902  p.begin( &mSelCache );
903  p.setRenderHint( QPainter::Antialiasing );
904  p.setBrush( needsBrush ? mSelBrush : Qt::NoBrush );
905  p.setPen( mSelPen );
906  p.translate( QPointF( center, center ) );
907  drawMarker( &p, context );
908  p.end();
909 
910  // Check that the selected version is different. If not, then re-render,
911  // filling the background with the selection color and using the normal
912  // colors for the symbol .. could be ugly!
913 
914  if ( mSelCache == mCache )
915  {
916  p.begin( &mSelCache );
917  p.setRenderHint( QPainter::Antialiasing );
918  p.fillRect( 0, 0, imageSize, imageSize, selColor );
919  p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
920  p.setPen( mPen );
921  p.translate( QPointF( center, center ) );
922  drawMarker( &p, context );
923  p.end();
924  }
925 
926  return true;
927 }
928 
929 void QgsSimpleMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, QgsSimpleMarkerSymbolLayerBase::Shape shape, const QPolygonF &polygon, const QPainterPath &path )
930 {
931  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
932  //of the rendered point!
933 
934  QPainter *p = context.renderContext().painter();
935  if ( !p )
936  {
937  return;
938  }
939 
940  bool ok = true;
942  {
945  if ( ok )
946  mBrush.setColor( c );
947  }
949  {
952  if ( ok )
953  {
954  mPen.setColor( c );
955  mSelPen.setColor( c );
956  }
957  }
959  {
962  if ( ok )
963  {
964  mPen.setWidthF( context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
966  }
967  }
969  {
972  if ( ok )
973  {
974  mPen.setStyle( QgsSymbolLayerUtils::decodePenStyle( strokeStyle ) );
975  mSelPen.setStyle( QgsSymbolLayerUtils::decodePenStyle( strokeStyle ) );
976  }
977  }
979  {
982  if ( ok )
983  {
984  mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
985  mSelPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
986  }
987  }
988 
989  if ( shapeIsFilled( shape ) )
990  {
991  p->setBrush( context.selected() ? mSelBrush : mBrush );
992  }
993  else
994  {
995  p->setBrush( Qt::NoBrush );
996  }
997  p->setPen( context.selected() ? mSelPen : mPen );
998 
999  if ( !polygon.isEmpty() )
1000  p->drawPolygon( polygon );
1001  else
1002  p->drawPath( path );
1003 }
1004 
1006 {
1007  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1008  //of the rendered point!
1009 
1010  QPainter *p = context.renderContext().painter();
1011  if ( !p )
1012  {
1013  return;
1014  }
1015 
1016  if ( mUsingCache )
1017  {
1018  QImage &img = context.selected() ? mSelCache : mCache;
1019  double s = img.width();
1020 
1021  bool hasDataDefinedSize = false;
1022  double scaledSize = calculateSize( context, hasDataDefinedSize );
1023 
1024  bool hasDataDefinedRotation = false;
1025  QPointF offset;
1026  double angle = 0;
1027  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
1028 
1029  p->drawImage( QRectF( point.x() - s / 2.0 + offset.x(),
1030  point.y() - s / 2.0 + offset.y(),
1031  s, s ), img );
1032  }
1033  else
1034  {
1036  }
1037 }
1038 
1040 {
1041  QgsStringMap map;
1042  map[QStringLiteral( "name" )] = encodeShape( mShape );
1043  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
1044  map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
1045  map[QStringLiteral( "size" )] = QString::number( mSize );
1046  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1047  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1048  map[QStringLiteral( "angle" )] = QString::number( mAngle );
1049  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1050  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1051  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1052  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1053  map[QStringLiteral( "outline_style" )] = QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle );
1054  map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
1055  map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
1056  map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
1057  map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
1058  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1059  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1060  return map;
1061 }
1062 
1064 {
1066  m->setOffset( mOffset );
1067  m->setSizeUnit( mSizeUnit );
1069  m->setOffsetUnit( mOffsetUnit );
1078  copyPaintEffect( m );
1079  return m;
1080 }
1081 
1082 void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
1083 {
1084  // <Graphic>
1085  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
1086  element.appendChild( graphicElem );
1087 
1090  QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, encodeShape( mShape ), mColor, mStrokeColor, mStrokeStyle, strokeWidth, size );
1091 
1092  // <Rotation>
1093  QString angleFunc;
1094  bool ok;
1095  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
1096  if ( !ok )
1097  {
1098  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mAngle );
1099  }
1100  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
1101  {
1102  angleFunc = QString::number( angle + mAngle );
1103  }
1104  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
1105 
1106  // <Displacement>
1108  QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
1109 }
1110 
1111 QString QgsSimpleMarkerSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
1112 {
1113  Q_UNUSED( mmScaleFactor )
1114  Q_UNUSED( mapUnitScaleFactor )
1115 #if 0
1116  QString ogrType = "3"; //default is circle
1117  if ( mName == "square" )
1118  {
1119  ogrType = "5";
1120  }
1121  else if ( mName == "triangle" )
1122  {
1123  ogrType = "7";
1124  }
1125  else if ( mName == "star" )
1126  {
1127  ogrType = "9";
1128  }
1129  else if ( mName == "circle" )
1130  {
1131  ogrType = "3";
1132  }
1133  else if ( mName == "cross" )
1134  {
1135  ogrType = "0";
1136  }
1137  else if ( mName == "x" || mName == "cross2" )
1138  {
1139  ogrType = "1";
1140  }
1141  else if ( mName == "line" )
1142  {
1143  ogrType = "10";
1144  }
1145 
1146  QString ogrString;
1147  ogrString.append( "SYMBOL(" );
1148  ogrString.append( "id:" );
1149  ogrString.append( '\"' );
1150  ogrString.append( "ogr-sym-" );
1151  ogrString.append( ogrType );
1152  ogrString.append( '\"' );
1153  ogrString.append( ",c:" );
1154  ogrString.append( mColor.name() );
1155  ogrString.append( ",o:" );
1156  ogrString.append( mStrokeColor.name() );
1157  ogrString.append( QString( ",s:%1mm" ).arg( mSize ) );
1158  ogrString.append( ')' );
1159  return ogrString;
1160 #endif //0
1161 
1162  QString ogrString;
1163  ogrString.append( "PEN(" );
1164  ogrString.append( "c:" );
1165  ogrString.append( mColor.name() );
1166  ogrString.append( ",w:" );
1167  ogrString.append( QString::number( mSize ) );
1168  ogrString.append( "mm" );
1169  ogrString.append( ")" );
1170  return ogrString;
1171 }
1172 
1174 {
1175  QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
1176 
1177  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
1178  if ( graphicElem.isNull() )
1179  return nullptr;
1180 
1181  QString name = QStringLiteral( "square" );
1182  QColor color, strokeColor;
1183  double strokeWidth, size;
1184  Qt::PenStyle strokeStyle;
1185 
1186  if ( !QgsSymbolLayerUtils::wellKnownMarkerFromSld( graphicElem, name, color, strokeColor, strokeStyle, strokeWidth, size ) )
1187  return nullptr;
1188 
1189  double angle = 0.0;
1190  QString angleFunc;
1191  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
1192  {
1193  bool ok;
1194  double d = angleFunc.toDouble( &ok );
1195  if ( ok )
1196  angle = d;
1197  }
1198 
1199  QPointF offset;
1200  QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, offset );
1201 
1202  Shape shape = decodeShape( name );
1203 
1204  QString uom = element.attribute( QStringLiteral( "uom" ) );
1205  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
1206  offset.setX( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.x() ) );
1207  offset.setY( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.y() ) );
1208 
1210  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
1211  m->setColor( color );
1212  m->setStrokeColor( strokeColor );
1213  m->setAngle( angle );
1214  m->setOffset( offset );
1215  m->setStrokeStyle( strokeStyle );
1216  m->setStrokeWidth( strokeWidth );
1217  return m;
1218 }
1219 
1221 {
1222  Q_UNUSED( context )
1223 
1224  if ( mPolygon.count() != 0 )
1225  {
1226  p->drawPolygon( mPolygon );
1227  }
1228  else
1229  {
1230  p->drawPath( mPath );
1231  }
1232 }
1233 
1234 bool QgsSimpleMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
1235 {
1236  //data defined size?
1237  double size = mSize;
1238 
1239  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
1240 
1241  //data defined size
1242  bool ok = true;
1243  if ( hasDataDefinedSize )
1244  {
1246 
1247  if ( ok )
1248  {
1249  switch ( mScaleMethod )
1250  {
1251  case QgsSymbol::ScaleArea:
1252  size = std::sqrt( size );
1253  break;
1255  break;
1256  }
1257  }
1258 
1260  }
1261 
1263  {
1264  size *= mmMapUnitScaleFactor;
1265  }
1266 
1268  {
1270  }
1271  double halfSize = size / 2.0;
1272 
1273  //strokeWidth
1274  double strokeWidth = mStrokeWidth;
1275 
1277  {
1280  }
1283  {
1285  }
1286 
1287  //color
1288  QColor pc = mPen.color();
1289  QColor bc = mBrush.color();
1291  {
1294  }
1296  {
1299  }
1300 
1301  //offset
1302  double offsetX = 0;
1303  double offsetY = 0;
1304  markerOffset( context, offsetX, offsetY );
1305  offsetX *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1306  offsetY *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1307 
1308 
1309  QPointF off( offsetX, offsetY );
1310 
1311  //angle
1312  double angle = mAngle + mLineAngle;
1314  {
1315  context.setOriginalValueVariable( mAngle );
1317  }
1318 
1319  Shape shape = mShape;
1321  {
1322  context.setOriginalValueVariable( encodeShape( shape ) );
1323  QString shapeName = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyName, context.renderContext().expressionContext(), QString(), &ok );
1324  if ( ok )
1325  {
1326  shape = decodeShape( shapeName, &ok );
1327  if ( !ok )
1328  shape = mShape;
1329  }
1330  }
1331 
1332  if ( angle )
1333  off = _rotatedOffset( off, angle );
1334 
1336 
1337  QTransform t;
1338  t.translate( shift.x() + off.x(), shift.y() - off.y() );
1339 
1340  if ( !qgsDoubleNear( angle, 0.0 ) )
1341  t.rotate( angle );
1342 
1343  QPolygonF polygon;
1344  if ( shapeToPolygon( shape, polygon ) )
1345  {
1346  t.scale( halfSize, -halfSize );
1347 
1348  polygon = t.map( polygon );
1349 
1350  QgsPointSequence p;
1351  p.reserve( polygon.size() );
1352  for ( int i = 0; i < polygon.size(); i++ )
1353  {
1354  p << QgsPoint( polygon[i] );
1355  }
1356 
1357  if ( mBrush.style() != Qt::NoBrush )
1358  e.writePolygon( QgsRingSequence() << p, layerName, QStringLiteral( "SOLID" ), bc );
1359  if ( mPen.style() != Qt::NoPen )
1360  e.writePolyline( p, layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1361  }
1362  else if ( shape == Circle )
1363  {
1364  shift += QPointF( off.x(), -off.y() );
1365  if ( mBrush.style() != Qt::NoBrush )
1366  e.writeFilledCircle( layerName, bc, QgsPoint( shift ), halfSize );
1367  if ( mPen.style() != Qt::NoPen )
1368  e.writeCircle( layerName, pc, QgsPoint( shift ), halfSize, QStringLiteral( "CONTINUOUS" ), strokeWidth );
1369  }
1370  else if ( shape == Line )
1371  {
1372  QPointF pt1 = t.map( QPointF( 0, -halfSize ) );
1373  QPointF pt2 = t.map( QPointF( 0, halfSize ) );
1374 
1375  if ( mPen.style() != Qt::NoPen )
1376  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1377  }
1378  else if ( shape == Cross )
1379  {
1380  if ( mPen.style() != Qt::NoPen )
1381  {
1382  QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
1383  QPointF pt2 = t.map( QPointF( halfSize, 0 ) );
1384  QPointF pt3 = t.map( QPointF( 0, -halfSize ) );
1385  QPointF pt4 = t.map( QPointF( 0, halfSize ) );
1386 
1387  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1388  e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1389  }
1390  }
1391  else if ( shape == Cross2 )
1392  {
1393  if ( mPen.style() != Qt::NoPen )
1394  {
1395  QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
1396  QPointF pt2 = t.map( QPointF( halfSize, halfSize ) );
1397  QPointF pt3 = t.map( QPointF( halfSize, -halfSize ) );
1398  QPointF pt4 = t.map( QPointF( -halfSize, halfSize ) );
1399 
1400  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1401  e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1402  }
1403  }
1404  else if ( shape == ArrowHead )
1405  {
1406  if ( mPen.style() != Qt::NoPen )
1407  {
1408  QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
1409  QPointF pt2 = t.map( QPointF( 0, 0 ) );
1410  QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
1411 
1412  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1413  e.writeLine( QgsPoint( pt3 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1414  }
1415  }
1416  else
1417  {
1418  QgsDebugMsg( QStringLiteral( "Unsupported dxf marker name %1" ).arg( encodeShape( shape ) ) );
1419  return false;
1420  }
1421 
1422  return true;
1423 }
1424 
1425 
1427 {
1429  mStrokeWidthUnit = unit;
1430 }
1431 
1433 {
1435  {
1436  return mStrokeWidthUnit;
1437  }
1439 }
1440 
1442 {
1444  mStrokeWidthMapUnitScale = scale;
1445 }
1446 
1448 {
1450  {
1451  return mStrokeWidthMapUnitScale;
1452  }
1453  return QgsMapUnitScale();
1454 }
1455 
1457 {
1458  QRectF symbolBounds = QgsSimpleMarkerSymbolLayerBase::bounds( point, context );
1459 
1460  // need to account for stroke width
1461  double penWidth = 0.0;
1462  bool ok = true;
1464  {
1467  if ( ok )
1468  {
1469  penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
1470  }
1471  }
1473  {
1476  if ( ok && strokeStyle == QLatin1String( "no" ) )
1477  {
1478  penWidth = 0.0;
1479  }
1480  }
1481  //antialiasing, add 1 pixel
1482  penWidth += 1;
1483 
1484  //extend bounds by pen width / 2.0
1485  symbolBounds.adjust( -penWidth / 2.0, -penWidth / 2.0,
1486  penWidth / 2.0, penWidth / 2.0 );
1487 
1488  return symbolBounds;
1489 }
1490 
1492 {
1493  if ( shapeIsFilled( mShape ) )
1494  {
1495  setFillColor( color );
1496  }
1497  else
1498  {
1499  setStrokeColor( color );
1500  }
1501 }
1502 
1504 {
1505  if ( shapeIsFilled( mShape ) )
1506  {
1507  return fillColor();
1508  }
1509  else
1510  {
1511  return strokeColor();
1512  }
1513 }
1514 
1515 
1516 
1517 
1518 //
1519 // QgsFilledMarkerSymbolLayer
1520 //
1521 
1523  : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
1524 {
1525  mFill.reset( static_cast<QgsFillSymbol *>( QgsFillSymbol::createSimple( QgsStringMap() ) ) );
1526 }
1527 
1529 {
1530  QString name = DEFAULT_SIMPLEMARKER_NAME;
1534 
1535  if ( props.contains( QStringLiteral( "name" ) ) )
1536  name = props[QStringLiteral( "name" )];
1537  if ( props.contains( QStringLiteral( "size" ) ) )
1538  size = props[QStringLiteral( "size" )].toDouble();
1539  if ( props.contains( QStringLiteral( "angle" ) ) )
1540  angle = props[QStringLiteral( "angle" )].toDouble();
1541  if ( props.contains( QStringLiteral( "scale_method" ) ) )
1542  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )] );
1543 
1544  QgsFilledMarkerSymbolLayer *m = new QgsFilledMarkerSymbolLayer( decodeShape( name ), size, angle, scaleMethod );
1545  if ( props.contains( QStringLiteral( "offset" ) ) )
1546  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
1547  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1548  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
1549  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1550  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
1551  if ( props.contains( QStringLiteral( "size_unit" ) ) )
1552  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
1553  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1554  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
1555  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1556  {
1557  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1558  }
1559  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1560  {
1561  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1562  }
1563 
1565 
1566  m->restoreOldDataDefinedProperties( props );
1567 
1568  return m;
1569 }
1570 
1572 {
1573  return QStringLiteral( "FilledMarker" );
1574 }
1575 
1577 {
1578  if ( mFill )
1579  {
1580  mFill->startRender( context.renderContext(), context.fields() );
1581  }
1582 
1584 }
1585 
1587 {
1588  if ( mFill )
1589  {
1590  mFill->stopRender( context.renderContext() );
1591  }
1592 }
1593 
1595 {
1596  QgsStringMap map;
1597  map[QStringLiteral( "name" )] = encodeShape( mShape );
1598  map[QStringLiteral( "size" )] = QString::number( mSize );
1599  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1600  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1601  map[QStringLiteral( "angle" )] = QString::number( mAngle );
1602  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1603  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1604  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1605  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1606  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1607  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1608 
1609  if ( mFill )
1610  {
1611  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mFill->color() );
1612  }
1613  return map;
1614 }
1615 
1617 {
1619  copyPaintEffect( m );
1621  m->setSubSymbol( mFill->clone() );
1622  return m;
1623 }
1624 
1626 {
1627  return mFill.get();
1628 }
1629 
1631 {
1632  if ( symbol && symbol->type() == QgsSymbol::Fill )
1633  {
1634  mFill.reset( static_cast<QgsFillSymbol *>( symbol ) );
1635  return true;
1636  }
1637  else
1638  {
1639  delete symbol;
1640  return false;
1641  }
1642 }
1643 
1645 {
1646  if ( mFill )
1647  {
1648  return QgsSymbolLayerUtils::estimateMaxSymbolBleed( mFill.get(), context );
1649  }
1650  return 0;
1651 }
1652 
1654 {
1655  QSet<QString> attr = QgsSimpleMarkerSymbolLayerBase::usedAttributes( context );
1656  if ( mFill )
1657  attr.unite( mFill->usedAttributes( context ) );
1658  return attr;
1659 }
1660 
1662 {
1664  return true;
1665  if ( mFill && mFill->hasDataDefinedProperties() )
1666  return true;
1667  return false;
1668 }
1669 
1671 {
1672  mColor = c;
1673  if ( mFill )
1674  mFill->setColor( c );
1675 }
1676 
1678 {
1679  return mFill ? mFill->color() : mColor;
1680 }
1681 
1682 void QgsFilledMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, QgsSimpleMarkerSymbolLayerBase::Shape shape, const QPolygonF &polygon, const QPainterPath &path )
1683 {
1684  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1685  //of the rendered point!
1686 
1687  QPainter *p = context.renderContext().painter();
1688  if ( !p )
1689  {
1690  return;
1691  }
1692 
1693  if ( shapeIsFilled( shape ) )
1694  {
1695  p->setBrush( Qt::red );
1696  }
1697  else
1698  {
1699  p->setBrush( Qt::NoBrush );
1700  }
1701  p->setPen( Qt::black );
1702 
1703  if ( !polygon.isEmpty() )
1704  {
1705  mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
1706  }
1707  else
1708  {
1709  QPolygonF poly = path.toFillPolygon();
1710  mFill->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
1711  }
1712 
1713 
1714 }
1715 
1716 
1718 
1719 
1721 {
1722  mPath = path;
1723  mSize = size;
1724  mAngle = angle;
1725  mOffset = QPointF( 0, 0 );
1727  mStrokeWidth = 0.2;
1728  mStrokeWidthUnit = QgsUnitTypes::RenderMillimeters;
1729  mColor = QColor( 35, 35, 35 );
1730  mStrokeColor = QColor( 35, 35, 35 );
1731  updateDefaultAspectRatio();
1732 }
1733 
1734 
1736 {
1737  QString name;
1738  double size = DEFAULT_SVGMARKER_SIZE;
1739  double angle = DEFAULT_SVGMARKER_ANGLE;
1741 
1742  if ( props.contains( QStringLiteral( "name" ) ) )
1743  name = props[QStringLiteral( "name" )];
1744  if ( props.contains( QStringLiteral( "size" ) ) )
1745  size = props[QStringLiteral( "size" )].toDouble();
1746  if ( props.contains( QStringLiteral( "angle" ) ) )
1747  angle = props[QStringLiteral( "angle" )].toDouble();
1748  if ( props.contains( QStringLiteral( "scale_method" ) ) )
1749  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )] );
1750 
1751  QgsSvgMarkerSymbolLayer *m = new QgsSvgMarkerSymbolLayer( name, size, angle, scaleMethod );
1752 
1753  //we only check the svg default parameters if necessary, since it could be expensive
1754  if ( !props.contains( QStringLiteral( "fill" ) ) && !props.contains( QStringLiteral( "color" ) ) && !props.contains( QStringLiteral( "outline" ) ) &&
1755  !props.contains( QStringLiteral( "outline_color" ) ) && !props.contains( QStringLiteral( "outline-width" ) ) && !props.contains( QStringLiteral( "outline_width" ) ) )
1756  {
1757  QColor fillColor, strokeColor;
1758  double fillOpacity = 1.0;
1759  double strokeOpacity = 1.0;
1760  double strokeWidth;
1761  bool hasFillParam = false, hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
1762  bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
1763  QgsApplication::svgCache()->containsParams( name, hasFillParam, hasDefaultFillColor, fillColor,
1764  hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
1765  hasStrokeParam, hasDefaultStrokeColor, strokeColor,
1766  hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
1767  hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
1768  if ( hasDefaultFillColor )
1769  {
1770  m->setFillColor( fillColor );
1771  }
1772  if ( hasDefaultFillOpacity )
1773  {
1774  QColor c = m->fillColor();
1775  c.setAlphaF( fillOpacity );
1776  m->setFillColor( c );
1777  }
1778  if ( hasDefaultStrokeColor )
1779  {
1780  m->setStrokeColor( strokeColor );
1781  }
1782  if ( hasDefaultStrokeWidth )
1783  {
1784  m->setStrokeWidth( strokeWidth );
1785  }
1786  if ( hasDefaultStrokeOpacity )
1787  {
1788  QColor c = m->strokeColor();
1789  c.setAlphaF( strokeOpacity );
1790  m->setStrokeColor( c );
1791  }
1792  }
1793 
1794  if ( props.contains( QStringLiteral( "size_unit" ) ) )
1795  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
1796  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1797  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
1798  if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
1799  m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
1800  if ( props.contains( QStringLiteral( "offset" ) ) )
1801  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
1802  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1803  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
1804  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1805  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
1806  if ( props.contains( QStringLiteral( "fill" ) ) )
1807  {
1808  //pre 2.5 projects used "fill"
1809  m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "fill" )] ) );
1810  }
1811  else if ( props.contains( QStringLiteral( "color" ) ) )
1812  {
1813  m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] ) );
1814  }
1815  if ( props.contains( QStringLiteral( "outline" ) ) )
1816  {
1817  //pre 2.5 projects used "outline"
1818  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline" )] ) );
1819  }
1820  else if ( props.contains( QStringLiteral( "outline_color" ) ) )
1821  {
1822  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )] ) );
1823  }
1824  else if ( props.contains( QStringLiteral( "line_color" ) ) )
1825  {
1826  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )] ) );
1827  }
1828 
1829  if ( props.contains( QStringLiteral( "outline-width" ) ) )
1830  {
1831  //pre 2.5 projects used "outline-width"
1832  m->setStrokeWidth( props[QStringLiteral( "outline-width" )].toDouble() );
1833  }
1834  else if ( props.contains( QStringLiteral( "outline_width" ) ) )
1835  {
1836  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
1837  }
1838  else if ( props.contains( QStringLiteral( "line_width" ) ) )
1839  {
1840  m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
1841  }
1842 
1843  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
1844  {
1845  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )] ) );
1846  }
1847  else if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
1848  {
1849  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )] ) );
1850  }
1851  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
1852  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )] ) );
1853 
1854  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1855  {
1856  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1857  }
1858  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1859  {
1860  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1861  }
1862 
1863  m->restoreOldDataDefinedProperties( props );
1864 
1866 
1867  return m;
1868 }
1869 
1871 {
1872  QgsStringMap::iterator it = properties.find( QStringLiteral( "name" ) );
1873  if ( it != properties.end() )
1874  {
1875  if ( saving )
1876  it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value(), pathResolver );
1877  else
1878  it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value(), pathResolver );
1879  }
1880 }
1881 
1882 void QgsSvgMarkerSymbolLayer::setPath( const QString &path )
1883 {
1884  mPath = path;
1885  QColor defaultFillColor, defaultStrokeColor;
1886  double strokeWidth, fillOpacity, strokeOpacity;
1887  bool hasFillParam = false, hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
1888  bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
1889  QgsApplication::svgCache()->containsParams( path, hasFillParam, hasDefaultFillColor, defaultFillColor,
1890  hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
1891  hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
1892  hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
1893  hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
1894 
1895  double newFillOpacity = hasFillOpacityParam ? fillColor().alphaF() : 1.0;
1896  double newStrokeOpacity = hasStrokeOpacityParam ? strokeColor().alphaF() : 1.0;
1897 
1898  if ( hasDefaultFillColor )
1899  {
1900  defaultFillColor.setAlphaF( newFillOpacity );
1901  setFillColor( defaultFillColor );
1902  }
1903  if ( hasDefaultFillOpacity )
1904  {
1905  QColor c = fillColor();
1906  c.setAlphaF( fillOpacity );
1907  setFillColor( c );
1908  }
1909  if ( hasDefaultStrokeColor )
1910  {
1911  defaultStrokeColor.setAlphaF( newStrokeOpacity );
1912  setStrokeColor( defaultStrokeColor );
1913  }
1914  if ( hasDefaultStrokeWidth )
1915  {
1916  setStrokeWidth( strokeWidth );
1917  }
1918  if ( hasDefaultStrokeOpacity )
1919  {
1920  QColor c = strokeColor();
1921  c.setAlphaF( strokeOpacity );
1922  setStrokeColor( c );
1923  }
1924 
1925  updateDefaultAspectRatio();
1926 }
1927 
1929 {
1930  if ( mDefaultAspectRatio == 0.0 )
1931  {
1932  //size
1933  double size = mSize;
1934  //assume 88 dpi as standard value
1935  double widthScaleFactor = 3.465;
1936  QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( mPath, size, mColor, mStrokeColor, mStrokeWidth, widthScaleFactor );
1937  // set default aspect ratio
1938  mDefaultAspectRatio = svgViewbox.isValid() ? svgViewbox.height() / svgViewbox.width() : 0.0;
1939  }
1940  return mDefaultAspectRatio;
1941 }
1942 
1944 {
1945  bool aPreservedAspectRatio = preservedAspectRatio();
1946  if ( aPreservedAspectRatio && !par )
1947  {
1948  mFixedAspectRatio = mDefaultAspectRatio;
1949  }
1950  else if ( !aPreservedAspectRatio && par )
1951  {
1952  mFixedAspectRatio = 0.0;
1953  }
1954  return preservedAspectRatio();
1955 }
1956 
1957 
1959 {
1960  return QStringLiteral( "SvgMarker" );
1961 }
1962 
1964 {
1965  QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
1966  Q_UNUSED( context )
1967 }
1968 
1970 {
1971  Q_UNUSED( context )
1972 }
1973 
1975 {
1976  QPainter *p = context.renderContext().painter();
1977  if ( !p )
1978  return;
1979 
1980  bool hasDataDefinedSize = false;
1981  double scaledSize = calculateSize( context, hasDataDefinedSize );
1982  double size = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
1983 
1984  //don't render symbols with size below one or above 10,000 pixels
1985  if ( static_cast< int >( size ) < 1 || 10000.0 < size )
1986  {
1987  return;
1988  }
1989 
1990  p->save();
1991 
1992  bool hasDataDefinedAspectRatio = false;
1993  double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
1994 
1995  QPointF outputOffset;
1996  double angle = 0.0;
1997  calculateOffsetAndRotation( context, scaledSize, outputOffset, angle );
1998 
1999  p->translate( point + outputOffset );
2000 
2001  bool rotated = !qgsDoubleNear( angle, 0 );
2002  if ( rotated )
2003  p->rotate( angle );
2004 
2005  QString path = mPath;
2007  {
2008  context.setOriginalValueVariable( mPath );
2010  context.renderContext().pathResolver() );
2011  }
2012 
2013  double strokeWidth = mStrokeWidth;
2015  {
2016  context.setOriginalValueVariable( mStrokeWidth );
2018  }
2019  strokeWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
2020 
2021  QColor fillColor = mColor;
2023  {
2026  }
2027 
2028  QColor strokeColor = mStrokeColor;
2030  {
2033  }
2034 
2035  bool fitsInCache = true;
2036  bool usePict = true;
2037  double hwRatio = 1.0;
2038  if ( !context.renderContext().forceVectorOutput() && !rotated )
2039  {
2040  QImage img = QgsApplication::svgCache()->svgAsImage( path, size, fillColor, strokeColor, strokeWidth,
2041  context.renderContext().scaleFactor(), fitsInCache, aspectRatio,
2043  if ( fitsInCache && img.width() > 1 )
2044  {
2045  usePict = false;
2046  //consider transparency
2047  if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2048  {
2049  QImage transparentImage = img.copy();
2050  QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2051  p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
2052  hwRatio = static_cast< double >( transparentImage.height() ) / static_cast< double >( transparentImage.width() );
2053  }
2054  else
2055  {
2056  p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2057  hwRatio = static_cast< double >( img.height() ) / static_cast< double >( img.width() );
2058  }
2059  }
2060  }
2061 
2062  if ( usePict || !fitsInCache )
2063  {
2064  p->setOpacity( context.opacity() );
2065  QPicture pct = QgsApplication::svgCache()->svgAsPicture( path, size, fillColor, strokeColor, strokeWidth,
2066  context.renderContext().scaleFactor(), context.renderContext().forceVectorOutput(), aspectRatio,
2068  if ( pct.width() > 1 )
2069  {
2070  p->save();
2071  _fixQPictureDPI( p );
2072  p->drawPicture( 0, 0, pct );
2073  p->restore();
2074  hwRatio = static_cast< double >( pct.height() ) / static_cast< double >( pct.width() );
2075  }
2076  }
2077 
2078  if ( context.selected() )
2079  {
2080  QPen pen( context.renderContext().selectionColor() );
2081  double penWidth = context.renderContext().convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters );
2082  if ( penWidth > size / 20 )
2083  {
2084  // keep the pen width from covering symbol
2085  penWidth = size / 20;
2086  }
2087  double penOffset = penWidth / 2;
2088  pen.setWidth( penWidth );
2089  p->setPen( pen );
2090  p->setBrush( Qt::NoBrush );
2091  double wSize = size + penOffset;
2092  double hSize = size * hwRatio + penOffset;
2093  p->drawRect( QRectF( -wSize / 2.0, -hSize / 2.0, wSize, hSize ) );
2094  }
2095 
2096  p->restore();
2097 
2099  {
2100  // workaround issue with nested QPictures forgetting antialiasing flag - see https://github.com/qgis/QGIS/issues/22909
2101  p->setRenderHint( QPainter::Antialiasing );
2102  }
2103 
2104 }
2105 
2106 double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2107 {
2108  double scaledSize = mSize;
2110 
2111  bool ok = true;
2112  if ( hasDataDefinedSize )
2113  {
2114  context.setOriginalValueVariable( mSize );
2116  }
2117  else
2118  {
2120  if ( hasDataDefinedSize )
2121  {
2122  context.setOriginalValueVariable( mSize );
2124  }
2125  }
2126 
2127  if ( hasDataDefinedSize && ok )
2128  {
2129  switch ( mScaleMethod )
2130  {
2131  case QgsSymbol::ScaleArea:
2132  scaledSize = std::sqrt( scaledSize );
2133  break;
2135  break;
2136  }
2137  }
2138 
2139  return scaledSize;
2140 }
2141 
2142 double QgsSvgMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2143 {
2145  if ( !hasDataDefinedAspectRatio )
2146  return mFixedAspectRatio;
2147 
2148  if ( !mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyHeight ) && mFixedAspectRatio <= 0.0 )
2149  return 0.0;
2150 
2151  double scaledAspectRatio = mDefaultAspectRatio;
2152  if ( mFixedAspectRatio > 0.0 )
2153  scaledAspectRatio = mFixedAspectRatio;
2154 
2155  double defaultHeight = mSize * scaledAspectRatio;
2156  scaledAspectRatio = defaultHeight / scaledSize;
2157 
2158  bool ok = true;
2159  double scaledHeight = scaledSize * scaledAspectRatio;
2161  {
2162  context.setOriginalValueVariable( defaultHeight );
2163  scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyHeight, context.renderContext().expressionContext(), defaultHeight, &ok );
2164  }
2165 
2166  if ( hasDataDefinedAspectRatio && ok )
2167  {
2168  switch ( mScaleMethod )
2169  {
2170  case QgsSymbol::ScaleArea:
2171  scaledHeight = sqrt( scaledHeight );
2172  break;
2174  break;
2175  }
2176  }
2177 
2178  scaledAspectRatio = scaledHeight / scaledSize;
2179 
2180  return scaledAspectRatio;
2181 }
2182 
2183 void QgsSvgMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledSize, QPointF &offset, double &angle ) const
2184 {
2185  //offset
2186  double offsetX = 0;
2187  double offsetY = 0;
2188  markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
2189  offset = QPointF( offsetX, offsetY );
2190 
2191  angle = mAngle + mLineAngle;
2193  {
2194  context.setOriginalValueVariable( mAngle );
2196  }
2197 
2199  if ( hasDataDefinedRotation )
2200  {
2201  // For non-point markers, "dataDefinedRotation" means following the
2202  // shape (shape-data defined). For them, "field-data defined" does
2203  // not work at all. TODO: if "field-data defined" ever gets implemented
2204  // we'll need a way to distinguish here between the two, possibly
2205  // using another flag in renderHints()
2206  const QgsFeature *f = context.feature();
2207  if ( f )
2208  {
2209  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
2210  {
2211  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2212  angle += m2p.mapRotation();
2213  }
2214  }
2215  }
2216 
2217  if ( angle )
2218  offset = _rotatedOffset( offset, angle );
2219 }
2220 
2221 
2223 {
2224  QgsStringMap map;
2225  map[QStringLiteral( "name" )] = mPath;
2226  map[QStringLiteral( "size" )] = QString::number( mSize );
2227  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2228  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2229  map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2230  map[QStringLiteral( "angle" )] = QString::number( mAngle );
2231  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2232  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2233  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2234  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2235  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
2236  map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
2237  map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
2238  map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2239  map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2240  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
2241  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
2242  return map;
2243 }
2244 
2246 {
2248  m->setFixedAspectRatio( mFixedAspectRatio );
2249  m->setColor( mColor );
2250  m->setStrokeColor( mStrokeColor );
2251  m->setStrokeWidth( mStrokeWidth );
2252  m->setStrokeWidthUnit( mStrokeWidthUnit );
2253  m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
2254  m->setOffset( mOffset );
2255  m->setOffsetUnit( mOffsetUnit );
2257  m->setSizeUnit( mSizeUnit );
2262  copyPaintEffect( m );
2263  return m;
2264 }
2265 
2267 {
2269  mStrokeWidthUnit = unit;
2270 }
2271 
2273 {
2275  if ( unit != mStrokeWidthUnit )
2276  {
2278  }
2279  return unit;
2280 }
2281 
2283 {
2285  mStrokeWidthMapUnitScale = scale;
2286 }
2287 
2289 {
2290  if ( QgsMarkerSymbolLayer::mapUnitScale() == mStrokeWidthMapUnitScale )
2291  {
2292  return mStrokeWidthMapUnitScale;
2293  }
2294  return QgsMapUnitScale();
2295 }
2296 
2297 void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
2298 {
2299  // <Graphic>
2300  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2301  element.appendChild( graphicElem );
2302 
2303  // encode a parametric SVG reference
2305  double strokeWidth = QgsSymbolLayerUtils::rescaleUom( mStrokeWidth, mStrokeWidthUnit, props );
2306  QgsSymbolLayerUtils::parametricSvgToSld( doc, graphicElem, mPath, mColor, size, mStrokeColor, strokeWidth );
2307 
2308  // <Rotation>
2309  QString angleFunc;
2310  bool ok;
2311  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2312  if ( !ok )
2313  {
2314  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mAngle );
2315  }
2316  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2317  {
2318  angleFunc = QString::number( angle + mAngle );
2319  }
2320 
2321  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2322 
2323  // <Displacement>
2324  QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2325  QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
2326 }
2327 
2329 {
2330  QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
2331 
2332  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
2333  if ( graphicElem.isNull() )
2334  return nullptr;
2335 
2336  QString path, mimeType;
2337  QColor fillColor;
2338  double size;
2339 
2340  if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
2341  return nullptr;
2342 
2343  QString uom = element.attribute( QStringLiteral( "uom" ) );
2344  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
2345 
2346  if ( mimeType != QLatin1String( "image/svg+xml" ) )
2347  return nullptr;
2348 
2349  double angle = 0.0;
2350  QString angleFunc;
2351  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2352  {
2353  bool ok;
2354  double d = angleFunc.toDouble( &ok );
2355  if ( ok )
2356  angle = d;
2357  }
2358 
2359  QPointF offset;
2360  QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, offset );
2361 
2362  QgsSvgMarkerSymbolLayer *m = new QgsSvgMarkerSymbolLayer( path, size );
2363  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
2364  m->setFillColor( fillColor );
2365  //m->setStrokeColor( strokeColor );
2366  //m->setStrokeWidth( strokeWidth );
2367  m->setAngle( angle );
2368  m->setOffset( offset );
2369  return m;
2370 }
2371 
2372 bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
2373 {
2374  //size
2375  double size = mSize;
2376 
2377  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
2378 
2379  bool ok = true;
2380  if ( hasDataDefinedSize )
2381  {
2382  context.setOriginalValueVariable( mSize );
2384  }
2385 
2386  if ( hasDataDefinedSize && ok )
2387  {
2388  switch ( mScaleMethod )
2389  {
2390  case QgsSymbol::ScaleArea:
2391  size = std::sqrt( size );
2392  break;
2394  break;
2395  }
2396  }
2397 
2399  {
2400  size *= mmMapUnitScaleFactor;
2401  }
2402 
2403  //offset, angle
2404  QPointF offset = mOffset;
2405 
2407  {
2409  const QVariant val = mDataDefinedProperties.value( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext(), QString() );
2410  const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
2411  if ( ok )
2412  offset = res;
2413  }
2414  double offsetX = offset.x();
2415  double offsetY = offset.y();
2416 
2417  QPointF outputOffset( offsetX, offsetY );
2418 
2419  double angle = mAngle + mLineAngle;
2421  {
2422  context.setOriginalValueVariable( mAngle );
2424  }
2425 
2426  if ( angle )
2427  outputOffset = _rotatedOffset( outputOffset, angle );
2428 
2429  outputOffset *= e.mapUnitScaleFactor( e.symbologyScale(), mOffsetUnit, e.mapUnits(), context.renderContext().mapToPixel().mapUnitsPerPixel() );
2430 
2431  QString path = mPath;
2433  {
2434  context.setOriginalValueVariable( mPath );
2436  context.renderContext().pathResolver() );
2437  }
2438 
2439  double strokeWidth = mStrokeWidth;
2441  {
2442  context.setOriginalValueVariable( mStrokeWidth );
2444  }
2445  strokeWidth *= e.mapUnitScaleFactor( e.symbologyScale(), mStrokeWidthUnit, e.mapUnits(), context.renderContext().mapToPixel().mapUnitsPerPixel() );
2446 
2447  QColor fillColor = mColor;
2449  {
2452  }
2453 
2454  QColor strokeColor = mStrokeColor;
2456  {
2459  }
2460 
2461  const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, size, fillColor, strokeColor, strokeWidth,
2462  context.renderContext().scaleFactor(), mFixedAspectRatio,
2464 
2465  QSvgRenderer r( svgContent );
2466  if ( !r.isValid() )
2467  return false;
2468 
2469  QgsDxfPaintDevice pd( &e );
2470  pd.setDrawingSize( QSizeF( r.defaultSize() ) );
2471 
2472  QSizeF outSize( r.defaultSize() );
2473  outSize.scale( size, size, Qt::KeepAspectRatio );
2474 
2475  QPainter p;
2476  p.begin( &pd );
2477  if ( !qgsDoubleNear( angle, 0.0 ) )
2478  {
2479  p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
2480  p.rotate( angle );
2481  p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
2482  }
2483  pd.setShift( shift + QPointF( outputOffset.x(), -outputOffset.y() ) );
2484  pd.setOutputSize( QRectF( -outSize.width() / 2.0, -outSize.height() / 2.0, outSize.width(), outSize.height() ) );
2485  pd.setLayer( layerName );
2486  r.render( &p );
2487  p.end();
2488  return true;
2489 }
2490 
2492 {
2493  bool hasDataDefinedSize = false;
2494  double scaledSize = calculateSize( context, hasDataDefinedSize );
2495  scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
2496 
2497  //don't render symbols with size below one or above 10,000 pixels
2498  if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
2499  {
2500  return QRectF();
2501  }
2502 
2503  QPointF outputOffset;
2504  double angle = 0.0;
2505  calculateOffsetAndRotation( context, scaledSize, outputOffset, angle );
2506 
2507  QString path = mPath;
2509  {
2510  context.setOriginalValueVariable( mPath );
2512  context.renderContext().pathResolver() );
2513  }
2514 
2515  double strokeWidth = mStrokeWidth;
2517  {
2518  context.setOriginalValueVariable( mStrokeWidth );
2520  }
2521  strokeWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
2522 
2523  //need to get colors to take advantage of cached SVGs
2524  QColor fillColor = mColor;
2526  {
2529  }
2530 
2531  QColor strokeColor = mStrokeColor;
2533  {
2536  }
2537 
2538  QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledSize, fillColor, strokeColor, strokeWidth,
2539  context.renderContext().scaleFactor(), mFixedAspectRatio,
2541 
2542  double scaledHeight = svgViewbox.isValid() ? scaledSize * svgViewbox.height() / svgViewbox.width() : scaledSize;
2543 
2544  QMatrix transform;
2545 
2546  // move to the desired position
2547  transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
2548 
2549  if ( !qgsDoubleNear( angle, 0.0 ) )
2550  transform.rotate( angle );
2551 
2552  //antialiasing
2553  strokeWidth += 1.0 / 2.0;
2554 
2555  QRectF symbolBounds = transform.mapRect( QRectF( -scaledSize / 2.0,
2556  -scaledHeight / 2.0,
2557  scaledSize,
2558  scaledHeight ) );
2559 
2560  //extend bounds by pen width / 2.0
2561  symbolBounds.adjust( -strokeWidth / 2.0, -strokeWidth / 2.0,
2562  strokeWidth / 2.0, strokeWidth / 2.0 );
2563 
2564  return symbolBounds;
2565 
2566 }
2567 
2569 
2571  : mPath( path )
2572 {
2573  mSize = size;
2574  mAngle = angle;
2575  mOffset = QPointF( 0, 0 );
2578 }
2579 
2580 
2582 {
2583  QString path;
2585  double angle = DEFAULT_RASTERMARKER_ANGLE;
2587 
2588  if ( props.contains( QStringLiteral( "imageFile" ) ) )
2589  path = props[QStringLiteral( "imageFile" )];
2590  if ( props.contains( QStringLiteral( "size" ) ) )
2591  size = props[QStringLiteral( "size" )].toDouble();
2592  if ( props.contains( QStringLiteral( "angle" ) ) )
2593  angle = props[QStringLiteral( "angle" )].toDouble();
2594  if ( props.contains( QStringLiteral( "scale_method" ) ) )
2595  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )] );
2596 
2597  QgsRasterMarkerSymbolLayer *m = new QgsRasterMarkerSymbolLayer( path, size, angle, scaleMethod );
2598 
2599  if ( props.contains( QStringLiteral( "alpha" ) ) )
2600  {
2601  m->setOpacity( props[QStringLiteral( "alpha" )].toDouble() );
2602  }
2603 
2604  if ( props.contains( QStringLiteral( "size_unit" ) ) )
2605  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
2606  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
2607  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
2608  if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
2609  m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
2610 
2611  if ( props.contains( QStringLiteral( "offset" ) ) )
2612  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
2613  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
2614  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
2615  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2616  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
2617 
2618  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2619  {
2620  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2621  }
2622  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2623  {
2624  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2625  }
2626 
2627  m->restoreOldDataDefinedProperties( props );
2629 
2630  return m;
2631 }
2632 
2634 {
2635  QgsStringMap::iterator it = properties.find( QStringLiteral( "name" ) );
2636  if ( it != properties.end() )
2637  {
2638  if ( saving )
2639  it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value(), pathResolver );
2640  else
2641  it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value(), pathResolver );
2642  }
2643 }
2644 
2645 void QgsRasterMarkerSymbolLayer::setPath( const QString &path )
2646 {
2647  mPath = path;
2649 }
2650 
2652 {
2653  bool aPreservedAspectRatio = preservedAspectRatio();
2654  if ( aPreservedAspectRatio && !par )
2655  {
2657  }
2658  else if ( !aPreservedAspectRatio && par )
2659  {
2660  mFixedAspectRatio = 0.0;
2661  }
2662  return preservedAspectRatio();
2663 }
2664 
2666 {
2667  if ( mDefaultAspectRatio == 0.0 )
2668  {
2670  mDefaultAspectRatio = ( !size.isNull() && size.isValid() && size.width() > 0 ) ? static_cast< double >( size.height() ) / static_cast< double >( size.width() ) : 0.0;
2671  }
2672  return mDefaultAspectRatio;
2673 }
2674 
2676 {
2677  return QStringLiteral( "RasterMarker" );
2678 }
2679 
2681 {
2682  QPainter *p = context.renderContext().painter();
2683  if ( !p )
2684  return;
2685 
2686  bool hasDataDefinedSize = false;
2687  double scaledSize = calculateSize( context, hasDataDefinedSize );
2688  double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
2689  bool hasDataDefinedAspectRatio = false;
2690  double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
2691  double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
2692 
2693  //don't render symbols with size below one or above 10,000 pixels
2694  if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2695  {
2696  return;
2697  }
2698 
2699  QString path = mPath;
2701  {
2702  context.setOriginalValueVariable( mPath );
2704  if ( preservedAspectRatio() && path != mPath )
2705  {
2706  QSize size = QgsApplication::imageCache()->originalSize( path );
2707  if ( !size.isNull() && size.isValid() && size.width() > 0 )
2708  {
2709  height = width * ( static_cast< double >( size.height() ) / static_cast< double >( size.width() ) );
2710  }
2711  }
2712  }
2713 
2714  if ( path.isEmpty() )
2715  return;
2716 
2717  p->save();
2718 
2719  QPointF outputOffset;
2720  double angle = 0.0;
2721  calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
2722 
2723  p->translate( point + outputOffset );
2724 
2725  bool rotated = !qgsDoubleNear( angle, 0 );
2726  if ( rotated )
2727  p->rotate( angle );
2728 
2729  double opacity = mOpacity;
2731  {
2734  }
2735  opacity *= context.opacity();
2736 
2737  bool cached;
2738  QImage img = QgsApplication::imageCache()->pathAsImage( path, QSize( width, preservedAspectRatio() ? 0 : width * aspectRatio ), preservedAspectRatio(), opacity, cached, ( context.renderContext().flags() & QgsRenderContext::RenderBlocking ) );
2739  if ( !img.isNull() )
2740  {
2741  if ( context.selected() )
2743 
2744  p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2745  }
2746 
2747  p->restore();
2748 }
2749 
2750 double QgsRasterMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2751 {
2752  double scaledSize = mSize;
2754 
2755  bool ok = true;
2756  if ( hasDataDefinedSize )
2757  {
2758  context.setOriginalValueVariable( mSize );
2760  }
2761  else
2762  {
2764  if ( hasDataDefinedSize )
2765  {
2766  context.setOriginalValueVariable( mSize );
2768  }
2769  }
2770 
2771  if ( hasDataDefinedSize && ok )
2772  {
2773  switch ( mScaleMethod )
2774  {
2775  case QgsSymbol::ScaleArea:
2776  scaledSize = std::sqrt( scaledSize );
2777  break;
2779  break;
2780  }
2781  }
2782 
2783  return scaledSize;
2784 }
2785 
2786 double QgsRasterMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2787 {
2789  if ( !hasDataDefinedAspectRatio )
2790  return mFixedAspectRatio;
2791 
2793  return 0.0;
2794 
2795  double scaledAspectRatio = mDefaultAspectRatio;
2796  if ( mFixedAspectRatio > 0.0 )
2797  scaledAspectRatio = mFixedAspectRatio;
2798 
2799  double defaultHeight = mSize * scaledAspectRatio;
2800  scaledAspectRatio = defaultHeight / scaledSize;
2801 
2802  bool ok = true;
2803  double scaledHeight = scaledSize * scaledAspectRatio;
2805  {
2806  context.setOriginalValueVariable( defaultHeight );
2807  scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyHeight, context.renderContext().expressionContext(), defaultHeight, &ok );
2808  }
2809 
2810  if ( hasDataDefinedAspectRatio && ok )
2811  {
2812  switch ( mScaleMethod )
2813  {
2814  case QgsSymbol::ScaleArea:
2815  scaledHeight = sqrt( scaledHeight );
2816  break;
2818  break;
2819  }
2820  }
2821 
2822  scaledAspectRatio = scaledHeight / scaledSize;
2823 
2824  return scaledAspectRatio;
2825 }
2826 
2827 void QgsRasterMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
2828 {
2829  //offset
2830  double offsetX = 0;
2831  double offsetY = 0;
2832  markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
2833  offset = QPointF( offsetX, offsetY );
2834 
2835  angle = mAngle + mLineAngle;
2837  {
2838  context.setOriginalValueVariable( mAngle );
2840  }
2841 
2843  if ( hasDataDefinedRotation )
2844  {
2845  const QgsFeature *f = context.feature();
2846  if ( f )
2847  {
2848  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
2849  {
2850  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2851  angle += m2p.mapRotation();
2852  }
2853  }
2854  }
2855 
2856  if ( angle )
2857  offset = _rotatedOffset( offset, angle );
2858 }
2859 
2860 
2862 {
2863  QgsStringMap map;
2864  map[QStringLiteral( "imageFile" )] = mPath;
2865  map[QStringLiteral( "size" )] = QString::number( mSize );
2866  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2867  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2868  map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2869  map[QStringLiteral( "angle" )] = QString::number( mAngle );
2870  map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
2871  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2872  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2873  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2874  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2875  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
2876  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
2877  return map;
2878 }
2879 
2881 {
2884  m->setOpacity( mOpacity );
2885  m->setOffset( mOffset );
2886  m->setOffsetUnit( mOffsetUnit );
2888  m->setSizeUnit( mSizeUnit );
2893  copyPaintEffect( m );
2894  return m;
2895 }
2896 
2898 {
2900 }
2901 
2903 {
2905 }
2906 
2908 {
2909  bool hasDataDefinedSize = false;
2910  double scaledSize = calculateSize( context, hasDataDefinedSize );
2911  double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
2912  bool hasDataDefinedAspectRatio = false;
2913  double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
2914  double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
2915 
2916  //don't render symbols with size below one or above 10,000 pixels
2917  if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
2918  {
2919  return QRectF();
2920  }
2921 
2922  QPointF outputOffset;
2923  double angle = 0.0;
2924  calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
2925 
2926  QMatrix transform;
2927 
2928  // move to the desired position
2929  transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
2930 
2931  if ( !qgsDoubleNear( angle, 0.0 ) )
2932  transform.rotate( angle );
2933 
2934  QRectF symbolBounds = transform.mapRect( QRectF( -width / 2.0,
2935  -height / 2.0,
2936  width,
2937  height ) );
2938 
2939  return symbolBounds;
2940 }
2941 
2943 
2944 QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QString chr, double pointSize, const QColor &color, double angle )
2945 {
2946  mFontFamily = fontFamily;
2947  mString = chr;
2948  mColor = color;
2949  mAngle = angle;
2950  mSize = pointSize;
2951  mOrigSize = pointSize;
2953  mOffset = QPointF( 0, 0 );
2955  mStrokeColor = DEFAULT_FONTMARKER_BORDERCOLOR;
2956  mStrokeWidth = 0.0;
2957  mStrokeWidthUnit = QgsUnitTypes::RenderMillimeters;
2958  mPenJoinStyle = DEFAULT_FONTMARKER_JOINSTYLE;
2959 }
2960 
2962 {
2963  delete mFontMetrics;
2964 }
2965 
2967 {
2968  QString fontFamily = DEFAULT_FONTMARKER_FONT;
2969  QString string = DEFAULT_FONTMARKER_CHR;
2970  double pointSize = DEFAULT_FONTMARKER_SIZE;
2972  double angle = DEFAULT_FONTMARKER_ANGLE;
2973 
2974  if ( props.contains( QStringLiteral( "font" ) ) )
2975  fontFamily = props[QStringLiteral( "font" )];
2976  if ( props.contains( QStringLiteral( "chr" ) ) && props[QStringLiteral( "chr" )].length() > 0 )
2977  string = props[QStringLiteral( "chr" )];
2978  if ( props.contains( QStringLiteral( "size" ) ) )
2979  pointSize = props[QStringLiteral( "size" )].toDouble();
2980  if ( props.contains( QStringLiteral( "color" ) ) )
2981  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] );
2982  if ( props.contains( QStringLiteral( "angle" ) ) )
2983  angle = props[QStringLiteral( "angle" )].toDouble();
2984 
2985  QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( fontFamily, string, pointSize, color, angle );
2986 
2987  if ( props.contains( QStringLiteral( "outline_color" ) ) )
2988  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )] ) );
2989  if ( props.contains( QStringLiteral( "outline_width" ) ) )
2990  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
2991  if ( props.contains( QStringLiteral( "offset" ) ) )
2992  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
2993  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
2994  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
2995  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2996  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
2997  if ( props.contains( QStringLiteral( "size_unit" ) ) )
2998  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
2999  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3000  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
3001  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
3002  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )] ) );
3003  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
3004  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )] ) );
3005  if ( props.contains( QStringLiteral( "joinstyle" ) ) )
3006  m->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )] ) );
3007  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3008  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3009  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3010  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3011 
3012  m->restoreOldDataDefinedProperties( props );
3013 
3014  return m;
3015 }
3016 
3018 {
3019  return QStringLiteral( "FontMarker" );
3020 }
3021 
3023 {
3024  QColor brushColor = mColor;
3025  QColor penColor = mStrokeColor;
3026 
3027  brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
3028  penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
3029 
3030  mBrush = QBrush( brushColor );
3031  mPen = QPen( penColor );
3032  mPen.setJoinStyle( mPenJoinStyle );
3033  mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
3034 
3035  mFont = QFont( mFontFamily );
3036  const double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3037  mNonZeroFontSize = !qgsDoubleNear( sizePixels, 0.0 );
3038  // if a non zero, but small pixel size results, round up to 2 pixels so that a "dot" is at least visible
3039  // (if we set a <=1 pixel size here Qt will reset the font to a default size, leading to much too large symbols)
3040  mFont.setPixelSize( std::max( 2, static_cast< int >( std::round( sizePixels ) ) ) );
3041  delete mFontMetrics;
3042  mFontMetrics = new QFontMetrics( mFont );
3043 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
3044  mChrWidth = mFontMetrics->width( mString );
3045 #else
3046  mChrWidth = mFontMetrics->horizontalAdvance( mString );
3047 #endif
3048  mChrOffset = QPointF( mChrWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3049  mOrigSize = mSize; // save in case the size would be data defined
3050 
3051  // use caching only when not using a data defined character
3053  if ( mUseCachedPath )
3054  {
3055  QPointF chrOffset = mChrOffset;
3056  double chrWidth;
3057  QString charToRender = characterToRender( context, chrOffset, chrWidth );
3058  mCachedPath = QPainterPath();
3059  mCachedPath.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3060  }
3061 }
3062 
3064 {
3065  Q_UNUSED( context )
3066 }
3067 
3068 QString QgsFontMarkerSymbolLayer::characterToRender( QgsSymbolRenderContext &context, QPointF &charOffset, double &charWidth )
3069 {
3070  charOffset = mChrOffset;
3071  QString stringToRender = mString;
3073  {
3074  context.setOriginalValueVariable( mString );
3076  if ( stringToRender != mString )
3077  {
3078 #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
3079  charWidth = mFontMetrics->width( stringToRender );
3080 #else
3081  charWidth = mFontMetrics->horizontalAdvance( stringToRender );
3082 #endif
3083  charOffset = QPointF( charWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3084  }
3085  }
3086  return stringToRender;
3087 }
3088 
3089 void QgsFontMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
3090  double scaledSize,
3091  bool &hasDataDefinedRotation,
3092  QPointF &offset,
3093  double &angle ) const
3094 {
3095  //offset
3096  double offsetX = 0;
3097  double offsetY = 0;
3098  markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
3099  offset = QPointF( offsetX, offsetY );
3100 
3101  //angle
3102  bool ok = true;
3103  angle = mAngle + mLineAngle;
3105  {
3106  context.setOriginalValueVariable( angle );
3108 
3109  // If the expression evaluation was not successful, fallback to static value
3110  if ( !ok )
3111  angle = mAngle + mLineAngle;
3112  }
3113 
3114  hasDataDefinedRotation = context.renderHints() & QgsSymbol::DynamicRotation;
3115  if ( hasDataDefinedRotation )
3116  {
3117  // For non-point markers, "dataDefinedRotation" means following the
3118  // shape (shape-data defined). For them, "field-data defined" does
3119  // not work at all. TODO: if "field-data defined" ever gets implemented
3120  // we'll need a way to distinguish here between the two, possibly
3121  // using another flag in renderHints()
3122  const QgsFeature *f = context.feature();
3123  if ( f )
3124  {
3125  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
3126  {
3127  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3128  angle += m2p.mapRotation();
3129  }
3130  }
3131  }
3132 
3133  if ( angle )
3134  offset = _rotatedOffset( offset, angle );
3135 }
3136 
3137 double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context )
3138 {
3139  double scaledSize = mSize;
3140  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
3141 
3142  bool ok = true;
3143  if ( hasDataDefinedSize )
3144  {
3145  context.setOriginalValueVariable( mSize );
3147  }
3148 
3149  if ( hasDataDefinedSize && ok )
3150  {
3151  switch ( mScaleMethod )
3152  {
3153  case QgsSymbol::ScaleArea:
3154  scaledSize = std::sqrt( scaledSize );
3155  break;
3157  break;
3158  }
3159  }
3160  return scaledSize;
3161 }
3162 
3164 {
3165  QPainter *p = context.renderContext().painter();
3166  if ( !p || !mNonZeroFontSize )
3167  return;
3168 
3169  QTransform transform;
3170 
3171  bool ok;
3172  QColor brushColor = mColor;
3174  {
3177  }
3178  brushColor = context.selected() ? context.renderContext().selectionColor() : brushColor;
3179  brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
3180  mBrush.setColor( brushColor );
3181 
3182  QColor penColor = mStrokeColor;
3184  {
3187  }
3188  penColor.setAlphaF( penColor.alphaF() * context.opacity() );
3189 
3190  double penWidth = context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3192  {
3193  context.setOriginalValueVariable( mStrokeWidth );
3194  double strokeWidth = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyStrokeWidth, context.renderContext().expressionContext(), mStrokeWidth, &ok );
3195  if ( ok )
3196  {
3197  penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3198  }
3199  }
3200 
3202  {
3205  if ( ok )
3206  {
3207  mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
3208  }
3209  }
3210 
3211  p->save();
3212  p->setBrush( mBrush );
3213  if ( !qgsDoubleNear( penWidth, 0.0 ) )
3214  {
3215  mPen.setColor( penColor );
3216  mPen.setWidthF( penWidth );
3217  p->setPen( mPen );
3218  }
3219  else
3220  {
3221  p->setPen( Qt::NoPen );
3222  }
3223 
3224  QPointF chrOffset = mChrOffset;
3225  double chrWidth;
3226  QString charToRender = characterToRender( context, chrOffset, chrWidth );
3227 
3228  double sizeToRender = calculateSize( context );
3229 
3230  bool hasDataDefinedRotation = false;
3231  QPointF offset;
3232  double angle = 0;
3233  calculateOffsetAndRotation( context, sizeToRender, hasDataDefinedRotation, offset, angle );
3234 
3235  p->translate( point.x() + offset.x(), point.y() + offset.y() );
3236 
3237  if ( !qgsDoubleNear( angle, 0.0 ) )
3238  transform.rotate( angle );
3239 
3240  if ( !qgsDoubleNear( sizeToRender, mOrigSize ) )
3241  {
3242  double s = sizeToRender / mOrigSize;
3243  transform.scale( s, s );
3244  }
3245 
3246  if ( mUseCachedPath )
3247  {
3248  p->drawPath( transform.map( mCachedPath ) );
3249  }
3250  else
3251  {
3252  QPainterPath path;
3253  path.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3254  p->drawPath( transform.map( path ) );
3255  }
3256 
3257  p->restore();
3258 }
3259 
3261 {
3262  QgsStringMap props;
3263  props[QStringLiteral( "font" )] = mFontFamily;
3264  props[QStringLiteral( "chr" )] = mString;
3265  props[QStringLiteral( "size" )] = QString::number( mSize );
3266  props[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3267  props[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3268  props[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
3269  props[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
3270  props[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
3271  props[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
3272  props[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
3273  props[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
3274  props[QStringLiteral( "angle" )] = QString::number( mAngle );
3275  props[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3276  props[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3277  props[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3278  props[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3279  props[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3280  return props;
3281 }
3282 
3284 {
3285  QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( mFontFamily, mString, mSize, mColor, mAngle );
3286  m->setStrokeColor( mStrokeColor );
3287  m->setStrokeWidth( mStrokeWidth );
3288  m->setStrokeWidthUnit( mStrokeWidthUnit );
3289  m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
3290  m->setPenJoinStyle( mPenJoinStyle );
3291  m->setOffset( mOffset );
3292  m->setOffsetUnit( mOffsetUnit );
3294  m->setSizeUnit( mSizeUnit );
3299  copyPaintEffect( m );
3300  return m;
3301 }
3302 
3303 void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
3304 {
3305  // <Graphic>
3306  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
3307  element.appendChild( graphicElem );
3308 
3309  QString fontPath = QStringLiteral( "ttf://%1" ).arg( mFontFamily );
3310  int markIndex = !mString.isEmpty() ? mString.at( 0 ).unicode() : 0;
3312  QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, QStringLiteral( "ttf" ), &markIndex, mColor, size );
3313 
3314  // <Rotation>
3315  QString angleFunc;
3316  bool ok;
3317  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
3318  if ( !ok )
3319  {
3320  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mAngle );
3321  }
3322  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
3323  {
3324  angleFunc = QString::number( angle + mAngle );
3325  }
3326  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
3327 
3328  // <Displacement>
3329  QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
3330  QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
3331 }
3332 
3334 {
3335  QPointF chrOffset = mChrOffset;
3336  double chrWidth = mChrWidth;
3337  //calculate width of rendered character
3338  ( void )characterToRender( context, chrOffset, chrWidth );
3339 
3340  if ( !mFontMetrics )
3341  mFontMetrics = new QFontMetrics( mFont );
3342 
3343  double scaledSize = calculateSize( context );
3344  if ( !qgsDoubleNear( scaledSize, mOrigSize ) )
3345  {
3346  chrWidth *= scaledSize / mOrigSize;
3347  }
3348 
3349  bool hasDataDefinedRotation = false;
3350  QPointF offset;
3351  double angle = 0;
3352  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
3353  scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3354 
3355  QMatrix transform;
3356 
3357  // move to the desired position
3358  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
3359 
3360  if ( !qgsDoubleNear( angle, 0.0 ) )
3361  transform.rotate( angle );
3362 
3363  QRectF symbolBounds = transform.mapRect( QRectF( -chrWidth / 2.0,
3364  -scaledSize / 2.0,
3365  chrWidth,
3366  scaledSize ) );
3367  return symbolBounds;
3368 }
3369 
3371 {
3372  QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
3373 
3374  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
3375  if ( graphicElem.isNull() )
3376  return nullptr;
3377 
3378  QString name, format;
3379  QColor color;
3380  double size;
3381  int chr;
3382 
3383  if ( !QgsSymbolLayerUtils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
3384  return nullptr;
3385 
3386  if ( !name.startsWith( QLatin1String( "ttf://" ) ) || format != QLatin1String( "ttf" ) )
3387  return nullptr;
3388 
3389  QString fontFamily = name.mid( 6 );
3390 
3391  double angle = 0.0;
3392  QString angleFunc;
3393  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
3394  {
3395  bool ok;
3396  double d = angleFunc.toDouble( &ok );
3397  if ( ok )
3398  angle = d;
3399  }
3400 
3401  QPointF offset;
3402  QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, offset );
3403 
3404  QString uom = element.attribute( QStringLiteral( "uom" ) );
3405  offset.setX( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.x() ) );
3406  offset.setY( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.y() ) );
3407  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
3408 
3409  QgsMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( fontFamily, QChar( chr ), size, color );
3410  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
3411  m->setAngle( angle );
3412  m->setOffset( offset );
3413  return m;
3414 }
3415 
bool prepareCache(QgsSymbolRenderContext &context)
Prepares cache image.
void setOffset(QPointF offset)
Sets the marker&#39;s offset, which is the horizontal and vertical displacement which the rendered marker...
static void wellKnownMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &name, const QColor &color, const QColor &strokeColor, Qt::PenStyle strokeStyle, double strokeWidth=-1, double size=-1)
#define DEFAULT_SIMPLEMARKER_SIZE
QgsMapUnitScale mapUnitScale() const override
static QgsSvgCache * svgCache()
Returns the application&#39;s SVG cache, used for caching SVG images and handling parameter replacement w...
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
bool writeDxf(QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift=QPointF(0.0, 0.0)) const override
write as DXF
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...
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
QString ogrFeatureStyle(double mmScaleFactor, double mapUnitScaleFactor) const override
void setOffsetUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol&#39;s offset.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsSimpleMarkerSymbolLayer from an SLD XML element.
const QgsPathResolver & pathResolver() const
Returns the path resolver for conversion between relative and absolute paths during rendering operati...
static QgsSymbol::ScaleMethod decodeScaleMethod(const QString &str)
double symbologyScale() const
Returns the reference scale for output.
Definition: qgsdxfexport.h:221
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
Sets the stroke width map unit scale.
#define DEFAULT_RASTERMARKER_SIZE
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)
void clipValueToMapUnitScale(double &value, const QgsMapUnitScale &scale, double pixelToMMFactor) const
Clips value to scale minimum/maximum.
double defaultAspectRatio() const
Returns the default marker aspect ratio between width and height, 0 if not yet calculated.
static QPointF toPoint(const QVariant &value, bool *ok=nullptr)
Converts a value to a point.
void setColor(const QColor &c) override
The fill color.
QColor mStrokeColor
Stroke color.
QgsFields fields() const
Fields of the layer.
Definition: qgssymbol.h:809
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
QgsRasterMarkerSymbolLayer(const QString &path=QString(), double size=DEFAULT_SVGMARKER_SIZE, double angle=DEFAULT_SVGMARKER_ANGLE, QgsSymbol::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructs raster marker symbol layer with picture from given absolute path to a raster image file...
Qt::PenStyle mStrokeStyle
Stroke style.
Calculate scale by the diameter.
Definition: qgssymbol.h:98
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:62
void setMapUnitScale(const QgsMapUnitScale &scale) override
A paint device for drawing into dxf files.
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...
#define DEFAULT_SIMPLEMARKER_NAME
Use antialiasing while drawing.
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit u)
Sets the unit for the width of the marker&#39;s stroke.
void setMapUnitScale(const QgsMapUnitScale &scale) override
void setStrokeStyle(Qt::PenStyle strokeStyle)
Sets the marker&#39;s stroke style (e.g., solid, dashed, etc)
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
Simple marker symbol layer, consisting of a rendered shape with solid fill color and an stroke...
void writePolyline(const QgsPointSequence &line, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Draw dxf primitives (LWPOLYLINE)
Abstract base class for simple marker symbol layers.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsFontMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
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)
Gets SVG as QImage.
Right facing arrow head (unfilled, lines only)
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.
static QgsFillSymbol * createSimple(const QgsStringMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties. ...
Definition: qgssymbol.cpp:1310
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
static QgsImageCache * imageCache()
Returns the application&#39;s image cache, used for caching resampled versions of raster images...
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:315
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
double mLineAngle
Line rotation angle (see setLineAngle() for details)
void setDrawingSize(QSizeF size)
QgsUnitTypes::RenderUnit mSizeUnit
Marker size unit.
virtual QColor strokeColor() const
Gets stroke color.
QgsMapUnitScale mapUnitScale() const override
QgsMapUnitScale mStrokeWidthMapUnitScale
Stroke width map unit scale.
static bool displacementFromSldElement(QDomElement &element, QPointF &offset)
static QPointF decodePoint(const QString &string)
Decodes a QSizeF from a string.
#define DEG2RAD(x)
void setStrokeColor(const QColor &color) override
Sets the marker&#39;s stroke color.
void writeSldMarker(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
Writes the symbol layer definition as a SLD XML element.
static void externalMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &path, const QString &format, int *markIndex=nullptr, const QColor &color=QColor(), double size=-1)
void copyPaintEffect(QgsSymbolLayer *destLayer) const
Copies paint effect of this layer to another symbol layer.
void setStrokeColor(const QColor &c) override
Set stroke color.
void setStrokeWidth(double w)
Sets the width of the marker&#39;s stroke.
static const int MAXIMUM_CACHE_WIDTH
Maximum width/height of cache image.
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
#define DEFAULT_FONTMARKER_COLOR
static void createDisplacementElement(QDomDocument &doc, QDomElement &element, QPointF offset)
QBrush mSelBrush
QBrush to use as fill of selected symbols.
Mixed or unknown units.
Definition: qgsunittypes.h:153
static QgsSimpleMarkerSymbolLayerBase::Shape decodeShape(const QString &name, bool *ok=nullptr)
Attempts to decode a string representation of a shape name to the corresponding shape.
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol&#39;s offset.
void restoreOldDataDefinedProperties(const QgsStringMap &stringMap)
Restores older data defined properties from string map.
void setStrokeWidth(double width)
Set&#39;s the marker&#39;s stroke width.
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
static double sizeInPixelsFromSldUom(const QString &uom, double size)
Returns the size scaled in pixels according to the uom attribute.
#define DEFAULT_FONTMARKER_BORDERCOLOR
double updateDefaultAspectRatio()
Calculates the default marker aspect ratio between width and height.
Flags flags() const
Returns combination of flags used for rendering.
double mFixedAspectRatio
The marker fixed aspect ratio.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
QgsUnitTypes::DistanceUnit mapUnits() const
Retrieve map units.
QPointF mOffset
Marker offset.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QColor strokeColor() const override
Returns the marker&#39;s stroke color.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
static bool rotationFromSldElement(QDomElement &element, QString &rotationFunc)
QgsUnitTypes::RenderUnit mStrokeWidthUnit
Stroke width units.
#define DEFAULT_SIMPLEMARKER_JOINSTYLE
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:197
QByteArray svgContent(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0, bool blocking=false)
Gets SVG content.
static QString encodeShape(QgsSimpleMarkerSymbolLayerBase::Shape shape)
Encodes a shape to its string representation.
QgsMapUnitScale mapUnitScale() const override
QMap< QString, QString > QgsStringMap
Definition: qgis.h:694
Rotation of symbol may be changed during rendering and symbol should not be cached.
Definition: qgssymbol.h:105
QSizeF svgViewboxSize(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0, bool blocking=false)
Calculates the viewbox size of a (possibly cached) SVG file.
static QString encodePoint(QPointF point)
Encodes a QPointF to a string.
Name, eg shape name for simple markers.
static Qt::PenJoinStyle decodePenJoinStyle(const QString &str)
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
static QList< QgsSimpleMarkerSymbolLayerBase::Shape > availableShapes()
Returns a list of all available shape types.
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
void writePolygon(const QgsRingSequence &polygon, const QString &layer, const QString &hatchPattern, const QColor &color)
Draw dxf filled polygon (HATCH)
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
double mapRotation() const
Returns current map rotation in degrees.
#define DEFAULT_FONTMARKER_SIZE
QColor strokeColor() const override
Gets stroke color.
void setPenJoinStyle(Qt::PenJoinStyle style)
Sets the stroke join style.
double strokeWidth() const
Returns the width of the marker&#39;s stroke.
static QgsSymbolLayer * createFromSld(QDomElement &element)
void setPath(const QString &path)
Set the marker SVG path.
QPainterPath mPath
Painter path representing shape. If mPolygon is empty then the shape is stored in mPath...
static QString encodeColor(const QColor &color)
Diagonal half square (bottom left half)
virtual bool hasDataDefinedProperties() const
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties...
virtual void setColor(const QColor &color)
The fill color.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
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
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const override
Returns the calculated value of the property with the specified key from within the collection...
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for data defined symbology.
Definition: qgssymbol.cpp:1251
static QString encodePenStyle(Qt::PenStyle style)
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:37
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsFilledMarkerSymbolLayer.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsSimpleMarkerSymbolLayer.
static QString svgSymbolPathToName(const QString &path, const QgsPathResolver &pathResolver)
Determines an SVG symbol&#39;s name from its path.
static double rescaleUom(double size, QgsUnitTypes::RenderUnit unit, const QgsStringMap &props)
Rescales the given size based on the uomScale found in the props, if any is found, otherwise returns the value un-modified.
One third circle (top left third)
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the stroke width.
Raster marker symbol layer class.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
bool shapeToPolygon(Shape shape, QPolygonF &polygon) const
Creates a polygon representing the specified shape.
void setStrokeColor(const QColor &color) override
Set stroke color.
double angle() const
Returns the rotation angle for the marker, in degrees clockwise from north.
QgsUnitTypes::RenderUnit mOffsetUnit
Offset units.
double updateDefaultAspectRatio()
Calculates the default marker aspect ratio between width and height.
void setColor(const QColor &color) override
The fill color.
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties...
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
double opacity() const
Returns the marker opacity.
void setFixedAspectRatio(double ratio)
Set the marker aspect ratio between width and height to be used in rendering, if the value set is low...
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
bool mUsingCache
true if using cached images of markers for drawing.
static void adjustHueSaturation(QImage &image, double saturation, const QColor &colorizeColor=QColor(), double colorizeStrength=1.0)
Alter the hue or saturation of a QImage.
QPen mPen
QPen corresponding to marker&#39;s stroke style.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
Sets the map scale for the width of the marker&#39;s stroke.
#define DEFAULT_SCALE_METHOD
Q_GUI_EXPORT int qt_defaultDpiY()
double calculateSize(QgsSymbolRenderContext &context, bool &hasDataDefinedSize) const
Calculates the desired size of the marker, considering data defined size overrides.
Calculate scale by the area.
Definition: qgssymbol.h:97
double mOpacity
The marker default opacity.
qreal opacity() const
Returns the opacity for the symbol.
Definition: qgssymbol.h:746
double size() const
Returns the symbol size.
bool preservedAspectRatio() const
Returns the preserved aspect ratio value, true if fixed aspect ratio has been lower or equal to 0...
double mStrokeWidth
Stroke width.
QgsMapUnitScale mapUnitScale() const override
#define DEFAULT_RASTERMARKER_ANGLE
QBrush mBrush
QBrush corresponding to marker&#39;s fill style.
QgsSimpleMarkerSymbolLayerBase(QgsSimpleMarkerSymbolLayerBase::Shape shape=Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, QgsSymbol::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructor for QgsSimpleMarkerSymbolLayerBase.
void setHorizontalAnchorPoint(HorizontalAnchorPoint h)
Sets the horizontal anchor point for positioning the symbol.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
static void resolvePaths(QgsStringMap &properties, const QgsPathResolver &pathResolver, bool saving)
Turns relative paths in properties map to absolute when reading and vice versa when writing...
void setSizeMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol&#39;s size.
#define DEFAULT_FONTMARKER_JOINSTYLE
void setLayer(const QString &layer)
static double mapUnitScaleFactor(double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits, double mapUnitsPerPixel=1.0)
Returns scale factor for conversion to map units.
QColor color() const override
The fill color.
void calculateOffsetAndRotation(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle) const
Calculates the marker offset and rotation.
virtual QColor color() const
The fill color.
static void createRotationElement(QDomDocument &doc, QDomElement &element, const QString &rotationFunc)
void setFillColor(const QColor &color) override
Set fill color.
QColor fillColor() const override
Gets fill color.
QColor selectionColor() const
Returns the color to use when rendering selected features.
#define DEFAULT_FONTMARKER_ANGLE
double mAngle
Marker rotation angle, in degrees clockwise from north.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void writeFilledCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius)
Write filled circle (as hatch)
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit unit)
Sets the stroke width unit.
double calculateAspectRatio(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio) const
Calculates the marker aspect ratio between width and height.
void markerOffset(QgsSymbolRenderContext &context, double &offsetX, double &offsetY) const
Calculates the required marker offset, including both the symbol offset and any displacement required...
static double estimateMaxSymbolBleed(QgsSymbol *symbol, const QgsRenderContext &context)
Returns the maximum estimated bleed for the symbol.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsFontMarkerSymbolLayer from a property map (see properties())
QgsMapUnitScale mSizeMapUnitScale
Marker size map unit scale.
Character, eg for font marker symbol layers.
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...
Qt::PenJoinStyle penJoinStyle() const
Returns the marker&#39;s stroke join style (e.g., miter, bevel, etc).
ScaleMethod
Scale method.
Definition: qgssymbol.h:95
static Q_INVOKABLE QgsUnitTypes::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
QgsSimpleMarkerSymbolLayerBase::Shape shape() const
Returns the shape for the rendered marker symbol.
void setOpacity(double opacity)
Set the marker opacity.
double mapUnitsPerPixel() const
Returns current map units per pixel.
QPen mSelPen
QPen to use as stroke of selected symbols.
QString path() const
Returns the marker raster image path.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
QgsRenderContext & renderContext()
Returns a reference to the context&#39;s render context.
Definition: qgssymbol.h:703
QgsSymbol * subSymbol() override
Returns the symbol&#39;s sub symbol, if present.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
static Qt::PenStyle decodePenStyle(const QString &str)
static bool externalGraphicFromSld(QDomElement &element, QString &path, QString &mime, QColor &color, double &size)
void setPath(const QString &path)
Set the marker raster image path.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
bool selected() const
Returns true if symbols should be rendered using the selected symbol coloring and style...
Definition: qgssymbol.h:759
static void resolvePaths(QgsStringMap &properties, const QgsPathResolver &pathResolver, bool saving)
Turns relative paths in properties map to absolute when reading and vice versa when writing...
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
Qt::PenStyle strokeStyle() const
Returns the marker&#39;s stroke style (e.g., solid, dashed, etc)
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets 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.
HorizontalAnchorPoint
Symbol horizontal anchor points.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
virtual void setStrokeColor(const QColor &color)
Set stroke color.
VerticalAnchorPoint
Symbol vertical anchor points.
Render and load remote sources in the same thread to ensure rendering remote sources (svg and images)...
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
bool setPreservedAspectRatio(bool par)
Set preserved the marker aspect ratio between width and height.
QString layerType() const override
Returns a string that represents this layer type.
Q_GUI_EXPORT int qt_defaultDpiX()
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
QgsSymbol::ScaleMethod scaleMethod() const
Returns the method to use for scaling the marker&#39;s size.
QgsRasterMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
QgsExpressionContext & expressionContext()
Gets the expression context.
QString layerType() const override
Returns a string that represents this layer type.
static QString encodeScaleMethod(QgsSymbol::ScaleMethod scaleMethod)
static bool externalMarkerFromSld(QDomElement &element, QString &path, QString &format, int &markIndex, QColor &color, double &size)
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
QVector< QgsPoint > QgsPointSequence
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
bool forceVectorOutput() const
Returns true if rendering operations should use vector operations instead of any faster raster shortc...
QString layerType() const override
Returns a string that represents this layer type.
void setFixedAspectRatio(double ratio)
Set the marker aspect ratio between width and height to be used in rendering, if the value set is low...
QVector< QgsPointSequence > QgsRingSequence
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsFontMarkerSymbolLayer from an SLD XML element.
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol&#39;s size.
QString valueAsString(int key, const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a string...
Stroke style (eg solid, dashed)
#define DEFAULT_SIMPLEMARKER_BORDERCOLOR
Fill symbol.
Definition: qgssymbol.h:88
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns the set of attributes referenced by the layer.
Contains information about the context of a rendering operation.
Abstract base class for marker symbol layers.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
void writeCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius, const QString &lineStyleName, double width)
Write circle (as polyline)
void setShift(QPointF shift)
double calculateAspectRatio(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio) const
Calculates the marker aspect ratio between width and height.
QPainter * painter()
Returns the destination QPainter for the render operation.
virtual void setFillColor(const QColor &color)
Set fill color.
const QgsMapToPixel & mapToPixel() const
Returns the context&#39;s map to pixel transform, which transforms between map coordinates and device coo...
QColor color() const override
The fill color.
SymbolType type() const
Returns the symbol&#39;s type.
Definition: qgssymbol.h:121
QImage mCache
Cached image of marker, if using cached version.
QgsSvgMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
QgsSimpleMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
Struct for storing maximum and minimum scales for measurements in map units.
void setMapUnitScale(const QgsMapUnitScale &scale) override
#define DEFAULT_SIMPLEMARKER_ANGLE
#define DEFAULT_SIMPLEMARKER_COLOR
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
QgsSimpleMarkerSymbolLayer(QgsSimpleMarkerSymbolLayerBase::Shape shape=Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, QgsSymbol::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.
QgsSymbol::ScaleMethod mScaleMethod
Marker size scaling method.
QSize originalSize(const QString &path, bool blocking=false) const
Returns the original size (in pixels) of the image at the specified path.
Qt::PenJoinStyle mPenJoinStyle
Stroke pen join style.
const QgsFeature * feature() const
Returns the current feature being rendered.
Definition: qgssymbol.h:784
QgsSvgMarkerSymbolLayer(const QString &path, double size=DEFAULT_SVGMARKER_SIZE, double angle=DEFAULT_SVGMARKER_ANGLE, QgsSymbol::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructs SVG marker symbol layer with picture from given absolute path to a SVG file...
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer&#39;s subsymbol. takes ownership of the passed symbol.
QColor fillColor() const override
Gets fill color.
HorizontalAnchorPoint mHorizontalAnchorPoint
Horizontal anchor point.
QImage mSelCache
Cached image of selected marker, if using cached version.
#define DEFAULT_FONTMARKER_CHR
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
void writeSldMarker(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
Writes the symbol layer definition as a SLD XML element.
void writeSldMarker(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
Writes the symbol layer definition as a SLD XML element.
static bool wellKnownMarkerFromSld(QDomElement &element, QString &name, QColor &color, QColor &strokeColor, Qt::PenStyle &strokeStyle, double &strokeWidth, double &size)
double mSize
Marker size.
void setAngle(double angle)
Sets the rotation angle for the marker.
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
QString layerType() const override
Returns a string that represents this layer type.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
double mDefaultAspectRatio
The marker default 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
QgsMapUnitScale mOffsetMapUnitScale
Offset map unit scale.
QgsWkbTypes::GeometryType type
Definition: qgsgeometry.h:126
QgsGeometry geometry
Definition: qgsfeature.h:67
VerticalAnchorPoint mVerticalAnchorPoint
Vertical anchor point.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
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)
Gets SVG as QPicture&.
void setFillColor(const QColor &color) override
Set fill color.
static bool shapeIsFilled(QgsSimpleMarkerSymbolLayerBase::Shape shape)
Returns true if a symbol shape has a fill.
static QString svgSymbolNameToPath(const QString &name, const QgsPathResolver &pathResolver)
Determines an SVG symbol&#39;s path from its name.
static QPointF _rotatedOffset(QPointF offset, double angle)
Adjusts a marker offset to account for rotation.
bool prepareMarkerShape(Shape shape)
Prepares the layer for drawing the specified shape (QPolygonF version)
Resolves relative paths into absolute paths and vice versa.
void setMapUnitScale(const QgsMapUnitScale &scale) override
bool setPreservedAspectRatio(bool par)
Set preserved the marker aspect ratio between width and height.
QString layerType() const override
Returns a string that represents this layer type.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Creates a raster marker symbol layer from a string map of properties.
#define DEFAULT_SVGMARKER_SIZE
QPointF offset() const
Returns the marker&#39;s offset, which is the horizontal and vertical displacement which the rendered mar...
QgsFilledMarkerSymbolLayer(QgsSimpleMarkerSymbolLayerBase::Shape shape=Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, QgsSymbol::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructor for QgsFilledMarkerSymbolLayer.
Filled marker symbol layer, consisting of a shape which is rendered using a QgsFillSymbol.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
QImage pathAsImage(const QString &path, const QSize size, const bool keepAspectRatio, const double opacity, bool &fitsInCache, bool blocking=false)
Returns the specified path rendered as an image.
virtual QColor fillColor() const
Gets fill color.
#define DEFAULT_SVGMARKER_ANGLE
static void multiplyImageOpacity(QImage *image, qreal opacity)
Multiplies opacity of image pixel values with a (global) transparency value.
Rotated cross (lines only), "x" shape.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QPolygonF mPolygon
Polygon of points in shape. If polygon is empty then shape is using mPath.
QgsFilledMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
#define DEFAULT_FONTMARKER_FONT
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
QgsPropertyCollection mDataDefinedProperties
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.
bool prepareMarkerPath(Shape symbol)
Prepares the layer for drawing the specified shape (QPainterPath version)
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:145
static QColor decodeColor(const QString &str)
void copyDataDefinedProperties(QgsSymbolLayer *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
void setOutputSize(const QRectF &r)
void setVerticalAnchorPoint(VerticalAnchorPoint v)
Sets the vertical anchor point for positioning the symbol.
QgsSymbol::RenderHints renderHints() const
Returns the rendering hint flags for the symbol.
Definition: qgssymbol.h:771
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...