QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
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  //angle
649  bool ok = true;
650  angle = mAngle + mLineAngle;
651  bool usingDataDefinedRotation = false;
653  {
654  context.setOriginalValueVariable( angle );
656  usingDataDefinedRotation = ok;
657  }
658 
659  hasDataDefinedRotation = context.renderHints() & QgsSymbol::DynamicRotation || usingDataDefinedRotation;
660  if ( hasDataDefinedRotation )
661  {
662  // For non-point markers, "dataDefinedRotation" means following the
663  // shape (shape-data defined). For them, "field-data defined" does
664  // not work at all. TODO: if "field-data defined" ever gets implemented
665  // we'll need a way to distinguish here between the two, possibly
666  // using another flag in renderHints()
667  const QgsFeature *f = context.feature();
668  if ( f )
669  {
670  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
671  {
672  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
673  angle += m2p.mapRotation();
674  }
675  }
676  }
677 
678  if ( angle )
679  offset = _rotatedOffset( offset, angle );
680 }
681 
682 
683 //
684 // QgsSimpleMarkerSymbolLayer
685 //
686 
688  : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
689  , mStrokeColor( strokeColor )
690  , mPenJoinStyle( penJoinStyle )
691 {
692  mColor = color;
693 }
694 
696 {
697  Shape shape = Circle;
700  Qt::PenJoinStyle penJoinStyle = DEFAULT_SIMPLEMARKER_JOINSTYLE;
704 
705  if ( props.contains( QStringLiteral( "name" ) ) )
706  {
707  shape = decodeShape( props[QStringLiteral( "name" )] );
708  }
709  if ( props.contains( QStringLiteral( "color" ) ) )
710  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] );
711  if ( props.contains( QStringLiteral( "color_border" ) ) )
712  {
713  //pre 2.5 projects use "color_border"
714  strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color_border" )] );
715  }
716  else if ( props.contains( QStringLiteral( "outline_color" ) ) )
717  {
718  strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )] );
719  }
720  else if ( props.contains( QStringLiteral( "line_color" ) ) )
721  {
722  strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )] );
723  }
724  if ( props.contains( QStringLiteral( "joinstyle" ) ) )
725  {
726  penJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )] );
727  }
728  if ( props.contains( QStringLiteral( "size" ) ) )
729  size = props[QStringLiteral( "size" )].toDouble();
730  if ( props.contains( QStringLiteral( "angle" ) ) )
731  angle = props[QStringLiteral( "angle" )].toDouble();
732  if ( props.contains( QStringLiteral( "scale_method" ) ) )
733  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )] );
734 
735  QgsSimpleMarkerSymbolLayer *m = new QgsSimpleMarkerSymbolLayer( shape, size, angle, scaleMethod, color, strokeColor, penJoinStyle );
736  if ( props.contains( QStringLiteral( "offset" ) ) )
737  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
738  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
739  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
740  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
741  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
742  if ( props.contains( QStringLiteral( "size_unit" ) ) )
743  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
744  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
745  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
746 
747  if ( props.contains( QStringLiteral( "outline_style" ) ) )
748  {
749  m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "outline_style" )] ) );
750  }
751  else if ( props.contains( QStringLiteral( "line_style" ) ) )
752  {
753  m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "line_style" )] ) );
754  }
755  if ( props.contains( QStringLiteral( "outline_width" ) ) )
756  {
757  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
758  }
759  else if ( props.contains( QStringLiteral( "line_width" ) ) )
760  {
761  m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
762  }
763  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
764  {
765  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )] ) );
766  }
767  if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
768  {
769  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )] ) );
770  }
771  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
772  {
773  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )] ) );
774  }
775 
776  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
777  {
778  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
779  }
780  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
781  {
782  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
783  }
784 
786 
787  return m;
788 }
789 
790 
792 {
793  return QStringLiteral( "SimpleMarker" );
794 }
795 
797 {
799 
800  QColor brushColor = mColor;
801  QColor penColor = mStrokeColor;
802 
803  brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
804  penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
805 
806  mBrush = QBrush( brushColor );
807  mPen = QPen( penColor );
808  mPen.setStyle( mStrokeStyle );
809  mPen.setJoinStyle( mPenJoinStyle );
811 
812  QColor selBrushColor = context.renderContext().selectionColor();
813  QColor selPenColor = selBrushColor == mColor ? selBrushColor : mStrokeColor;
814  if ( context.opacity() < 1 )
815  {
816  selBrushColor.setAlphaF( context.opacity() );
817  selPenColor.setAlphaF( context.opacity() );
818  }
819  mSelBrush = QBrush( selBrushColor );
820  mSelPen = QPen( selPenColor );
821  mSelPen.setStyle( mStrokeStyle );
823 
825  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
826 
827  // use caching only when:
828  // - size, rotation, shape, color, stroke color is not data-defined
829  // - drawing to screen (not printer)
830  mUsingCache = !hasDataDefinedRotation && !hasDataDefinedSize && !context.renderContext().forceVectorOutput()
834 
835  if ( !shapeIsFilled( mShape ) )
836  {
837  // some markers can't be drawn as a polygon (circle, cross)
838  // For these set the selected stroke color to the selected color
839  mSelPen.setColor( selBrushColor );
840  }
841 
842 
843  if ( mUsingCache )
844  {
845  if ( !prepareCache( context ) )
846  {
847  mUsingCache = false;
848  }
849  }
850  else
851  {
852  mCache = QImage();
853  mSelCache = QImage();
854  }
855 }
856 
857 
859 {
860  double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
861  // take into account angle (which is not data-defined otherwise cache wouldn't be used)
862  if ( !qgsDoubleNear( mAngle, 0.0 ) )
863  {
864  scaledSize = ( std::abs( std::sin( mAngle * M_PI / 180 ) ) + std::abs( std::cos( mAngle * M_PI / 180 ) ) ) * scaledSize;
865  }
866  // calculate necessary image size for the cache
867  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
868  int imageSize = ( static_cast< int >( scaledSize ) + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width
869  double center = imageSize / 2.0;
870  if ( imageSize > MAXIMUM_CACHE_WIDTH )
871  {
872  return false;
873  }
874 
875  mCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
876  mCache.fill( 0 );
877 
878  bool needsBrush = shapeIsFilled( mShape );
879 
880  QPainter p;
881  p.begin( &mCache );
882  p.setRenderHint( QPainter::Antialiasing );
883  p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
884  p.setPen( mPen );
885  p.translate( QPointF( center, center ) );
886  drawMarker( &p, context );
887  p.end();
888 
889  // Construct the selected version of the Cache
890 
891  QColor selColor = context.renderContext().selectionColor();
892 
893  mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
894  mSelCache.fill( 0 );
895 
896  p.begin( &mSelCache );
897  p.setRenderHint( QPainter::Antialiasing );
898  p.setBrush( needsBrush ? mSelBrush : Qt::NoBrush );
899  p.setPen( mSelPen );
900  p.translate( QPointF( center, center ) );
901  drawMarker( &p, context );
902  p.end();
903 
904  // Check that the selected version is different. If not, then re-render,
905  // filling the background with the selection color and using the normal
906  // colors for the symbol .. could be ugly!
907 
908  if ( mSelCache == mCache )
909  {
910  p.begin( &mSelCache );
911  p.setRenderHint( QPainter::Antialiasing );
912  p.fillRect( 0, 0, imageSize, imageSize, selColor );
913  p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
914  p.setPen( mPen );
915  p.translate( QPointF( center, center ) );
916  drawMarker( &p, context );
917  p.end();
918  }
919 
920  return true;
921 }
922 
923 void QgsSimpleMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, QgsSimpleMarkerSymbolLayerBase::Shape shape, const QPolygonF &polygon, const QPainterPath &path )
924 {
925  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
926  //of the rendered point!
927 
928  QPainter *p = context.renderContext().painter();
929  if ( !p )
930  {
931  return;
932  }
933 
934  bool ok = true;
936  {
939  if ( ok )
940  mBrush.setColor( c );
941  }
943  {
946  if ( ok )
947  {
948  mPen.setColor( c );
949  mSelPen.setColor( c );
950  }
951  }
953  {
956  if ( ok )
957  {
958  mPen.setWidthF( context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
960  }
961  }
963  {
966  if ( ok )
967  {
968  mPen.setStyle( QgsSymbolLayerUtils::decodePenStyle( strokeStyle ) );
969  mSelPen.setStyle( QgsSymbolLayerUtils::decodePenStyle( strokeStyle ) );
970  }
971  }
973  {
976  if ( ok )
977  {
978  mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
979  mSelPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
980  }
981  }
982 
983  if ( shapeIsFilled( shape ) )
984  {
985  p->setBrush( context.selected() ? mSelBrush : mBrush );
986  }
987  else
988  {
989  p->setBrush( Qt::NoBrush );
990  }
991  p->setPen( context.selected() ? mSelPen : mPen );
992 
993  if ( !polygon.isEmpty() )
994  p->drawPolygon( polygon );
995  else
996  p->drawPath( path );
997 }
998 
1000 {
1001  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1002  //of the rendered point!
1003 
1004  QPainter *p = context.renderContext().painter();
1005  if ( !p )
1006  {
1007  return;
1008  }
1009 
1010  if ( mUsingCache )
1011  {
1012  QImage &img = context.selected() ? mSelCache : mCache;
1013  double s = img.width();
1014 
1015  bool hasDataDefinedSize = false;
1016  double scaledSize = calculateSize( context, hasDataDefinedSize );
1017 
1018  bool hasDataDefinedRotation = false;
1019  QPointF offset;
1020  double angle = 0;
1021  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
1022 
1023  p->drawImage( QRectF( point.x() - s / 2.0 + offset.x(),
1024  point.y() - s / 2.0 + offset.y(),
1025  s, s ), img );
1026  }
1027  else
1028  {
1030  }
1031 }
1032 
1034 {
1035  QgsStringMap map;
1036  map[QStringLiteral( "name" )] = encodeShape( mShape );
1037  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
1038  map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
1039  map[QStringLiteral( "size" )] = QString::number( mSize );
1040  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1041  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1042  map[QStringLiteral( "angle" )] = QString::number( mAngle );
1043  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1044  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1045  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1046  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1047  map[QStringLiteral( "outline_style" )] = QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle );
1048  map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
1049  map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
1050  map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
1051  map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
1052  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1053  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1054  return map;
1055 }
1056 
1058 {
1060  m->setOffset( mOffset );
1061  m->setSizeUnit( mSizeUnit );
1063  m->setOffsetUnit( mOffsetUnit );
1072  copyPaintEffect( m );
1073  return m;
1074 }
1075 
1076 void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
1077 {
1078  // <Graphic>
1079  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
1080  element.appendChild( graphicElem );
1081 
1084  QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, encodeShape( mShape ), mColor, mStrokeColor, mStrokeStyle, strokeWidth, size );
1085 
1086  // <Rotation>
1087  QString angleFunc;
1088  bool ok;
1089  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
1090  if ( !ok )
1091  {
1092  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mAngle );
1093  }
1094  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
1095  {
1096  angleFunc = QString::number( angle + mAngle );
1097  }
1098  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
1099 
1100  // <Displacement>
1102  QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
1103 }
1104 
1105 QString QgsSimpleMarkerSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
1106 {
1107  Q_UNUSED( mmScaleFactor )
1108  Q_UNUSED( mapUnitScaleFactor )
1109 #if 0
1110  QString ogrType = "3"; //default is circle
1111  if ( mName == "square" )
1112  {
1113  ogrType = "5";
1114  }
1115  else if ( mName == "triangle" )
1116  {
1117  ogrType = "7";
1118  }
1119  else if ( mName == "star" )
1120  {
1121  ogrType = "9";
1122  }
1123  else if ( mName == "circle" )
1124  {
1125  ogrType = "3";
1126  }
1127  else if ( mName == "cross" )
1128  {
1129  ogrType = "0";
1130  }
1131  else if ( mName == "x" || mName == "cross2" )
1132  {
1133  ogrType = "1";
1134  }
1135  else if ( mName == "line" )
1136  {
1137  ogrType = "10";
1138  }
1139 
1140  QString ogrString;
1141  ogrString.append( "SYMBOL(" );
1142  ogrString.append( "id:" );
1143  ogrString.append( '\"' );
1144  ogrString.append( "ogr-sym-" );
1145  ogrString.append( ogrType );
1146  ogrString.append( '\"' );
1147  ogrString.append( ",c:" );
1148  ogrString.append( mColor.name() );
1149  ogrString.append( ",o:" );
1150  ogrString.append( mStrokeColor.name() );
1151  ogrString.append( QString( ",s:%1mm" ).arg( mSize ) );
1152  ogrString.append( ')' );
1153  return ogrString;
1154 #endif //0
1155 
1156  QString ogrString;
1157  ogrString.append( "PEN(" );
1158  ogrString.append( "c:" );
1159  ogrString.append( mColor.name() );
1160  ogrString.append( ",w:" );
1161  ogrString.append( QString::number( mSize ) );
1162  ogrString.append( "mm" );
1163  ogrString.append( ")" );
1164  return ogrString;
1165 }
1166 
1168 {
1169  QgsDebugMsg( QStringLiteral( "Entered." ) );
1170 
1171  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
1172  if ( graphicElem.isNull() )
1173  return nullptr;
1174 
1175  QString name = QStringLiteral( "square" );
1176  QColor color, strokeColor;
1177  double strokeWidth, size;
1178  Qt::PenStyle strokeStyle;
1179 
1180  if ( !QgsSymbolLayerUtils::wellKnownMarkerFromSld( graphicElem, name, color, strokeColor, strokeStyle, strokeWidth, size ) )
1181  return nullptr;
1182 
1183  double angle = 0.0;
1184  QString angleFunc;
1185  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
1186  {
1187  bool ok;
1188  double d = angleFunc.toDouble( &ok );
1189  if ( ok )
1190  angle = d;
1191  }
1192 
1193  QPointF offset;
1194  QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, offset );
1195 
1196  Shape shape = decodeShape( name );
1197 
1198  QString uom = element.attribute( QStringLiteral( "uom" ) );
1199  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
1200  offset.setX( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.x() ) );
1201  offset.setY( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.y() ) );
1202 
1204  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
1205  m->setColor( color );
1206  m->setStrokeColor( strokeColor );
1207  m->setAngle( angle );
1208  m->setOffset( offset );
1209  m->setStrokeStyle( strokeStyle );
1210  m->setStrokeWidth( strokeWidth );
1211  return m;
1212 }
1213 
1215 {
1216  Q_UNUSED( context )
1217 
1218  if ( mPolygon.count() != 0 )
1219  {
1220  p->drawPolygon( mPolygon );
1221  }
1222  else
1223  {
1224  p->drawPath( mPath );
1225  }
1226 }
1227 
1228 bool QgsSimpleMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
1229 {
1230  Q_UNUSED( mmMapUnitScaleFactor )
1231 
1232  //data defined size?
1233  double size = mSize;
1234 
1235  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
1236 
1237  //data defined size
1238  bool ok = true;
1239  if ( hasDataDefinedSize )
1240  {
1242 
1243  if ( ok )
1244  {
1245  switch ( mScaleMethod )
1246  {
1247  case QgsSymbol::ScaleArea:
1248  size = std::sqrt( size );
1249  break;
1251  break;
1252  }
1253  }
1254 
1256  }
1258  {
1260  }
1261  double halfSize = size / 2.0;
1262 
1263  //strokeWidth
1264  double strokeWidth = mStrokeWidth;
1265 
1267  {
1270  }
1273  {
1275  }
1276 
1277  //color
1278  QColor pc = mPen.color();
1279  QColor bc = mBrush.color();
1281  {
1284  }
1286  {
1289  }
1290 
1291  //offset
1292  double offsetX = 0;
1293  double offsetY = 0;
1294  markerOffset( context, offsetX, offsetY );
1295  offsetX *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1296  offsetY *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1297 
1298 
1299  QPointF off( offsetX, offsetY );
1300 
1301  //angle
1302  double angle = mAngle + mLineAngle;
1304  {
1305  context.setOriginalValueVariable( mAngle );
1307  }
1308 
1309  Shape shape = mShape;
1311  {
1312  context.setOriginalValueVariable( encodeShape( shape ) );
1313  QString shapeName = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyName, context.renderContext().expressionContext(), QString(), &ok );
1314  if ( ok )
1315  {
1316  shape = decodeShape( shapeName, &ok );
1317  if ( !ok )
1318  shape = mShape;
1319  }
1320  }
1321 
1322  if ( angle )
1323  off = _rotatedOffset( off, angle );
1324 
1326 
1327  QTransform t;
1328  t.translate( shift.x() + off.x(), shift.y() - off.y() );
1329 
1330  if ( !qgsDoubleNear( angle, 0.0 ) )
1331  t.rotate( angle );
1332 
1333  QPolygonF polygon;
1334  if ( shapeToPolygon( shape, polygon ) )
1335  {
1336  t.scale( halfSize, -halfSize );
1337 
1338  polygon = t.map( polygon );
1339 
1340  QgsPointSequence p;
1341  p.reserve( polygon.size() );
1342  for ( int i = 0; i < polygon.size(); i++ )
1343  {
1344  p << QgsPoint( polygon[i] );
1345  }
1346 
1347  if ( mBrush.style() != Qt::NoBrush )
1348  e.writePolygon( QgsRingSequence() << p, layerName, QStringLiteral( "SOLID" ), bc );
1349  if ( mPen.style() != Qt::NoPen )
1350  e.writePolyline( p, layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1351  }
1352  else if ( shape == Circle )
1353  {
1354  shift += QPointF( off.x(), -off.y() );
1355  if ( mBrush.style() != Qt::NoBrush )
1356  e.writeFilledCircle( layerName, bc, QgsPoint( shift ), halfSize );
1357  if ( mPen.style() != Qt::NoPen )
1358  e.writeCircle( layerName, pc, QgsPoint( shift ), halfSize, QStringLiteral( "CONTINUOUS" ), strokeWidth );
1359  }
1360  else if ( shape == Line )
1361  {
1362  QPointF pt1 = t.map( QPointF( 0, -halfSize ) );
1363  QPointF pt2 = t.map( QPointF( 0, halfSize ) );
1364 
1365  if ( mPen.style() != Qt::NoPen )
1366  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1367  }
1368  else if ( shape == Cross )
1369  {
1370  if ( mPen.style() != Qt::NoPen )
1371  {
1372  QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
1373  QPointF pt2 = t.map( QPointF( halfSize, 0 ) );
1374  QPointF pt3 = t.map( QPointF( 0, -halfSize ) );
1375  QPointF pt4 = t.map( QPointF( 0, halfSize ) );
1376 
1377  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1378  e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1379  }
1380  }
1381  else if ( shape == Cross2 )
1382  {
1383  if ( mPen.style() != Qt::NoPen )
1384  {
1385  QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
1386  QPointF pt2 = t.map( QPointF( halfSize, halfSize ) );
1387  QPointF pt3 = t.map( QPointF( halfSize, -halfSize ) );
1388  QPointF pt4 = t.map( QPointF( -halfSize, halfSize ) );
1389 
1390  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1391  e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1392  }
1393  }
1394  else if ( shape == ArrowHead )
1395  {
1396  if ( mPen.style() != Qt::NoPen )
1397  {
1398  QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
1399  QPointF pt2 = t.map( QPointF( 0, 0 ) );
1400  QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
1401 
1402  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1403  e.writeLine( QgsPoint( pt3 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1404  }
1405  }
1406  else
1407  {
1408  QgsDebugMsg( QStringLiteral( "Unsupported dxf marker name %1" ).arg( encodeShape( shape ) ) );
1409  return false;
1410  }
1411 
1412  return true;
1413 }
1414 
1415 
1417 {
1419  mStrokeWidthUnit = unit;
1420 }
1421 
1423 {
1425  {
1426  return mStrokeWidthUnit;
1427  }
1429 }
1430 
1432 {
1434  mStrokeWidthMapUnitScale = scale;
1435 }
1436 
1438 {
1440  {
1441  return mStrokeWidthMapUnitScale;
1442  }
1443  return QgsMapUnitScale();
1444 }
1445 
1447 {
1448  QRectF symbolBounds = QgsSimpleMarkerSymbolLayerBase::bounds( point, context );
1449 
1450  // need to account for stroke width
1451  double penWidth = 0.0;
1452  bool ok = true;
1454  {
1457  if ( ok )
1458  {
1459  penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
1460  }
1461  }
1463  {
1466  if ( ok && strokeStyle == QLatin1String( "no" ) )
1467  {
1468  penWidth = 0.0;
1469  }
1470  }
1471  //antialiasing, add 1 pixel
1472  penWidth += 1;
1473 
1474  //extend bounds by pen width / 2.0
1475  symbolBounds.adjust( -penWidth / 2.0, -penWidth / 2.0,
1476  penWidth / 2.0, penWidth / 2.0 );
1477 
1478  return symbolBounds;
1479 }
1480 
1482 {
1483  if ( shapeIsFilled( mShape ) )
1484  {
1485  setFillColor( color );
1486  }
1487  else
1488  {
1489  setStrokeColor( color );
1490  }
1491 }
1492 
1494 {
1495  if ( shapeIsFilled( mShape ) )
1496  {
1497  return fillColor();
1498  }
1499  else
1500  {
1501  return strokeColor();
1502  }
1503 }
1504 
1505 
1506 
1507 
1508 //
1509 // QgsFilledMarkerSymbolLayer
1510 //
1511 
1513  : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
1514 {
1515  mFill.reset( static_cast<QgsFillSymbol *>( QgsFillSymbol::createSimple( QgsStringMap() ) ) );
1516 }
1517 
1519 {
1520  QString name = DEFAULT_SIMPLEMARKER_NAME;
1524 
1525  if ( props.contains( QStringLiteral( "name" ) ) )
1526  name = props[QStringLiteral( "name" )];
1527  if ( props.contains( QStringLiteral( "size" ) ) )
1528  size = props[QStringLiteral( "size" )].toDouble();
1529  if ( props.contains( QStringLiteral( "angle" ) ) )
1530  angle = props[QStringLiteral( "angle" )].toDouble();
1531  if ( props.contains( QStringLiteral( "scale_method" ) ) )
1532  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )] );
1533 
1534  QgsFilledMarkerSymbolLayer *m = new QgsFilledMarkerSymbolLayer( decodeShape( name ), size, angle, scaleMethod );
1535  if ( props.contains( QStringLiteral( "offset" ) ) )
1536  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
1537  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1538  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
1539  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1540  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
1541  if ( props.contains( QStringLiteral( "size_unit" ) ) )
1542  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
1543  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1544  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
1545  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1546  {
1547  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1548  }
1549  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1550  {
1551  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1552  }
1553 
1555 
1556  m->restoreOldDataDefinedProperties( props );
1557 
1558  return m;
1559 }
1560 
1562 {
1563  return QStringLiteral( "FilledMarker" );
1564 }
1565 
1567 {
1568  if ( mFill )
1569  {
1570  mFill->startRender( context.renderContext(), context.fields() );
1571  }
1572 
1574 }
1575 
1577 {
1578  if ( mFill )
1579  {
1580  mFill->stopRender( context.renderContext() );
1581  }
1582 }
1583 
1585 {
1586  QgsStringMap map;
1587  map[QStringLiteral( "name" )] = encodeShape( mShape );
1588  map[QStringLiteral( "size" )] = QString::number( mSize );
1589  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1590  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1591  map[QStringLiteral( "angle" )] = QString::number( mAngle );
1592  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1593  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1594  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1595  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1596  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1597  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1598 
1599  if ( mFill )
1600  {
1601  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mFill->color() );
1602  }
1603  return map;
1604 }
1605 
1607 {
1609  copyPaintEffect( m );
1611  m->setSubSymbol( mFill->clone() );
1612  return m;
1613 }
1614 
1616 {
1617  return mFill.get();
1618 }
1619 
1621 {
1622  if ( symbol && symbol->type() == QgsSymbol::Fill )
1623  {
1624  mFill.reset( static_cast<QgsFillSymbol *>( symbol ) );
1625  return true;
1626  }
1627  else
1628  {
1629  delete symbol;
1630  return false;
1631  }
1632 }
1633 
1635 {
1636  if ( mFill )
1637  {
1638  return QgsSymbolLayerUtils::estimateMaxSymbolBleed( mFill.get(), context );
1639  }
1640  return 0;
1641 }
1642 
1644 {
1645  QSet<QString> attr = QgsSimpleMarkerSymbolLayerBase::usedAttributes( context );
1646  if ( mFill )
1647  attr.unite( mFill->usedAttributes( context ) );
1648  return attr;
1649 }
1650 
1652 {
1654  return true;
1655  if ( mFill && mFill->hasDataDefinedProperties() )
1656  return true;
1657  return false;
1658 }
1659 
1661 {
1662  mColor = c;
1663  if ( mFill )
1664  mFill->setColor( c );
1665 }
1666 
1668 {
1669  return mFill ? mFill->color() : mColor;
1670 }
1671 
1672 void QgsFilledMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, QgsSimpleMarkerSymbolLayerBase::Shape shape, const QPolygonF &polygon, const QPainterPath &path )
1673 {
1674  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1675  //of the rendered point!
1676 
1677  QPainter *p = context.renderContext().painter();
1678  if ( !p )
1679  {
1680  return;
1681  }
1682 
1683  if ( shapeIsFilled( shape ) )
1684  {
1685  p->setBrush( Qt::red );
1686  }
1687  else
1688  {
1689  p->setBrush( Qt::NoBrush );
1690  }
1691  p->setPen( Qt::black );
1692 
1693  if ( !polygon.isEmpty() )
1694  {
1695  mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
1696  }
1697  else
1698  {
1699  QPolygonF poly = path.toFillPolygon();
1700  mFill->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
1701  }
1702 
1703 
1704 }
1705 
1706 
1708 
1709 
1711 {
1712  mPath = path;
1713  mSize = size;
1714  mAngle = angle;
1715  mOffset = QPointF( 0, 0 );
1717  mStrokeWidth = 0.2;
1718  mStrokeWidthUnit = QgsUnitTypes::RenderMillimeters;
1719  mColor = QColor( 35, 35, 35 );
1720  mStrokeColor = QColor( 35, 35, 35 );
1721  updateDefaultAspectRatio();
1722 }
1723 
1724 
1726 {
1727  QString name;
1728  double size = DEFAULT_SVGMARKER_SIZE;
1729  double angle = DEFAULT_SVGMARKER_ANGLE;
1731 
1732  if ( props.contains( QStringLiteral( "name" ) ) )
1733  name = props[QStringLiteral( "name" )];
1734  if ( props.contains( QStringLiteral( "size" ) ) )
1735  size = props[QStringLiteral( "size" )].toDouble();
1736  if ( props.contains( QStringLiteral( "angle" ) ) )
1737  angle = props[QStringLiteral( "angle" )].toDouble();
1738  if ( props.contains( QStringLiteral( "scale_method" ) ) )
1739  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )] );
1740 
1741  QgsSvgMarkerSymbolLayer *m = new QgsSvgMarkerSymbolLayer( name, size, angle, scaleMethod );
1742 
1743  //we only check the svg default parameters if necessary, since it could be expensive
1744  if ( !props.contains( QStringLiteral( "fill" ) ) && !props.contains( QStringLiteral( "color" ) ) && !props.contains( QStringLiteral( "outline" ) ) &&
1745  !props.contains( QStringLiteral( "outline_color" ) ) && !props.contains( QStringLiteral( "outline-width" ) ) && !props.contains( QStringLiteral( "outline_width" ) ) )
1746  {
1747  QColor fillColor, strokeColor;
1748  double fillOpacity = 1.0;
1749  double strokeOpacity = 1.0;
1750  double strokeWidth;
1751  bool hasFillParam = false, hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
1752  bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
1753  QgsApplication::svgCache()->containsParams( name, hasFillParam, hasDefaultFillColor, fillColor,
1754  hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
1755  hasStrokeParam, hasDefaultStrokeColor, strokeColor,
1756  hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
1757  hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
1758  if ( hasDefaultFillColor )
1759  {
1760  m->setFillColor( fillColor );
1761  }
1762  if ( hasDefaultFillOpacity )
1763  {
1764  QColor c = m->fillColor();
1765  c.setAlphaF( fillOpacity );
1766  m->setFillColor( c );
1767  }
1768  if ( hasDefaultStrokeColor )
1769  {
1770  m->setStrokeColor( strokeColor );
1771  }
1772  if ( hasDefaultStrokeWidth )
1773  {
1774  m->setStrokeWidth( strokeWidth );
1775  }
1776  if ( hasDefaultStrokeOpacity )
1777  {
1778  QColor c = m->strokeColor();
1779  c.setAlphaF( strokeOpacity );
1780  m->setStrokeColor( c );
1781  }
1782  }
1783 
1784  if ( props.contains( QStringLiteral( "size_unit" ) ) )
1785  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
1786  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1787  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
1788  if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
1789  m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
1790  if ( props.contains( QStringLiteral( "offset" ) ) )
1791  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
1792  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1793  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
1794  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1795  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
1796  if ( props.contains( QStringLiteral( "fill" ) ) )
1797  {
1798  //pre 2.5 projects used "fill"
1799  m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "fill" )] ) );
1800  }
1801  else if ( props.contains( QStringLiteral( "color" ) ) )
1802  {
1803  m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] ) );
1804  }
1805  if ( props.contains( QStringLiteral( "outline" ) ) )
1806  {
1807  //pre 2.5 projects used "outline"
1808  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline" )] ) );
1809  }
1810  else if ( props.contains( QStringLiteral( "outline_color" ) ) )
1811  {
1812  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )] ) );
1813  }
1814  else if ( props.contains( QStringLiteral( "line_color" ) ) )
1815  {
1816  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )] ) );
1817  }
1818 
1819  if ( props.contains( QStringLiteral( "outline-width" ) ) )
1820  {
1821  //pre 2.5 projects used "outline-width"
1822  m->setStrokeWidth( props[QStringLiteral( "outline-width" )].toDouble() );
1823  }
1824  else if ( props.contains( QStringLiteral( "outline_width" ) ) )
1825  {
1826  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
1827  }
1828  else if ( props.contains( QStringLiteral( "line_width" ) ) )
1829  {
1830  m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
1831  }
1832 
1833  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
1834  {
1835  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )] ) );
1836  }
1837  else if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
1838  {
1839  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )] ) );
1840  }
1841  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
1842  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )] ) );
1843 
1844  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1845  {
1846  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1847  }
1848  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1849  {
1850  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1851  }
1852 
1853  m->restoreOldDataDefinedProperties( props );
1854 
1856 
1857  return m;
1858 }
1859 
1861 {
1862  QgsStringMap::iterator it = properties.find( QStringLiteral( "name" ) );
1863  if ( it != properties.end() )
1864  {
1865  if ( saving )
1866  it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value(), pathResolver );
1867  else
1868  it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value(), pathResolver );
1869  }
1870 }
1871 
1872 void QgsSvgMarkerSymbolLayer::setPath( const QString &path )
1873 {
1874  mPath = path;
1875  QColor defaultFillColor, defaultStrokeColor;
1876  double strokeWidth, fillOpacity, strokeOpacity;
1877  bool hasFillParam = false, hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
1878  bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
1879  QgsApplication::svgCache()->containsParams( path, hasFillParam, hasDefaultFillColor, defaultFillColor,
1880  hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
1881  hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
1882  hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
1883  hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
1884 
1885  double newFillOpacity = hasFillOpacityParam ? fillColor().alphaF() : 1.0;
1886  double newStrokeOpacity = hasStrokeOpacityParam ? strokeColor().alphaF() : 1.0;
1887 
1888  if ( hasDefaultFillColor )
1889  {
1890  defaultFillColor.setAlphaF( newFillOpacity );
1891  setFillColor( defaultFillColor );
1892  }
1893  if ( hasDefaultFillOpacity )
1894  {
1895  QColor c = fillColor();
1896  c.setAlphaF( fillOpacity );
1897  setFillColor( c );
1898  }
1899  if ( hasDefaultStrokeColor )
1900  {
1901  defaultStrokeColor.setAlphaF( newStrokeOpacity );
1902  setStrokeColor( defaultStrokeColor );
1903  }
1904  if ( hasDefaultStrokeWidth )
1905  {
1906  setStrokeWidth( strokeWidth );
1907  }
1908  if ( hasDefaultStrokeOpacity )
1909  {
1910  QColor c = strokeColor();
1911  c.setAlphaF( strokeOpacity );
1912  setStrokeColor( c );
1913  }
1914 
1915  updateDefaultAspectRatio();
1916 }
1917 
1919 {
1920  if ( mDefaultAspectRatio == 0.0 )
1921  {
1922  //size
1923  double size = mSize;
1924  //assume 88 dpi as standard value
1925  double widthScaleFactor = 3.465;
1926  QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( mPath, size, mColor, mStrokeColor, mStrokeWidth, widthScaleFactor );
1927  // set default aspect ratio
1928  mDefaultAspectRatio = svgViewbox.isValid() ? svgViewbox.height() / svgViewbox.width() : 0.0;
1929  }
1930  return mDefaultAspectRatio;
1931 }
1932 
1934 {
1935  bool aPreservedAspectRatio = preservedAspectRatio();
1936  if ( aPreservedAspectRatio && !par )
1937  {
1938  mFixedAspectRatio = mDefaultAspectRatio;
1939  }
1940  else if ( !aPreservedAspectRatio && par )
1941  {
1942  mFixedAspectRatio = 0.0;
1943  }
1944  return preservedAspectRatio();
1945 }
1946 
1947 
1949 {
1950  return QStringLiteral( "SvgMarker" );
1951 }
1952 
1954 {
1955  QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
1956  Q_UNUSED( context )
1957 }
1958 
1960 {
1961  Q_UNUSED( context )
1962 }
1963 
1965 {
1966  QPainter *p = context.renderContext().painter();
1967  if ( !p )
1968  return;
1969 
1970  bool hasDataDefinedSize = false;
1971  double scaledSize = calculateSize( context, hasDataDefinedSize );
1972  double size = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
1973 
1974  //don't render symbols with size below one or above 10,000 pixels
1975  if ( static_cast< int >( size ) < 1 || 10000.0 < size )
1976  {
1977  return;
1978  }
1979 
1980  p->save();
1981 
1982  bool hasDataDefinedAspectRatio = false;
1983  double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
1984 
1985  QPointF outputOffset;
1986  double angle = 0.0;
1987  calculateOffsetAndRotation( context, scaledSize, outputOffset, angle );
1988 
1989  p->translate( point + outputOffset );
1990 
1991  bool rotated = !qgsDoubleNear( angle, 0 );
1992  if ( rotated )
1993  p->rotate( angle );
1994 
1995  QString path = mPath;
1997  {
1998  context.setOriginalValueVariable( mPath );
2000  context.renderContext().pathResolver() );
2001  }
2002 
2003  double strokeWidth = mStrokeWidth;
2005  {
2006  context.setOriginalValueVariable( mStrokeWidth );
2008  }
2009  strokeWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
2010 
2011  QColor fillColor = mColor;
2013  {
2016  }
2017 
2018  QColor strokeColor = mStrokeColor;
2020  {
2023  }
2024 
2025  bool fitsInCache = true;
2026  bool usePict = true;
2027  double hwRatio = 1.0;
2028  if ( !context.renderContext().forceVectorOutput() && !rotated )
2029  {
2030  QImage img = QgsApplication::svgCache()->svgAsImage( path, size, fillColor, strokeColor, strokeWidth,
2031  context.renderContext().scaleFactor(), fitsInCache, aspectRatio,
2033  if ( fitsInCache && img.width() > 1 )
2034  {
2035  usePict = false;
2036  //consider transparency
2037  if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2038  {
2039  QImage transparentImage = img.copy();
2040  QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2041  p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
2042  hwRatio = static_cast< double >( transparentImage.height() ) / static_cast< double >( transparentImage.width() );
2043  }
2044  else
2045  {
2046  p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2047  hwRatio = static_cast< double >( img.height() ) / static_cast< double >( img.width() );
2048  }
2049  }
2050  }
2051 
2052  if ( usePict || !fitsInCache )
2053  {
2054  p->setOpacity( context.opacity() );
2055  QPicture pct = QgsApplication::svgCache()->svgAsPicture( path, size, fillColor, strokeColor, strokeWidth,
2056  context.renderContext().scaleFactor(), context.renderContext().forceVectorOutput(), aspectRatio,
2058  if ( pct.width() > 1 )
2059  {
2060  p->save();
2061  _fixQPictureDPI( p );
2062  p->drawPicture( 0, 0, pct );
2063  p->restore();
2064  hwRatio = static_cast< double >( pct.height() ) / static_cast< double >( pct.width() );
2065  }
2066  }
2067 
2068  if ( context.selected() )
2069  {
2070  QPen pen( context.renderContext().selectionColor() );
2071  double penWidth = context.renderContext().convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters );
2072  if ( penWidth > size / 20 )
2073  {
2074  // keep the pen width from covering symbol
2075  penWidth = size / 20;
2076  }
2077  double penOffset = penWidth / 2;
2078  pen.setWidth( penWidth );
2079  p->setPen( pen );
2080  p->setBrush( Qt::NoBrush );
2081  double wSize = size + penOffset;
2082  double hSize = size * hwRatio + penOffset;
2083  p->drawRect( QRectF( -wSize / 2.0, -hSize / 2.0, wSize, hSize ) );
2084  }
2085 
2086  p->restore();
2087 
2089  {
2090  // workaround issue with nested QPictures forgetting antialiasing flag - see https://github.com/qgis/QGIS/issues/22909
2091  p->setRenderHint( QPainter::Antialiasing );
2092  }
2093 
2094 }
2095 
2096 double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2097 {
2098  double scaledSize = mSize;
2100 
2101  bool ok = true;
2102  if ( hasDataDefinedSize )
2103  {
2104  context.setOriginalValueVariable( mSize );
2106  }
2107  else
2108  {
2110  if ( hasDataDefinedSize )
2111  {
2112  context.setOriginalValueVariable( mSize );
2114  }
2115  }
2116 
2117  if ( hasDataDefinedSize && ok )
2118  {
2119  switch ( mScaleMethod )
2120  {
2121  case QgsSymbol::ScaleArea:
2122  scaledSize = std::sqrt( scaledSize );
2123  break;
2125  break;
2126  }
2127  }
2128 
2129  return scaledSize;
2130 }
2131 
2132 double QgsSvgMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2133 {
2135  if ( !hasDataDefinedAspectRatio )
2136  return mFixedAspectRatio;
2137 
2138  if ( !mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyHeight ) && mFixedAspectRatio <= 0.0 )
2139  return 0.0;
2140 
2141  double scaledAspectRatio = mDefaultAspectRatio;
2142  if ( mFixedAspectRatio > 0.0 )
2143  scaledAspectRatio = mFixedAspectRatio;
2144 
2145  double defaultHeight = mSize * scaledAspectRatio;
2146  scaledAspectRatio = defaultHeight / scaledSize;
2147 
2148  bool ok = true;
2149  double scaledHeight = scaledSize * scaledAspectRatio;
2151  {
2152  context.setOriginalValueVariable( defaultHeight );
2153  scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyHeight, context.renderContext().expressionContext(), defaultHeight, &ok );
2154  }
2155 
2156  if ( hasDataDefinedAspectRatio && ok )
2157  {
2158  switch ( mScaleMethod )
2159  {
2160  case QgsSymbol::ScaleArea:
2161  scaledHeight = sqrt( scaledHeight );
2162  break;
2164  break;
2165  }
2166  }
2167 
2168  scaledAspectRatio = scaledHeight / scaledSize;
2169 
2170  return scaledAspectRatio;
2171 }
2172 
2173 void QgsSvgMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledSize, QPointF &offset, double &angle ) const
2174 {
2175  //offset
2176  double offsetX = 0;
2177  double offsetY = 0;
2178  markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
2179  offset = QPointF( offsetX, offsetY );
2180 
2181  angle = mAngle + mLineAngle;
2183  {
2184  context.setOriginalValueVariable( mAngle );
2186  }
2187 
2189  if ( hasDataDefinedRotation )
2190  {
2191  // For non-point markers, "dataDefinedRotation" means following the
2192  // shape (shape-data defined). For them, "field-data defined" does
2193  // not work at all. TODO: if "field-data defined" ever gets implemented
2194  // we'll need a way to distinguish here between the two, possibly
2195  // using another flag in renderHints()
2196  const QgsFeature *f = context.feature();
2197  if ( f )
2198  {
2199  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
2200  {
2201  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2202  angle += m2p.mapRotation();
2203  }
2204  }
2205  }
2206 
2207  if ( angle )
2208  offset = _rotatedOffset( offset, angle );
2209 }
2210 
2211 
2213 {
2214  QgsStringMap map;
2215  map[QStringLiteral( "name" )] = mPath;
2216  map[QStringLiteral( "size" )] = QString::number( mSize );
2217  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2218  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2219  map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2220  map[QStringLiteral( "angle" )] = QString::number( mAngle );
2221  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2222  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2223  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2224  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2225  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
2226  map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
2227  map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
2228  map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2229  map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2230  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
2231  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
2232  return map;
2233 }
2234 
2236 {
2238  m->setFixedAspectRatio( mFixedAspectRatio );
2239  m->setColor( mColor );
2240  m->setStrokeColor( mStrokeColor );
2241  m->setStrokeWidth( mStrokeWidth );
2242  m->setStrokeWidthUnit( mStrokeWidthUnit );
2243  m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
2244  m->setOffset( mOffset );
2245  m->setOffsetUnit( mOffsetUnit );
2247  m->setSizeUnit( mSizeUnit );
2252  copyPaintEffect( m );
2253  return m;
2254 }
2255 
2257 {
2259  mStrokeWidthUnit = unit;
2260 }
2261 
2263 {
2265  if ( unit != mStrokeWidthUnit )
2266  {
2268  }
2269  return unit;
2270 }
2271 
2273 {
2275  mStrokeWidthMapUnitScale = scale;
2276 }
2277 
2279 {
2280  if ( QgsMarkerSymbolLayer::mapUnitScale() == mStrokeWidthMapUnitScale )
2281  {
2282  return mStrokeWidthMapUnitScale;
2283  }
2284  return QgsMapUnitScale();
2285 }
2286 
2287 void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
2288 {
2289  // <Graphic>
2290  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2291  element.appendChild( graphicElem );
2292 
2293  // encode a parametric SVG reference
2295  double strokeWidth = QgsSymbolLayerUtils::rescaleUom( mStrokeWidth, mStrokeWidthUnit, props );
2296  QgsSymbolLayerUtils::parametricSvgToSld( doc, graphicElem, mPath, mColor, size, mStrokeColor, strokeWidth );
2297 
2298  // <Rotation>
2299  QString angleFunc;
2300  bool ok;
2301  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2302  if ( !ok )
2303  {
2304  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mAngle );
2305  }
2306  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2307  {
2308  angleFunc = QString::number( angle + mAngle );
2309  }
2310 
2311  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2312 
2313  // <Displacement>
2314  QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2315  QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
2316 }
2317 
2319 {
2320  QgsDebugMsg( QStringLiteral( "Entered." ) );
2321 
2322  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
2323  if ( graphicElem.isNull() )
2324  return nullptr;
2325 
2326  QString path, mimeType;
2327  QColor fillColor;
2328  double size;
2329 
2330  if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
2331  return nullptr;
2332 
2333  QString uom = element.attribute( QStringLiteral( "uom" ) );
2334  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
2335 
2336  if ( mimeType != QLatin1String( "image/svg+xml" ) )
2337  return nullptr;
2338 
2339  double angle = 0.0;
2340  QString angleFunc;
2341  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2342  {
2343  bool ok;
2344  double d = angleFunc.toDouble( &ok );
2345  if ( ok )
2346  angle = d;
2347  }
2348 
2349  QPointF offset;
2350  QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, offset );
2351 
2352  QgsSvgMarkerSymbolLayer *m = new QgsSvgMarkerSymbolLayer( path, size );
2353  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
2354  m->setFillColor( fillColor );
2355  //m->setStrokeColor( strokeColor );
2356  //m->setStrokeWidth( strokeWidth );
2357  m->setAngle( angle );
2358  m->setOffset( offset );
2359  return m;
2360 }
2361 
2362 bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
2363 {
2364  //size
2365  double size = mSize;
2366 
2367  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
2368 
2369  bool ok = true;
2370  if ( hasDataDefinedSize )
2371  {
2372  context.setOriginalValueVariable( mSize );
2374  }
2375 
2376  if ( hasDataDefinedSize && ok )
2377  {
2378  switch ( mScaleMethod )
2379  {
2380  case QgsSymbol::ScaleArea:
2381  size = std::sqrt( size );
2382  break;
2384  break;
2385  }
2386  }
2387 
2389  {
2390  size *= mmMapUnitScaleFactor;
2391  }
2392 
2393  //offset, angle
2394  QPointF offset = mOffset;
2395 
2397  {
2399  const QVariant val = mDataDefinedProperties.value( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext(), QString() );
2400  const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
2401  if ( ok )
2402  offset = res;
2403  }
2404  double offsetX = offset.x();
2405  double offsetY = offset.y();
2406 
2407  QPointF outputOffset( offsetX, offsetY );
2408 
2409  double angle = mAngle + mLineAngle;
2411  {
2412  context.setOriginalValueVariable( mAngle );
2414  }
2415 
2416  if ( angle )
2417  outputOffset = _rotatedOffset( outputOffset, angle );
2418 
2419  outputOffset *= e.mapUnitScaleFactor( e.symbologyScale(), mOffsetUnit, e.mapUnits(), context.renderContext().mapToPixel().mapUnitsPerPixel() );
2420 
2421  QString path = mPath;
2423  {
2424  context.setOriginalValueVariable( mPath );
2426  context.renderContext().pathResolver() );
2427  }
2428 
2429  double strokeWidth = mStrokeWidth;
2431  {
2432  context.setOriginalValueVariable( mStrokeWidth );
2434  }
2435  strokeWidth *= e.mapUnitScaleFactor( e.symbologyScale(), mStrokeWidthUnit, e.mapUnits(), context.renderContext().mapToPixel().mapUnitsPerPixel() );
2436 
2437  QColor fillColor = mColor;
2439  {
2442  }
2443 
2444  QColor strokeColor = mStrokeColor;
2446  {
2449  }
2450 
2451  const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, size, fillColor, strokeColor, strokeWidth,
2452  context.renderContext().scaleFactor(), mFixedAspectRatio,
2454 
2455  QSvgRenderer r( svgContent );
2456  if ( !r.isValid() )
2457  return false;
2458 
2459  QgsDxfPaintDevice pd( &e );
2460  pd.setDrawingSize( QSizeF( r.defaultSize() ) );
2461 
2462  QSizeF outSize( r.defaultSize() );
2463  outSize.scale( size, size, Qt::KeepAspectRatio );
2464 
2465  QPainter p;
2466  p.begin( &pd );
2467  if ( !qgsDoubleNear( angle, 0.0 ) )
2468  {
2469  p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
2470  p.rotate( angle );
2471  p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
2472  }
2473  pd.setShift( shift + QPointF( outputOffset.x(), -outputOffset.y() ) );
2474  pd.setOutputSize( QRectF( -outSize.width() / 2.0, -outSize.height() / 2.0, outSize.width(), outSize.height() ) );
2475  pd.setLayer( layerName );
2476  r.render( &p );
2477  p.end();
2478  return true;
2479 }
2480 
2482 {
2483  bool hasDataDefinedSize = false;
2484  double scaledSize = calculateSize( context, hasDataDefinedSize );
2485  scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
2486 
2487  //don't render symbols with size below one or above 10,000 pixels
2488  if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
2489  {
2490  return QRectF();
2491  }
2492 
2493  QPointF outputOffset;
2494  double angle = 0.0;
2495  calculateOffsetAndRotation( context, scaledSize, outputOffset, angle );
2496 
2497  QString path = mPath;
2499  {
2500  context.setOriginalValueVariable( mPath );
2502  context.renderContext().pathResolver() );
2503  }
2504 
2505  double strokeWidth = mStrokeWidth;
2507  {
2508  context.setOriginalValueVariable( mStrokeWidth );
2510  }
2511  strokeWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
2512 
2513  //need to get colors to take advantage of cached SVGs
2514  QColor fillColor = mColor;
2516  {
2519  }
2520 
2521  QColor strokeColor = mStrokeColor;
2523  {
2526  }
2527 
2528  QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledSize, fillColor, strokeColor, strokeWidth,
2529  context.renderContext().scaleFactor(), mFixedAspectRatio,
2531 
2532  double scaledHeight = svgViewbox.isValid() ? scaledSize * svgViewbox.height() / svgViewbox.width() : scaledSize;
2533 
2534  QMatrix transform;
2535 
2536  // move to the desired position
2537  transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
2538 
2539  if ( !qgsDoubleNear( angle, 0.0 ) )
2540  transform.rotate( angle );
2541 
2542  //antialiasing
2543  strokeWidth += 1.0 / 2.0;
2544 
2545  QRectF symbolBounds = transform.mapRect( QRectF( -scaledSize / 2.0,
2546  -scaledHeight / 2.0,
2547  scaledSize,
2548  scaledHeight ) );
2549 
2550  //extend bounds by pen width / 2.0
2551  symbolBounds.adjust( -strokeWidth / 2.0, -strokeWidth / 2.0,
2552  strokeWidth / 2.0, strokeWidth / 2.0 );
2553 
2554  return symbolBounds;
2555 
2556 }
2557 
2559 
2561  : mPath( path )
2562 {
2563  mSize = size;
2564  mAngle = angle;
2565  mOffset = QPointF( 0, 0 );
2568 }
2569 
2570 
2572 {
2573  QString path;
2575  double angle = DEFAULT_RASTERMARKER_ANGLE;
2577 
2578  if ( props.contains( QStringLiteral( "imageFile" ) ) )
2579  path = props[QStringLiteral( "imageFile" )];
2580  if ( props.contains( QStringLiteral( "size" ) ) )
2581  size = props[QStringLiteral( "size" )].toDouble();
2582  if ( props.contains( QStringLiteral( "angle" ) ) )
2583  angle = props[QStringLiteral( "angle" )].toDouble();
2584  if ( props.contains( QStringLiteral( "scale_method" ) ) )
2585  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )] );
2586 
2587  QgsRasterMarkerSymbolLayer *m = new QgsRasterMarkerSymbolLayer( path, size, angle, scaleMethod );
2588 
2589  if ( props.contains( QStringLiteral( "alpha" ) ) )
2590  {
2591  m->setOpacity( props[QStringLiteral( "alpha" )].toDouble() );
2592  }
2593 
2594  if ( props.contains( QStringLiteral( "size_unit" ) ) )
2595  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
2596  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
2597  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
2598  if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
2599  m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
2600 
2601  if ( props.contains( QStringLiteral( "offset" ) ) )
2602  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
2603  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
2604  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
2605  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2606  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
2607 
2608  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2609  {
2610  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2611  }
2612  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2613  {
2614  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2615  }
2616 
2617  m->restoreOldDataDefinedProperties( props );
2619 
2620  return m;
2621 }
2622 
2624 {
2625  QgsStringMap::iterator it = properties.find( QStringLiteral( "name" ) );
2626  if ( it != properties.end() )
2627  {
2628  if ( saving )
2629  it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value(), pathResolver );
2630  else
2631  it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value(), pathResolver );
2632  }
2633 }
2634 
2635 void QgsRasterMarkerSymbolLayer::setPath( const QString &path )
2636 {
2637  mPath = path;
2639 }
2640 
2642 {
2643  bool aPreservedAspectRatio = preservedAspectRatio();
2644  if ( aPreservedAspectRatio && !par )
2645  {
2647  }
2648  else if ( !aPreservedAspectRatio && par )
2649  {
2650  mFixedAspectRatio = 0.0;
2651  }
2652  return preservedAspectRatio();
2653 }
2654 
2656 {
2657  if ( mDefaultAspectRatio == 0.0 )
2658  {
2660  mDefaultAspectRatio = ( !size.isNull() && size.isValid() && size.width() > 0 ) ? static_cast< double >( size.height() ) / static_cast< double >( size.width() ) : 0.0;
2661  }
2662  return mDefaultAspectRatio;
2663 }
2664 
2666 {
2667  return QStringLiteral( "RasterMarker" );
2668 }
2669 
2671 {
2672  QPainter *p = context.renderContext().painter();
2673  if ( !p )
2674  return;
2675 
2676  bool hasDataDefinedSize = false;
2677  double scaledSize = calculateSize( context, hasDataDefinedSize );
2678  double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
2679  bool hasDataDefinedAspectRatio = false;
2680  double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
2681  double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
2682 
2683  //don't render symbols with size below one or above 10,000 pixels
2684  if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2685  {
2686  return;
2687  }
2688 
2689  QString path = mPath;
2691  {
2692  context.setOriginalValueVariable( mPath );
2694  if ( preservedAspectRatio() && path != mPath )
2695  {
2696  QSize size = QgsApplication::imageCache()->originalSize( path );
2697  if ( !size.isNull() && size.isValid() && size.width() > 0 )
2698  {
2699  height = width * ( static_cast< double >( size.height() ) / static_cast< double >( size.width() ) );
2700  }
2701  }
2702  }
2703 
2704  if ( path.isEmpty() )
2705  return;
2706 
2707  p->save();
2708 
2709  QPointF outputOffset;
2710  double angle = 0.0;
2711  calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
2712 
2713  p->translate( point + outputOffset );
2714 
2715  bool rotated = !qgsDoubleNear( angle, 0 );
2716  if ( rotated )
2717  p->rotate( angle );
2718 
2719  double opacity = mOpacity;
2721  {
2724  }
2725  opacity *= context.opacity();
2726 
2727  bool cached;
2728  QImage img = QgsApplication::imageCache()->pathAsImage( path, QSize( width, preservedAspectRatio() ? 0 : width * aspectRatio ), preservedAspectRatio(), opacity, cached, ( context.renderContext().flags() & QgsRenderContext::RenderBlocking ) );
2729  if ( !img.isNull() )
2730  {
2731  if ( context.selected() )
2733 
2734  p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2735  }
2736 
2737  p->restore();
2738 }
2739 
2740 double QgsRasterMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2741 {
2742  double scaledSize = mSize;
2744 
2745  bool ok = true;
2746  if ( hasDataDefinedSize )
2747  {
2748  context.setOriginalValueVariable( mSize );
2750  }
2751  else
2752  {
2754  if ( hasDataDefinedSize )
2755  {
2756  context.setOriginalValueVariable( mSize );
2758  }
2759  }
2760 
2761  if ( hasDataDefinedSize && ok )
2762  {
2763  switch ( mScaleMethod )
2764  {
2765  case QgsSymbol::ScaleArea:
2766  scaledSize = std::sqrt( scaledSize );
2767  break;
2769  break;
2770  }
2771  }
2772 
2773  return scaledSize;
2774 }
2775 
2776 double QgsRasterMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2777 {
2779  if ( !hasDataDefinedAspectRatio )
2780  return mFixedAspectRatio;
2781 
2783  return 0.0;
2784 
2785  double scaledAspectRatio = mDefaultAspectRatio;
2786  if ( mFixedAspectRatio > 0.0 )
2787  scaledAspectRatio = mFixedAspectRatio;
2788 
2789  double defaultHeight = mSize * scaledAspectRatio;
2790  scaledAspectRatio = defaultHeight / scaledSize;
2791 
2792  bool ok = true;
2793  double scaledHeight = scaledSize * scaledAspectRatio;
2795  {
2796  context.setOriginalValueVariable( defaultHeight );
2797  scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyHeight, context.renderContext().expressionContext(), defaultHeight, &ok );
2798  }
2799 
2800  if ( hasDataDefinedAspectRatio && ok )
2801  {
2802  switch ( mScaleMethod )
2803  {
2804  case QgsSymbol::ScaleArea:
2805  scaledHeight = sqrt( scaledHeight );
2806  break;
2808  break;
2809  }
2810  }
2811 
2812  scaledAspectRatio = scaledHeight / scaledSize;
2813 
2814  return scaledAspectRatio;
2815 }
2816 
2817 void QgsRasterMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
2818 {
2819  //offset
2820  double offsetX = 0;
2821  double offsetY = 0;
2822  markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
2823  offset = QPointF( offsetX, offsetY );
2824 
2825  angle = mAngle + mLineAngle;
2827  {
2828  context.setOriginalValueVariable( mAngle );
2830  }
2831 
2833  if ( hasDataDefinedRotation )
2834  {
2835  const QgsFeature *f = context.feature();
2836  if ( f )
2837  {
2838  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
2839  {
2840  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2841  angle += m2p.mapRotation();
2842  }
2843  }
2844  }
2845 
2846  if ( angle )
2847  offset = _rotatedOffset( offset, angle );
2848 }
2849 
2850 
2852 {
2853  QgsStringMap map;
2854  map[QStringLiteral( "imageFile" )] = mPath;
2855  map[QStringLiteral( "size" )] = QString::number( mSize );
2856  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2857  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2858  map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2859  map[QStringLiteral( "angle" )] = QString::number( mAngle );
2860  map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
2861  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2862  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2863  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2864  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2865  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
2866  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
2867  return map;
2868 }
2869 
2871 {
2874  m->setOpacity( mOpacity );
2875  m->setOffset( mOffset );
2876  m->setOffsetUnit( mOffsetUnit );
2878  m->setSizeUnit( mSizeUnit );
2883  copyPaintEffect( m );
2884  return m;
2885 }
2886 
2888 {
2890 }
2891 
2893 {
2895 }
2896 
2898 {
2899  bool hasDataDefinedSize = false;
2900  double scaledSize = calculateSize( context, hasDataDefinedSize );
2901  double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
2902  bool hasDataDefinedAspectRatio = false;
2903  double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
2904  double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
2905 
2906  //don't render symbols with size below one or above 10,000 pixels
2907  if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
2908  {
2909  return QRectF();
2910  }
2911 
2912  QPointF outputOffset;
2913  double angle = 0.0;
2914  calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
2915 
2916  QMatrix transform;
2917 
2918  // move to the desired position
2919  transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
2920 
2921  if ( !qgsDoubleNear( angle, 0.0 ) )
2922  transform.rotate( angle );
2923 
2924  QRectF symbolBounds = transform.mapRect( QRectF( -width / 2.0,
2925  -height / 2.0,
2926  width,
2927  height ) );
2928 
2929  return symbolBounds;
2930 }
2931 
2933 
2934 QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QString chr, double pointSize, const QColor &color, double angle )
2935 {
2936  mFontFamily = fontFamily;
2937  mString = chr;
2938  mColor = color;
2939  mAngle = angle;
2940  mSize = pointSize;
2941  mOrigSize = pointSize;
2943  mOffset = QPointF( 0, 0 );
2945  mStrokeColor = DEFAULT_FONTMARKER_BORDERCOLOR;
2946  mStrokeWidth = 0.0;
2947  mStrokeWidthUnit = QgsUnitTypes::RenderMillimeters;
2948  mPenJoinStyle = DEFAULT_FONTMARKER_JOINSTYLE;
2949 }
2950 
2952 {
2953  delete mFontMetrics;
2954 }
2955 
2957 {
2958  QString fontFamily = DEFAULT_FONTMARKER_FONT;
2959  QString string = DEFAULT_FONTMARKER_CHR;
2960  double pointSize = DEFAULT_FONTMARKER_SIZE;
2962  double angle = DEFAULT_FONTMARKER_ANGLE;
2963 
2964  if ( props.contains( QStringLiteral( "font" ) ) )
2965  fontFamily = props[QStringLiteral( "font" )];
2966  if ( props.contains( QStringLiteral( "chr" ) ) && props[QStringLiteral( "chr" )].length() > 0 )
2967  string = props[QStringLiteral( "chr" )];
2968  if ( props.contains( QStringLiteral( "size" ) ) )
2969  pointSize = props[QStringLiteral( "size" )].toDouble();
2970  if ( props.contains( QStringLiteral( "color" ) ) )
2971  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] );
2972  if ( props.contains( QStringLiteral( "angle" ) ) )
2973  angle = props[QStringLiteral( "angle" )].toDouble();
2974 
2975  QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( fontFamily, string, pointSize, color, angle );
2976 
2977  if ( props.contains( QStringLiteral( "outline_color" ) ) )
2978  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )] ) );
2979  if ( props.contains( QStringLiteral( "outline_width" ) ) )
2980  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
2981  if ( props.contains( QStringLiteral( "offset" ) ) )
2982  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
2983  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
2984  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
2985  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2986  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
2987  if ( props.contains( QStringLiteral( "size_unit" ) ) )
2988  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
2989  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
2990  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
2991  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
2992  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )] ) );
2993  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
2994  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )] ) );
2995  if ( props.contains( QStringLiteral( "joinstyle" ) ) )
2996  m->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )] ) );
2997  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2998  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2999  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3000  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3001 
3002  m->restoreOldDataDefinedProperties( props );
3003 
3004  return m;
3005 }
3006 
3008 {
3009  return QStringLiteral( "FontMarker" );
3010 }
3011 
3013 {
3014  QColor brushColor = mColor;
3015  QColor penColor = mStrokeColor;
3016 
3017  brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
3018  penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
3019 
3020  mBrush = QBrush( brushColor );
3021  mPen = QPen( penColor );
3022  mPen.setJoinStyle( mPenJoinStyle );
3023  mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
3024 
3025  mFont = QFont( mFontFamily );
3026  const double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3027  mNonZeroFontSize = !qgsDoubleNear( sizePixels, 0.0 );
3028  // if a non zero, but small pixel size results, round up to 2 pixels so that a "dot" is at least visible
3029  // (if we set a <=1 pixel size here Qt will reset the font to a default size, leading to much too large symbols)
3030  mFont.setPixelSize( std::max( 2, static_cast< int >( std::round( sizePixels ) ) ) );
3031  delete mFontMetrics;
3032  mFontMetrics = new QFontMetrics( mFont );
3033  mChrWidth = mFontMetrics->width( mString );
3034  mChrOffset = QPointF( mChrWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3035  mOrigSize = mSize; // save in case the size would be data defined
3036 
3037  // use caching only when not using a data defined character
3039  if ( mUseCachedPath )
3040  {
3041  QPointF chrOffset = mChrOffset;
3042  double chrWidth;
3043  QString charToRender = characterToRender( context, chrOffset, chrWidth );
3044  mCachedPath = QPainterPath();
3045  mCachedPath.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3046  }
3047 }
3048 
3050 {
3051  Q_UNUSED( context )
3052 }
3053 
3054 QString QgsFontMarkerSymbolLayer::characterToRender( QgsSymbolRenderContext &context, QPointF &charOffset, double &charWidth )
3055 {
3056  charOffset = mChrOffset;
3057  QString stringToRender = mString;
3059  {
3060  context.setOriginalValueVariable( mString );
3062  if ( stringToRender != mString )
3063  {
3064  charWidth = mFontMetrics->width( stringToRender );
3065  charOffset = QPointF( charWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3066  }
3067  }
3068  return stringToRender;
3069 }
3070 
3071 void QgsFontMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
3072  double scaledSize,
3073  bool &hasDataDefinedRotation,
3074  QPointF &offset,
3075  double &angle ) const
3076 {
3077  //offset
3078  double offsetX = 0;
3079  double offsetY = 0;
3080  markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
3081  offset = QPointF( offsetX, offsetY );
3082 
3083  //angle
3084  bool ok = true;
3085  angle = mAngle + mLineAngle;
3086  bool usingDataDefinedRotation = false;
3088  {
3089  context.setOriginalValueVariable( angle );
3091  usingDataDefinedRotation = ok;
3092  }
3093 
3094  hasDataDefinedRotation = context.renderHints() & QgsSymbol::DynamicRotation || usingDataDefinedRotation;
3095  if ( hasDataDefinedRotation )
3096  {
3097  // For non-point markers, "dataDefinedRotation" means following the
3098  // shape (shape-data defined). For them, "field-data defined" does
3099  // not work at all. TODO: if "field-data defined" ever gets implemented
3100  // we'll need a way to distinguish here between the two, possibly
3101  // using another flag in renderHints()
3102  const QgsFeature *f = context.feature();
3103  if ( f )
3104  {
3105  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
3106  {
3107  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3108  angle += m2p.mapRotation();
3109  }
3110  }
3111  }
3112 
3113  if ( angle )
3114  offset = _rotatedOffset( offset, angle );
3115 }
3116 
3117 double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context )
3118 {
3119  double scaledSize = mSize;
3120  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
3121 
3122  bool ok = true;
3123  if ( hasDataDefinedSize )
3124  {
3125  context.setOriginalValueVariable( mSize );
3127  }
3128 
3129  if ( hasDataDefinedSize && ok )
3130  {
3131  switch ( mScaleMethod )
3132  {
3133  case QgsSymbol::ScaleArea:
3134  scaledSize = std::sqrt( scaledSize );
3135  break;
3137  break;
3138  }
3139  }
3140  return scaledSize;
3141 }
3142 
3144 {
3145  QPainter *p = context.renderContext().painter();
3146  if ( !p || !mNonZeroFontSize )
3147  return;
3148 
3149  QTransform transform;
3150 
3151  bool ok;
3152  QColor brushColor = mColor;
3154  {
3157  }
3158  brushColor = context.selected() ? context.renderContext().selectionColor() : brushColor;
3159  brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
3160  mBrush.setColor( brushColor );
3161 
3162  QColor penColor = mStrokeColor;
3164  {
3167  }
3168  penColor.setAlphaF( penColor.alphaF() * context.opacity() );
3169 
3170  double penWidth = context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3172  {
3173  context.setOriginalValueVariable( mStrokeWidth );
3174  double strokeWidth = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyStrokeWidth, context.renderContext().expressionContext(), mStrokeWidth, &ok );
3175  if ( ok )
3176  {
3177  penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3178  }
3179  }
3180 
3182  {
3185  if ( ok )
3186  {
3187  mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
3188  }
3189  }
3190 
3191  p->save();
3192  p->setBrush( mBrush );
3193  if ( !qgsDoubleNear( penWidth, 0.0 ) )
3194  {
3195  mPen.setColor( penColor );
3196  mPen.setWidthF( penWidth );
3197  p->setPen( mPen );
3198  }
3199  else
3200  {
3201  p->setPen( Qt::NoPen );
3202  }
3203 
3204  QPointF chrOffset = mChrOffset;
3205  double chrWidth;
3206  QString charToRender = characterToRender( context, chrOffset, chrWidth );
3207 
3208  double sizeToRender = calculateSize( context );
3209 
3210  bool hasDataDefinedRotation = false;
3211  QPointF offset;
3212  double angle = 0;
3213  calculateOffsetAndRotation( context, sizeToRender, hasDataDefinedRotation, offset, angle );
3214 
3215  p->translate( point.x() + offset.x(), point.y() + offset.y() );
3216 
3217  if ( !qgsDoubleNear( angle, 0.0 ) )
3218  transform.rotate( angle );
3219 
3220  if ( !qgsDoubleNear( sizeToRender, mOrigSize ) )
3221  {
3222  double s = sizeToRender / mOrigSize;
3223  transform.scale( s, s );
3224  }
3225 
3226  if ( mUseCachedPath )
3227  {
3228  p->drawPath( transform.map( mCachedPath ) );
3229  }
3230  else
3231  {
3232  QPainterPath path;
3233  path.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3234  p->drawPath( transform.map( path ) );
3235  }
3236 
3237  p->restore();
3238 }
3239 
3241 {
3242  QgsStringMap props;
3243  props[QStringLiteral( "font" )] = mFontFamily;
3244  props[QStringLiteral( "chr" )] = mString;
3245  props[QStringLiteral( "size" )] = QString::number( mSize );
3246  props[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3247  props[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3248  props[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
3249  props[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
3250  props[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
3251  props[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
3252  props[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
3253  props[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
3254  props[QStringLiteral( "angle" )] = QString::number( mAngle );
3255  props[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3256  props[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3257  props[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3258  props[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3259  props[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3260  return props;
3261 }
3262 
3264 {
3265  QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( mFontFamily, mString, mSize, mColor, mAngle );
3266  m->setStrokeColor( mStrokeColor );
3267  m->setStrokeWidth( mStrokeWidth );
3268  m->setStrokeWidthUnit( mStrokeWidthUnit );
3269  m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
3270  m->setPenJoinStyle( mPenJoinStyle );
3271  m->setOffset( mOffset );
3272  m->setOffsetUnit( mOffsetUnit );
3274  m->setSizeUnit( mSizeUnit );
3279  copyPaintEffect( m );
3280  return m;
3281 }
3282 
3283 void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
3284 {
3285  // <Graphic>
3286  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
3287  element.appendChild( graphicElem );
3288 
3289  QString fontPath = QStringLiteral( "ttf://%1" ).arg( mFontFamily );
3290  int markIndex = !mString.isEmpty() ? mString.at( 0 ).unicode() : 0;
3292  QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, QStringLiteral( "ttf" ), &markIndex, mColor, size );
3293 
3294  // <Rotation>
3295  QString angleFunc;
3296  bool ok;
3297  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
3298  if ( !ok )
3299  {
3300  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mAngle );
3301  }
3302  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
3303  {
3304  angleFunc = QString::number( angle + mAngle );
3305  }
3306  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
3307 
3308  // <Displacement>
3309  QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
3310  QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
3311 }
3312 
3314 {
3315  QPointF chrOffset = mChrOffset;
3316  double chrWidth = mChrWidth;
3317  //calculate width of rendered character
3318  ( void )characterToRender( context, chrOffset, chrWidth );
3319 
3320  if ( !mFontMetrics )
3321  mFontMetrics = new QFontMetrics( mFont );
3322 
3323  double scaledSize = calculateSize( context );
3324  if ( !qgsDoubleNear( scaledSize, mOrigSize ) )
3325  {
3326  chrWidth *= scaledSize / mOrigSize;
3327  }
3328 
3329  bool hasDataDefinedRotation = false;
3330  QPointF offset;
3331  double angle = 0;
3332  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
3333  scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3334 
3335  QMatrix transform;
3336 
3337  // move to the desired position
3338  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
3339 
3340  if ( !qgsDoubleNear( angle, 0.0 ) )
3341  transform.rotate( angle );
3342 
3343  QRectF symbolBounds = transform.mapRect( QRectF( -chrWidth / 2.0,
3344  -scaledSize / 2.0,
3345  chrWidth,
3346  scaledSize ) );
3347  return symbolBounds;
3348 }
3349 
3351 {
3352  QgsDebugMsg( QStringLiteral( "Entered." ) );
3353 
3354  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
3355  if ( graphicElem.isNull() )
3356  return nullptr;
3357 
3358  QString name, format;
3359  QColor color;
3360  double size;
3361  int chr;
3362 
3363  if ( !QgsSymbolLayerUtils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
3364  return nullptr;
3365 
3366  if ( !name.startsWith( QLatin1String( "ttf://" ) ) || format != QLatin1String( "ttf" ) )
3367  return nullptr;
3368 
3369  QString fontFamily = name.mid( 6 );
3370 
3371  double angle = 0.0;
3372  QString angleFunc;
3373  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
3374  {
3375  bool ok;
3376  double d = angleFunc.toDouble( &ok );
3377  if ( ok )
3378  angle = d;
3379  }
3380 
3381  QPointF offset;
3382  QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, offset );
3383 
3384  QString uom = element.attribute( QStringLiteral( "uom" ) );
3385  offset.setX( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.x() ) );
3386  offset.setY( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.y() ) );
3387  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
3388 
3389  QgsMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( fontFamily, QChar( chr ), size, color );
3390  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
3391  m->setAngle( angle );
3392  m->setOffset( offset );
3393  return m;
3394 }
3395 
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:154
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:774
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:97
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:61
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:1251
void startRender(QgsSymbolRenderContext &context) override
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:280
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
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:612
Rotation of symbol may be changed during rendering and symbol should not be cached.
Definition: qgssymbol.h:104
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
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:1192
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
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...
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:96
double mOpacity
The marker default opacity.
qreal opacity() const
Returns the opacity for the symbol.
Definition: qgssymbol.h:711
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:94
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:668
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
bool selected() const
Returns true if symbols should be rendered using the selected symbol coloring and style...
Definition: qgssymbol.h:724
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
QVector< QgsPoint > QgsPointSequence
void stopRender(QgsSymbolRenderContext &context) override
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:87
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:120
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
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:749
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
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
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:736
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...