QGIS API Documentation  3.9.0-Master (224899f119)
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 
862  // calculate necessary image size for the cache
863  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
864  int imageSize = ( static_cast< int >( scaledSize ) + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width
865  double center = imageSize / 2.0;
866 
867  if ( imageSize > MAXIMUM_CACHE_WIDTH )
868  {
869  return false;
870  }
871 
872  mCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
873  mCache.fill( 0 );
874 
875  bool needsBrush = shapeIsFilled( mShape );
876 
877  QPainter p;
878  p.begin( &mCache );
879  p.setRenderHint( QPainter::Antialiasing );
880  p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
881  p.setPen( mPen );
882  p.translate( QPointF( center, center ) );
883  drawMarker( &p, context );
884  p.end();
885 
886  // Construct the selected version of the Cache
887 
888  QColor selColor = context.renderContext().selectionColor();
889 
890  mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
891  mSelCache.fill( 0 );
892 
893  p.begin( &mSelCache );
894  p.setRenderHint( QPainter::Antialiasing );
895  p.setBrush( needsBrush ? mSelBrush : Qt::NoBrush );
896  p.setPen( mSelPen );
897  p.translate( QPointF( center, center ) );
898  drawMarker( &p, context );
899  p.end();
900 
901  // Check that the selected version is different. If not, then re-render,
902  // filling the background with the selection color and using the normal
903  // colors for the symbol .. could be ugly!
904 
905  if ( mSelCache == mCache )
906  {
907  p.begin( &mSelCache );
908  p.setRenderHint( QPainter::Antialiasing );
909  p.fillRect( 0, 0, imageSize, imageSize, selColor );
910  p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
911  p.setPen( mPen );
912  p.translate( QPointF( center, center ) );
913  drawMarker( &p, context );
914  p.end();
915  }
916 
917  return true;
918 }
919 
920 void QgsSimpleMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, QgsSimpleMarkerSymbolLayerBase::Shape shape, const QPolygonF &polygon, const QPainterPath &path )
921 {
922  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
923  //of the rendered point!
924 
925  QPainter *p = context.renderContext().painter();
926  if ( !p )
927  {
928  return;
929  }
930 
931  bool ok = true;
933  {
936  if ( ok )
937  mBrush.setColor( c );
938  }
940  {
943  if ( ok )
944  {
945  mPen.setColor( c );
946  mSelPen.setColor( c );
947  }
948  }
950  {
953  if ( ok )
954  {
955  mPen.setWidthF( context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
957  }
958  }
960  {
963  if ( ok )
964  {
965  mPen.setStyle( QgsSymbolLayerUtils::decodePenStyle( strokeStyle ) );
966  mSelPen.setStyle( QgsSymbolLayerUtils::decodePenStyle( strokeStyle ) );
967  }
968  }
970  {
973  if ( ok )
974  {
975  mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
976  mSelPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
977  }
978  }
979 
980  if ( shapeIsFilled( shape ) )
981  {
982  p->setBrush( context.selected() ? mSelBrush : mBrush );
983  }
984  else
985  {
986  p->setBrush( Qt::NoBrush );
987  }
988  p->setPen( context.selected() ? mSelPen : mPen );
989 
990  if ( !polygon.isEmpty() )
991  p->drawPolygon( polygon );
992  else
993  p->drawPath( path );
994 }
995 
997 {
998  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
999  //of the rendered point!
1000 
1001  QPainter *p = context.renderContext().painter();
1002  if ( !p )
1003  {
1004  return;
1005  }
1006 
1007  if ( mUsingCache )
1008  {
1009  QImage &img = context.selected() ? mSelCache : mCache;
1010  double s = img.width();
1011 
1012  bool hasDataDefinedSize = false;
1013  double scaledSize = calculateSize( context, hasDataDefinedSize );
1014 
1015  bool hasDataDefinedRotation = false;
1016  QPointF offset;
1017  double angle = 0;
1018  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
1019 
1020  p->drawImage( QRectF( point.x() - s / 2.0 + offset.x(),
1021  point.y() - s / 2.0 + offset.y(),
1022  s, s ), img );
1023  }
1024  else
1025  {
1027  }
1028 }
1029 
1031 {
1032  QgsStringMap map;
1033  map[QStringLiteral( "name" )] = encodeShape( mShape );
1034  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
1035  map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
1036  map[QStringLiteral( "size" )] = QString::number( mSize );
1037  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1038  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1039  map[QStringLiteral( "angle" )] = QString::number( mAngle );
1040  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1041  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1042  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1043  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1044  map[QStringLiteral( "outline_style" )] = QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle );
1045  map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
1046  map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
1047  map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
1048  map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
1049  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1050  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1051  return map;
1052 }
1053 
1055 {
1057  m->setOffset( mOffset );
1058  m->setSizeUnit( mSizeUnit );
1060  m->setOffsetUnit( mOffsetUnit );
1069  copyPaintEffect( m );
1070  return m;
1071 }
1072 
1073 void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
1074 {
1075  // <Graphic>
1076  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
1077  element.appendChild( graphicElem );
1078 
1081  QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, encodeShape( mShape ), mColor, mStrokeColor, mStrokeStyle, strokeWidth, size );
1082 
1083  // <Rotation>
1084  QString angleFunc;
1085  bool ok;
1086  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
1087  if ( !ok )
1088  {
1089  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mAngle );
1090  }
1091  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
1092  {
1093  angleFunc = QString::number( angle + mAngle );
1094  }
1095  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
1096 
1097  // <Displacement>
1099  QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
1100 }
1101 
1102 QString QgsSimpleMarkerSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
1103 {
1104  Q_UNUSED( mmScaleFactor )
1105  Q_UNUSED( mapUnitScaleFactor )
1106 #if 0
1107  QString ogrType = "3"; //default is circle
1108  if ( mName == "square" )
1109  {
1110  ogrType = "5";
1111  }
1112  else if ( mName == "triangle" )
1113  {
1114  ogrType = "7";
1115  }
1116  else if ( mName == "star" )
1117  {
1118  ogrType = "9";
1119  }
1120  else if ( mName == "circle" )
1121  {
1122  ogrType = "3";
1123  }
1124  else if ( mName == "cross" )
1125  {
1126  ogrType = "0";
1127  }
1128  else if ( mName == "x" || mName == "cross2" )
1129  {
1130  ogrType = "1";
1131  }
1132  else if ( mName == "line" )
1133  {
1134  ogrType = "10";
1135  }
1136 
1137  QString ogrString;
1138  ogrString.append( "SYMBOL(" );
1139  ogrString.append( "id:" );
1140  ogrString.append( '\"' );
1141  ogrString.append( "ogr-sym-" );
1142  ogrString.append( ogrType );
1143  ogrString.append( '\"' );
1144  ogrString.append( ",c:" );
1145  ogrString.append( mColor.name() );
1146  ogrString.append( ",o:" );
1147  ogrString.append( mStrokeColor.name() );
1148  ogrString.append( QString( ",s:%1mm" ).arg( mSize ) );
1149  ogrString.append( ')' );
1150  return ogrString;
1151 #endif //0
1152 
1153  QString ogrString;
1154  ogrString.append( "PEN(" );
1155  ogrString.append( "c:" );
1156  ogrString.append( mColor.name() );
1157  ogrString.append( ",w:" );
1158  ogrString.append( QString::number( mSize ) );
1159  ogrString.append( "mm" );
1160  ogrString.append( ")" );
1161  return ogrString;
1162 }
1163 
1165 {
1166  QgsDebugMsg( QStringLiteral( "Entered." ) );
1167 
1168  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
1169  if ( graphicElem.isNull() )
1170  return nullptr;
1171 
1172  QString name = QStringLiteral( "square" );
1173  QColor color, strokeColor;
1174  double strokeWidth, size;
1175  Qt::PenStyle strokeStyle;
1176 
1177  if ( !QgsSymbolLayerUtils::wellKnownMarkerFromSld( graphicElem, name, color, strokeColor, strokeStyle, strokeWidth, size ) )
1178  return nullptr;
1179 
1180  double angle = 0.0;
1181  QString angleFunc;
1182  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
1183  {
1184  bool ok;
1185  double d = angleFunc.toDouble( &ok );
1186  if ( ok )
1187  angle = d;
1188  }
1189 
1190  QPointF offset;
1191  QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, offset );
1192 
1193  Shape shape = decodeShape( name );
1194 
1195  QString uom = element.attribute( QStringLiteral( "uom" ) );
1196  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
1197  offset.setX( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.x() ) );
1198  offset.setY( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.y() ) );
1199 
1201  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
1202  m->setColor( color );
1203  m->setStrokeColor( strokeColor );
1204  m->setAngle( angle );
1205  m->setOffset( offset );
1206  m->setStrokeStyle( strokeStyle );
1207  m->setStrokeWidth( strokeWidth );
1208  return m;
1209 }
1210 
1212 {
1213  Q_UNUSED( context )
1214 
1215  if ( mPolygon.count() != 0 )
1216  {
1217  p->drawPolygon( mPolygon );
1218  }
1219  else
1220  {
1221  p->drawPath( mPath );
1222  }
1223 }
1224 
1225 bool QgsSimpleMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
1226 {
1227  Q_UNUSED( mmMapUnitScaleFactor )
1228 
1229  //data defined size?
1230  double size = mSize;
1231 
1232  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
1233 
1234  //data defined size
1235  bool ok = true;
1236  if ( hasDataDefinedSize )
1237  {
1239 
1240  if ( ok )
1241  {
1242  switch ( mScaleMethod )
1243  {
1244  case QgsSymbol::ScaleArea:
1245  size = std::sqrt( size );
1246  break;
1248  break;
1249  }
1250  }
1251 
1253  }
1255  {
1257  }
1258  double halfSize = size / 2.0;
1259 
1260  //strokeWidth
1261  double strokeWidth = mStrokeWidth;
1262 
1264  {
1267  }
1270  {
1272  }
1273 
1274  //color
1275  QColor pc = mPen.color();
1276  QColor bc = mBrush.color();
1278  {
1281  }
1283  {
1286  }
1287 
1288  //offset
1289  double offsetX = 0;
1290  double offsetY = 0;
1291  markerOffset( context, offsetX, offsetY );
1292  offsetX *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1293  offsetY *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1294 
1295 
1296  QPointF off( offsetX, offsetY );
1297 
1298  //angle
1299  double angle = mAngle + mLineAngle;
1301  {
1302  context.setOriginalValueVariable( mAngle );
1304  }
1305 
1306  Shape shape = mShape;
1308  {
1309  context.setOriginalValueVariable( encodeShape( shape ) );
1310  QString shapeName = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyName, context.renderContext().expressionContext(), QString(), &ok );
1311  if ( ok )
1312  {
1313  shape = decodeShape( shapeName, &ok );
1314  if ( !ok )
1315  shape = mShape;
1316  }
1317  }
1318 
1319  if ( angle )
1320  off = _rotatedOffset( off, angle );
1321 
1323 
1324  QTransform t;
1325  t.translate( shift.x() + off.x(), shift.y() - off.y() );
1326 
1327  if ( !qgsDoubleNear( angle, 0.0 ) )
1328  t.rotate( angle );
1329 
1330  QPolygonF polygon;
1331  if ( shapeToPolygon( shape, polygon ) )
1332  {
1333  t.scale( halfSize, -halfSize );
1334 
1335  polygon = t.map( polygon );
1336 
1337  QgsPointSequence p;
1338  p.reserve( polygon.size() );
1339  for ( int i = 0; i < polygon.size(); i++ )
1340  {
1341  p << QgsPoint( polygon[i] );
1342  }
1343 
1344  if ( mBrush.style() != Qt::NoBrush )
1345  e.writePolygon( QgsRingSequence() << p, layerName, QStringLiteral( "SOLID" ), bc );
1346  if ( mPen.style() != Qt::NoPen )
1347  e.writePolyline( p, layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1348  }
1349  else if ( shape == Circle )
1350  {
1351  shift += QPointF( off.x(), -off.y() );
1352  if ( mBrush.style() != Qt::NoBrush )
1353  e.writeFilledCircle( layerName, bc, QgsPoint( shift ), halfSize );
1354  if ( mPen.style() != Qt::NoPen )
1355  e.writeCircle( layerName, pc, QgsPoint( shift ), halfSize, QStringLiteral( "CONTINUOUS" ), strokeWidth );
1356  }
1357  else if ( shape == Line )
1358  {
1359  QPointF pt1 = t.map( QPointF( 0, -halfSize ) );
1360  QPointF pt2 = t.map( QPointF( 0, halfSize ) );
1361 
1362  if ( mPen.style() != Qt::NoPen )
1363  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1364  }
1365  else if ( shape == Cross )
1366  {
1367  if ( mPen.style() != Qt::NoPen )
1368  {
1369  QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
1370  QPointF pt2 = t.map( QPointF( halfSize, 0 ) );
1371  QPointF pt3 = t.map( QPointF( 0, -halfSize ) );
1372  QPointF pt4 = t.map( QPointF( 0, halfSize ) );
1373 
1374  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1375  e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1376  }
1377  }
1378  else if ( shape == Cross2 )
1379  {
1380  if ( mPen.style() != Qt::NoPen )
1381  {
1382  QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
1383  QPointF pt2 = t.map( QPointF( halfSize, halfSize ) );
1384  QPointF pt3 = t.map( QPointF( halfSize, -halfSize ) );
1385  QPointF pt4 = t.map( QPointF( -halfSize, halfSize ) );
1386 
1387  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1388  e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1389  }
1390  }
1391  else if ( shape == ArrowHead )
1392  {
1393  if ( mPen.style() != Qt::NoPen )
1394  {
1395  QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
1396  QPointF pt2 = t.map( QPointF( 0, 0 ) );
1397  QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
1398 
1399  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1400  e.writeLine( QgsPoint( pt3 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1401  }
1402  }
1403  else
1404  {
1405  QgsDebugMsg( QStringLiteral( "Unsupported dxf marker name %1" ).arg( encodeShape( shape ) ) );
1406  return false;
1407  }
1408 
1409  return true;
1410 }
1411 
1412 
1414 {
1416  mStrokeWidthUnit = unit;
1417 }
1418 
1420 {
1422  {
1423  return mStrokeWidthUnit;
1424  }
1426 }
1427 
1429 {
1431  mStrokeWidthMapUnitScale = scale;
1432 }
1433 
1435 {
1437  {
1438  return mStrokeWidthMapUnitScale;
1439  }
1440  return QgsMapUnitScale();
1441 }
1442 
1444 {
1445  QRectF symbolBounds = QgsSimpleMarkerSymbolLayerBase::bounds( point, context );
1446 
1447  // need to account for stroke width
1448  double penWidth = 0.0;
1449  bool ok = true;
1451  {
1454  if ( ok )
1455  {
1456  penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
1457  }
1458  }
1460  {
1463  if ( ok && strokeStyle == QLatin1String( "no" ) )
1464  {
1465  penWidth = 0.0;
1466  }
1467  }
1468  //antialiasing, add 1 pixel
1469  penWidth += 1;
1470 
1471  //extend bounds by pen width / 2.0
1472  symbolBounds.adjust( -penWidth / 2.0, -penWidth / 2.0,
1473  penWidth / 2.0, penWidth / 2.0 );
1474 
1475  return symbolBounds;
1476 }
1477 
1479 {
1480  if ( shapeIsFilled( mShape ) )
1481  {
1482  setFillColor( color );
1483  }
1484  else
1485  {
1486  setStrokeColor( color );
1487  }
1488 }
1489 
1491 {
1492  if ( shapeIsFilled( mShape ) )
1493  {
1494  return fillColor();
1495  }
1496  else
1497  {
1498  return strokeColor();
1499  }
1500 }
1501 
1502 
1503 
1504 
1505 //
1506 // QgsFilledMarkerSymbolLayer
1507 //
1508 
1510  : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
1511 {
1512  mFill.reset( static_cast<QgsFillSymbol *>( QgsFillSymbol::createSimple( QgsStringMap() ) ) );
1513 }
1514 
1516 {
1517  QString name = DEFAULT_SIMPLEMARKER_NAME;
1521 
1522  if ( props.contains( QStringLiteral( "name" ) ) )
1523  name = props[QStringLiteral( "name" )];
1524  if ( props.contains( QStringLiteral( "size" ) ) )
1525  size = props[QStringLiteral( "size" )].toDouble();
1526  if ( props.contains( QStringLiteral( "angle" ) ) )
1527  angle = props[QStringLiteral( "angle" )].toDouble();
1528  if ( props.contains( QStringLiteral( "scale_method" ) ) )
1529  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )] );
1530 
1531  QgsFilledMarkerSymbolLayer *m = new QgsFilledMarkerSymbolLayer( decodeShape( name ), size, angle, scaleMethod );
1532  if ( props.contains( QStringLiteral( "offset" ) ) )
1533  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
1534  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1535  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
1536  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1537  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
1538  if ( props.contains( QStringLiteral( "size_unit" ) ) )
1539  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
1540  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1541  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
1542  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1543  {
1544  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1545  }
1546  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1547  {
1548  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1549  }
1550 
1552 
1553  m->restoreOldDataDefinedProperties( props );
1554 
1555  return m;
1556 }
1557 
1559 {
1560  return QStringLiteral( "FilledMarker" );
1561 }
1562 
1564 {
1565  if ( mFill )
1566  {
1567  mFill->startRender( context.renderContext(), context.fields() );
1568  }
1569 
1571 }
1572 
1574 {
1575  if ( mFill )
1576  {
1577  mFill->stopRender( context.renderContext() );
1578  }
1579 }
1580 
1582 {
1583  QgsStringMap map;
1584  map[QStringLiteral( "name" )] = encodeShape( mShape );
1585  map[QStringLiteral( "size" )] = QString::number( mSize );
1586  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1587  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1588  map[QStringLiteral( "angle" )] = QString::number( mAngle );
1589  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1590  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1591  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1592  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1593  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1594  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1595 
1596  if ( mFill )
1597  {
1598  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mFill->color() );
1599  }
1600  return map;
1601 }
1602 
1604 {
1606  copyPaintEffect( m );
1608  m->setSubSymbol( mFill->clone() );
1609  return m;
1610 }
1611 
1613 {
1614  return mFill.get();
1615 }
1616 
1618 {
1619  if ( symbol && symbol->type() == QgsSymbol::Fill )
1620  {
1621  mFill.reset( static_cast<QgsFillSymbol *>( symbol ) );
1622  return true;
1623  }
1624  else
1625  {
1626  delete symbol;
1627  return false;
1628  }
1629 }
1630 
1632 {
1633  if ( mFill )
1634  {
1635  return QgsSymbolLayerUtils::estimateMaxSymbolBleed( mFill.get(), context );
1636  }
1637  return 0;
1638 }
1639 
1641 {
1642  QSet<QString> attr = QgsSimpleMarkerSymbolLayerBase::usedAttributes( context );
1643  if ( mFill )
1644  attr.unite( mFill->usedAttributes( context ) );
1645  return attr;
1646 }
1647 
1649 {
1651  return true;
1652  if ( mFill && mFill->hasDataDefinedProperties() )
1653  return true;
1654  return false;
1655 }
1656 
1658 {
1659  mColor = c;
1660  if ( mFill )
1661  mFill->setColor( c );
1662 }
1663 
1665 {
1666  return mFill ? mFill->color() : mColor;
1667 }
1668 
1669 void QgsFilledMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, QgsSimpleMarkerSymbolLayerBase::Shape shape, const QPolygonF &polygon, const QPainterPath &path )
1670 {
1671  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1672  //of the rendered point!
1673 
1674  QPainter *p = context.renderContext().painter();
1675  if ( !p )
1676  {
1677  return;
1678  }
1679 
1680  if ( shapeIsFilled( shape ) )
1681  {
1682  p->setBrush( Qt::red );
1683  }
1684  else
1685  {
1686  p->setBrush( Qt::NoBrush );
1687  }
1688  p->setPen( Qt::black );
1689 
1690  if ( !polygon.isEmpty() )
1691  {
1692  mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
1693  }
1694  else
1695  {
1696  QPolygonF poly = path.toFillPolygon();
1697  mFill->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
1698  }
1699 
1700 
1701 }
1702 
1703 
1705 
1706 
1708 {
1709  mPath = path;
1710  mSize = size;
1711  mAngle = angle;
1712  mOffset = QPointF( 0, 0 );
1714  mStrokeWidth = 0.2;
1715  mStrokeWidthUnit = QgsUnitTypes::RenderMillimeters;
1716  mColor = QColor( 35, 35, 35 );
1717  mStrokeColor = QColor( 35, 35, 35 );
1718  updateDefaultAspectRatio();
1719 }
1720 
1721 
1723 {
1724  QString name;
1725  double size = DEFAULT_SVGMARKER_SIZE;
1726  double angle = DEFAULT_SVGMARKER_ANGLE;
1728 
1729  if ( props.contains( QStringLiteral( "name" ) ) )
1730  name = props[QStringLiteral( "name" )];
1731  if ( props.contains( QStringLiteral( "size" ) ) )
1732  size = props[QStringLiteral( "size" )].toDouble();
1733  if ( props.contains( QStringLiteral( "angle" ) ) )
1734  angle = props[QStringLiteral( "angle" )].toDouble();
1735  if ( props.contains( QStringLiteral( "scale_method" ) ) )
1736  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )] );
1737 
1738  QgsSvgMarkerSymbolLayer *m = new QgsSvgMarkerSymbolLayer( name, size, angle, scaleMethod );
1739 
1740  //we only check the svg default parameters if necessary, since it could be expensive
1741  if ( !props.contains( QStringLiteral( "fill" ) ) && !props.contains( QStringLiteral( "color" ) ) && !props.contains( QStringLiteral( "outline" ) ) &&
1742  !props.contains( QStringLiteral( "outline_color" ) ) && !props.contains( QStringLiteral( "outline-width" ) ) && !props.contains( QStringLiteral( "outline_width" ) ) )
1743  {
1744  QColor fillColor, strokeColor;
1745  double fillOpacity = 1.0;
1746  double strokeOpacity = 1.0;
1747  double strokeWidth;
1748  bool hasFillParam = false, hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
1749  bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
1750  QgsApplication::svgCache()->containsParams( name, hasFillParam, hasDefaultFillColor, fillColor,
1751  hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
1752  hasStrokeParam, hasDefaultStrokeColor, strokeColor,
1753  hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
1754  hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
1755  if ( hasDefaultFillColor )
1756  {
1757  m->setFillColor( fillColor );
1758  }
1759  if ( hasDefaultFillOpacity )
1760  {
1761  QColor c = m->fillColor();
1762  c.setAlphaF( fillOpacity );
1763  m->setFillColor( c );
1764  }
1765  if ( hasDefaultStrokeColor )
1766  {
1767  m->setStrokeColor( strokeColor );
1768  }
1769  if ( hasDefaultStrokeWidth )
1770  {
1771  m->setStrokeWidth( strokeWidth );
1772  }
1773  if ( hasDefaultStrokeOpacity )
1774  {
1775  QColor c = m->strokeColor();
1776  c.setAlphaF( strokeOpacity );
1777  m->setStrokeColor( c );
1778  }
1779  }
1780 
1781  if ( props.contains( QStringLiteral( "size_unit" ) ) )
1782  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
1783  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1784  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
1785  if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
1786  m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
1787  if ( props.contains( QStringLiteral( "offset" ) ) )
1788  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
1789  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1790  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
1791  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1792  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
1793  if ( props.contains( QStringLiteral( "fill" ) ) )
1794  {
1795  //pre 2.5 projects used "fill"
1796  m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "fill" )] ) );
1797  }
1798  else if ( props.contains( QStringLiteral( "color" ) ) )
1799  {
1800  m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] ) );
1801  }
1802  if ( props.contains( QStringLiteral( "outline" ) ) )
1803  {
1804  //pre 2.5 projects used "outline"
1805  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline" )] ) );
1806  }
1807  else if ( props.contains( QStringLiteral( "outline_color" ) ) )
1808  {
1809  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )] ) );
1810  }
1811  else if ( props.contains( QStringLiteral( "line_color" ) ) )
1812  {
1813  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )] ) );
1814  }
1815 
1816  if ( props.contains( QStringLiteral( "outline-width" ) ) )
1817  {
1818  //pre 2.5 projects used "outline-width"
1819  m->setStrokeWidth( props[QStringLiteral( "outline-width" )].toDouble() );
1820  }
1821  else if ( props.contains( QStringLiteral( "outline_width" ) ) )
1822  {
1823  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
1824  }
1825  else if ( props.contains( QStringLiteral( "line_width" ) ) )
1826  {
1827  m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
1828  }
1829 
1830  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
1831  {
1832  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )] ) );
1833  }
1834  else if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
1835  {
1836  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )] ) );
1837  }
1838  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
1839  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )] ) );
1840 
1841  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1842  {
1843  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1844  }
1845  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1846  {
1847  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1848  }
1849 
1850  m->restoreOldDataDefinedProperties( props );
1851 
1853 
1854  return m;
1855 }
1856 
1858 {
1859  QgsStringMap::iterator it = properties.find( QStringLiteral( "name" ) );
1860  if ( it != properties.end() )
1861  {
1862  if ( saving )
1863  it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value(), pathResolver );
1864  else
1865  it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value(), pathResolver );
1866  }
1867 }
1868 
1869 void QgsSvgMarkerSymbolLayer::setPath( const QString &path )
1870 {
1871  mPath = path;
1872  QColor defaultFillColor, defaultStrokeColor;
1873  double strokeWidth, fillOpacity, strokeOpacity;
1874  bool hasFillParam = false, hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
1875  bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
1876  QgsApplication::svgCache()->containsParams( path, hasFillParam, hasDefaultFillColor, defaultFillColor,
1877  hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
1878  hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
1879  hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
1880  hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
1881 
1882  double newFillOpacity = hasFillOpacityParam ? fillColor().alphaF() : 1.0;
1883  double newStrokeOpacity = hasStrokeOpacityParam ? strokeColor().alphaF() : 1.0;
1884 
1885  if ( hasDefaultFillColor )
1886  {
1887  defaultFillColor.setAlphaF( newFillOpacity );
1888  setFillColor( defaultFillColor );
1889  }
1890  if ( hasDefaultFillOpacity )
1891  {
1892  QColor c = fillColor();
1893  c.setAlphaF( fillOpacity );
1894  setFillColor( c );
1895  }
1896  if ( hasDefaultStrokeColor )
1897  {
1898  defaultStrokeColor.setAlphaF( newStrokeOpacity );
1899  setStrokeColor( defaultStrokeColor );
1900  }
1901  if ( hasDefaultStrokeWidth )
1902  {
1903  setStrokeWidth( strokeWidth );
1904  }
1905  if ( hasDefaultStrokeOpacity )
1906  {
1907  QColor c = strokeColor();
1908  c.setAlphaF( strokeOpacity );
1909  setStrokeColor( c );
1910  }
1911 
1912  updateDefaultAspectRatio();
1913 }
1914 
1916 {
1917  if ( mDefaultAspectRatio == 0.0 )
1918  {
1919  //size
1920  double size = mSize;
1921  //assume 88 dpi as standard value
1922  double widthScaleFactor = 3.465;
1923  QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( mPath, size, mColor, mStrokeColor, mStrokeWidth, widthScaleFactor );
1924  // set default aspect ratio
1925  mDefaultAspectRatio = svgViewbox.isValid() ? svgViewbox.height() / svgViewbox.width() : 0.0;
1926  }
1927  return mDefaultAspectRatio;
1928 }
1929 
1931 {
1932  bool aPreservedAspectRatio = preservedAspectRatio();
1933  if ( aPreservedAspectRatio && !par )
1934  {
1935  mFixedAspectRatio = mDefaultAspectRatio;
1936  }
1937  else if ( !aPreservedAspectRatio && par )
1938  {
1939  mFixedAspectRatio = 0.0;
1940  }
1941  return preservedAspectRatio();
1942 }
1943 
1944 
1946 {
1947  return QStringLiteral( "SvgMarker" );
1948 }
1949 
1951 {
1952  QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
1953  Q_UNUSED( context )
1954 }
1955 
1957 {
1958  Q_UNUSED( context )
1959 }
1960 
1962 {
1963  QPainter *p = context.renderContext().painter();
1964  if ( !p )
1965  return;
1966 
1967  bool hasDataDefinedSize = false;
1968  double scaledSize = calculateSize( context, hasDataDefinedSize );
1969  double size = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
1970 
1971  //don't render symbols with size below one or above 10,000 pixels
1972  if ( static_cast< int >( size ) < 1 || 10000.0 < size )
1973  {
1974  return;
1975  }
1976 
1977  p->save();
1978 
1979  bool hasDataDefinedAspectRatio = false;
1980  double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
1981 
1982  QPointF outputOffset;
1983  double angle = 0.0;
1984  calculateOffsetAndRotation( context, scaledSize, outputOffset, angle );
1985 
1986  p->translate( point + outputOffset );
1987 
1988  bool rotated = !qgsDoubleNear( angle, 0 );
1989  if ( rotated )
1990  p->rotate( angle );
1991 
1992  QString path = mPath;
1994  {
1995  context.setOriginalValueVariable( mPath );
1997  context.renderContext().pathResolver() );
1998  }
1999 
2000  double strokeWidth = mStrokeWidth;
2002  {
2003  context.setOriginalValueVariable( mStrokeWidth );
2005  }
2006  strokeWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
2007 
2008  QColor fillColor = mColor;
2010  {
2013  }
2014 
2015  QColor strokeColor = mStrokeColor;
2017  {
2020  }
2021 
2022  bool fitsInCache = true;
2023  bool usePict = true;
2024  double hwRatio = 1.0;
2025  if ( !context.renderContext().forceVectorOutput() && !rotated )
2026  {
2027  QImage img = QgsApplication::svgCache()->svgAsImage( path, size, fillColor, strokeColor, strokeWidth,
2028  context.renderContext().scaleFactor(), fitsInCache, aspectRatio );
2029  if ( fitsInCache && img.width() > 1 )
2030  {
2031  usePict = false;
2032  //consider transparency
2033  if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2034  {
2035  QImage transparentImage = img.copy();
2036  QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2037  p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
2038  hwRatio = static_cast< double >( transparentImage.height() ) / static_cast< double >( transparentImage.width() );
2039  }
2040  else
2041  {
2042  p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2043  hwRatio = static_cast< double >( img.height() ) / static_cast< double >( img.width() );
2044  }
2045  }
2046  }
2047 
2048  if ( usePict || !fitsInCache )
2049  {
2050  p->setOpacity( context.opacity() );
2051  QPicture pct = QgsApplication::svgCache()->svgAsPicture( path, size, fillColor, strokeColor, strokeWidth,
2052  context.renderContext().scaleFactor(), context.renderContext().forceVectorOutput(), aspectRatio );
2053  if ( pct.width() > 1 )
2054  {
2055  p->save();
2056  _fixQPictureDPI( p );
2057  p->drawPicture( 0, 0, pct );
2058  p->restore();
2059  hwRatio = static_cast< double >( pct.height() ) / static_cast< double >( pct.width() );
2060  }
2061  }
2062 
2063  if ( context.selected() )
2064  {
2065  QPen pen( context.renderContext().selectionColor() );
2066  double penWidth = context.renderContext().convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters );
2067  if ( penWidth > size / 20 )
2068  {
2069  // keep the pen width from covering symbol
2070  penWidth = size / 20;
2071  }
2072  double penOffset = penWidth / 2;
2073  pen.setWidth( penWidth );
2074  p->setPen( pen );
2075  p->setBrush( Qt::NoBrush );
2076  double wSize = size + penOffset;
2077  double hSize = size * hwRatio + penOffset;
2078  p->drawRect( QRectF( -wSize / 2.0, -hSize / 2.0, wSize, hSize ) );
2079  }
2080 
2081  p->restore();
2082 
2084  {
2085  // workaround issue with nested QPictures forgetting antialiasing flag - see https://github.com/qgis/QGIS/issues/22909
2086  p->setRenderHint( QPainter::Antialiasing );
2087  }
2088 
2089 }
2090 
2091 double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2092 {
2093  double scaledSize = mSize;
2095 
2096  bool ok = true;
2097  if ( hasDataDefinedSize )
2098  {
2099  context.setOriginalValueVariable( mSize );
2101  }
2102  else
2103  {
2105  if ( hasDataDefinedSize )
2106  {
2107  context.setOriginalValueVariable( mSize );
2109  }
2110  }
2111 
2112  if ( hasDataDefinedSize && ok )
2113  {
2114  switch ( mScaleMethod )
2115  {
2116  case QgsSymbol::ScaleArea:
2117  scaledSize = std::sqrt( scaledSize );
2118  break;
2120  break;
2121  }
2122  }
2123 
2124  return scaledSize;
2125 }
2126 
2127 double QgsSvgMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2128 {
2130  if ( !hasDataDefinedAspectRatio )
2131  return mFixedAspectRatio;
2132 
2133  if ( !mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyHeight ) && mFixedAspectRatio <= 0.0 )
2134  return 0.0;
2135 
2136  double scaledAspectRatio = mDefaultAspectRatio;
2137  if ( mFixedAspectRatio > 0.0 )
2138  scaledAspectRatio = mFixedAspectRatio;
2139 
2140  double defaultHeight = mSize * scaledAspectRatio;
2141  scaledAspectRatio = defaultHeight / scaledSize;
2142 
2143  bool ok = true;
2144  double scaledHeight = scaledSize * scaledAspectRatio;
2146  {
2147  context.setOriginalValueVariable( defaultHeight );
2148  scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyHeight, context.renderContext().expressionContext(), defaultHeight, &ok );
2149  }
2150 
2151  if ( hasDataDefinedAspectRatio && ok )
2152  {
2153  switch ( mScaleMethod )
2154  {
2155  case QgsSymbol::ScaleArea:
2156  scaledHeight = sqrt( scaledHeight );
2157  break;
2159  break;
2160  }
2161  }
2162 
2163  scaledAspectRatio = scaledHeight / scaledSize;
2164 
2165  return scaledAspectRatio;
2166 }
2167 
2168 void QgsSvgMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledSize, QPointF &offset, double &angle ) const
2169 {
2170  //offset
2171  double offsetX = 0;
2172  double offsetY = 0;
2173  markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
2174  offset = QPointF( offsetX, offsetY );
2175 
2176  angle = mAngle + mLineAngle;
2178  {
2179  context.setOriginalValueVariable( mAngle );
2181  }
2182 
2184  if ( hasDataDefinedRotation )
2185  {
2186  // For non-point markers, "dataDefinedRotation" means following the
2187  // shape (shape-data defined). For them, "field-data defined" does
2188  // not work at all. TODO: if "field-data defined" ever gets implemented
2189  // we'll need a way to distinguish here between the two, possibly
2190  // using another flag in renderHints()
2191  const QgsFeature *f = context.feature();
2192  if ( f )
2193  {
2194  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
2195  {
2196  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2197  angle += m2p.mapRotation();
2198  }
2199  }
2200  }
2201 
2202  if ( angle )
2203  offset = _rotatedOffset( offset, angle );
2204 }
2205 
2206 
2208 {
2209  QgsStringMap map;
2210  map[QStringLiteral( "name" )] = mPath;
2211  map[QStringLiteral( "size" )] = QString::number( mSize );
2212  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2213  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2214  map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2215  map[QStringLiteral( "angle" )] = QString::number( mAngle );
2216  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2217  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2218  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2219  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2220  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
2221  map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
2222  map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
2223  map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2224  map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2225  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
2226  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
2227  return map;
2228 }
2229 
2231 {
2233  m->setFixedAspectRatio( mFixedAspectRatio );
2234  m->setColor( mColor );
2235  m->setStrokeColor( mStrokeColor );
2236  m->setStrokeWidth( mStrokeWidth );
2237  m->setStrokeWidthUnit( mStrokeWidthUnit );
2238  m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
2239  m->setOffset( mOffset );
2240  m->setOffsetUnit( mOffsetUnit );
2242  m->setSizeUnit( mSizeUnit );
2247  copyPaintEffect( m );
2248  return m;
2249 }
2250 
2252 {
2254  mStrokeWidthUnit = unit;
2255 }
2256 
2258 {
2260  if ( unit != mStrokeWidthUnit )
2261  {
2263  }
2264  return unit;
2265 }
2266 
2268 {
2270  mStrokeWidthMapUnitScale = scale;
2271 }
2272 
2274 {
2275  if ( QgsMarkerSymbolLayer::mapUnitScale() == mStrokeWidthMapUnitScale )
2276  {
2277  return mStrokeWidthMapUnitScale;
2278  }
2279  return QgsMapUnitScale();
2280 }
2281 
2282 void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
2283 {
2284  // <Graphic>
2285  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2286  element.appendChild( graphicElem );
2287 
2288  // encode a parametric SVG reference
2290  double strokeWidth = QgsSymbolLayerUtils::rescaleUom( mStrokeWidth, mStrokeWidthUnit, props );
2291  QgsSymbolLayerUtils::parametricSvgToSld( doc, graphicElem, mPath, mColor, size, mStrokeColor, strokeWidth );
2292 
2293  // <Rotation>
2294  QString angleFunc;
2295  bool ok;
2296  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2297  if ( !ok )
2298  {
2299  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mAngle );
2300  }
2301  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2302  {
2303  angleFunc = QString::number( angle + mAngle );
2304  }
2305 
2306  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2307 
2308  // <Displacement>
2309  QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2310  QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
2311 }
2312 
2314 {
2315  QgsDebugMsg( QStringLiteral( "Entered." ) );
2316 
2317  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
2318  if ( graphicElem.isNull() )
2319  return nullptr;
2320 
2321  QString path, mimeType;
2322  QColor fillColor;
2323  double size;
2324 
2325  if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
2326  return nullptr;
2327 
2328  QString uom = element.attribute( QStringLiteral( "uom" ) );
2329  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
2330 
2331  if ( mimeType != QLatin1String( "image/svg+xml" ) )
2332  return nullptr;
2333 
2334  double angle = 0.0;
2335  QString angleFunc;
2336  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2337  {
2338  bool ok;
2339  double d = angleFunc.toDouble( &ok );
2340  if ( ok )
2341  angle = d;
2342  }
2343 
2344  QPointF offset;
2345  QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, offset );
2346 
2347  QgsSvgMarkerSymbolLayer *m = new QgsSvgMarkerSymbolLayer( path, size );
2348  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
2349  m->setFillColor( fillColor );
2350  //m->setStrokeColor( strokeColor );
2351  //m->setStrokeWidth( strokeWidth );
2352  m->setAngle( angle );
2353  m->setOffset( offset );
2354  return m;
2355 }
2356 
2357 bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
2358 {
2359  //size
2360  double size = mSize;
2361 
2362  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
2363 
2364  bool ok = true;
2365  if ( hasDataDefinedSize )
2366  {
2367  context.setOriginalValueVariable( mSize );
2369  }
2370 
2371  if ( hasDataDefinedSize && ok )
2372  {
2373  switch ( mScaleMethod )
2374  {
2375  case QgsSymbol::ScaleArea:
2376  size = std::sqrt( size );
2377  break;
2379  break;
2380  }
2381  }
2382 
2384  {
2385  size *= mmMapUnitScaleFactor;
2386  }
2387 
2388  //offset, angle
2389  QPointF offset = mOffset;
2390 
2392  {
2394  const QVariant val = mDataDefinedProperties.value( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext(), QString() );
2395  const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
2396  if ( ok )
2397  offset = res;
2398  }
2399  double offsetX = offset.x();
2400  double offsetY = offset.y();
2401 
2402  QPointF outputOffset( offsetX, offsetY );
2403 
2404  double angle = mAngle + mLineAngle;
2406  {
2407  context.setOriginalValueVariable( mAngle );
2409  }
2410 
2411  if ( angle )
2412  outputOffset = _rotatedOffset( outputOffset, angle );
2413 
2414  outputOffset *= e.mapUnitScaleFactor( e.symbologyScale(), mOffsetUnit, e.mapUnits(), context.renderContext().mapToPixel().mapUnitsPerPixel() );
2415 
2416  QString path = mPath;
2418  {
2419  context.setOriginalValueVariable( mPath );
2421  context.renderContext().pathResolver() );
2422  }
2423 
2424  double strokeWidth = mStrokeWidth;
2426  {
2427  context.setOriginalValueVariable( mStrokeWidth );
2429  }
2430  strokeWidth *= e.mapUnitScaleFactor( e.symbologyScale(), mStrokeWidthUnit, e.mapUnits(), context.renderContext().mapToPixel().mapUnitsPerPixel() );
2431 
2432  QColor fillColor = mColor;
2434  {
2437  }
2438 
2439  QColor strokeColor = mStrokeColor;
2441  {
2444  }
2445 
2446  const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, size, fillColor, strokeColor, strokeWidth,
2447  context.renderContext().scaleFactor(), mFixedAspectRatio );
2448 
2449  QSvgRenderer r( svgContent );
2450  if ( !r.isValid() )
2451  return false;
2452 
2453  QgsDxfPaintDevice pd( &e );
2454  pd.setDrawingSize( QSizeF( r.defaultSize() ) );
2455 
2456  QSizeF outSize( r.defaultSize() );
2457  outSize.scale( size, size, Qt::KeepAspectRatio );
2458 
2459  QPainter p;
2460  p.begin( &pd );
2461  if ( !qgsDoubleNear( angle, 0.0 ) )
2462  {
2463  p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
2464  p.rotate( angle );
2465  p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
2466  }
2467  pd.setShift( shift + QPointF( outputOffset.x(), -outputOffset.y() ) );
2468  pd.setOutputSize( QRectF( -outSize.width() / 2.0, -outSize.height() / 2.0, outSize.width(), outSize.height() ) );
2469  pd.setLayer( layerName );
2470  r.render( &p );
2471  p.end();
2472  return true;
2473 }
2474 
2476 {
2477  bool hasDataDefinedSize = false;
2478  double scaledSize = calculateSize( context, hasDataDefinedSize );
2479  scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
2480 
2481  //don't render symbols with size below one or above 10,000 pixels
2482  if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
2483  {
2484  return QRectF();
2485  }
2486 
2487  QPointF outputOffset;
2488  double angle = 0.0;
2489  calculateOffsetAndRotation( context, scaledSize, outputOffset, angle );
2490 
2491  QString path = mPath;
2493  {
2494  context.setOriginalValueVariable( mPath );
2496  context.renderContext().pathResolver() );
2497  }
2498 
2499  double strokeWidth = mStrokeWidth;
2501  {
2502  context.setOriginalValueVariable( mStrokeWidth );
2504  }
2505  strokeWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
2506 
2507  //need to get colors to take advantage of cached SVGs
2508  QColor fillColor = mColor;
2510  {
2513  }
2514 
2515  QColor strokeColor = mStrokeColor;
2517  {
2520  }
2521 
2522  QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledSize, fillColor, strokeColor, strokeWidth,
2523  context.renderContext().scaleFactor(), mFixedAspectRatio );
2524 
2525  double scaledHeight = svgViewbox.isValid() ? scaledSize * svgViewbox.height() / svgViewbox.width() : scaledSize;
2526 
2527  QMatrix transform;
2528 
2529  // move to the desired position
2530  transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
2531 
2532  if ( !qgsDoubleNear( angle, 0.0 ) )
2533  transform.rotate( angle );
2534 
2535  //antialiasing
2536  strokeWidth += 1.0 / 2.0;
2537 
2538  QRectF symbolBounds = transform.mapRect( QRectF( -scaledSize / 2.0,
2539  -scaledHeight / 2.0,
2540  scaledSize,
2541  scaledHeight ) );
2542 
2543  //extend bounds by pen width / 2.0
2544  symbolBounds.adjust( -strokeWidth / 2.0, -strokeWidth / 2.0,
2545  strokeWidth / 2.0, strokeWidth / 2.0 );
2546 
2547  return symbolBounds;
2548 
2549 }
2550 
2552 
2554  : mPath( path )
2555 {
2556  mSize = size;
2557  mAngle = angle;
2558  mOffset = QPointF( 0, 0 );
2561 }
2562 
2563 
2565 {
2566  QString path;
2568  double angle = DEFAULT_RASTERMARKER_ANGLE;
2570 
2571  if ( props.contains( QStringLiteral( "imageFile" ) ) )
2572  path = props[QStringLiteral( "imageFile" )];
2573  if ( props.contains( QStringLiteral( "size" ) ) )
2574  size = props[QStringLiteral( "size" )].toDouble();
2575  if ( props.contains( QStringLiteral( "angle" ) ) )
2576  angle = props[QStringLiteral( "angle" )].toDouble();
2577  if ( props.contains( QStringLiteral( "scale_method" ) ) )
2578  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )] );
2579 
2580  QgsRasterMarkerSymbolLayer *m = new QgsRasterMarkerSymbolLayer( path, size, angle, scaleMethod );
2581 
2582  if ( props.contains( QStringLiteral( "alpha" ) ) )
2583  {
2584  m->setOpacity( props[QStringLiteral( "alpha" )].toDouble() );
2585  }
2586 
2587  if ( props.contains( QStringLiteral( "size_unit" ) ) )
2588  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
2589  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
2590  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
2591  if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
2592  m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
2593 
2594  if ( props.contains( QStringLiteral( "offset" ) ) )
2595  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
2596  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
2597  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
2598  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2599  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
2600 
2601  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2602  {
2603  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2604  }
2605  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2606  {
2607  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2608  }
2609 
2610  m->restoreOldDataDefinedProperties( props );
2612 
2613  return m;
2614 }
2615 
2617 {
2618  QgsStringMap::iterator it = properties.find( QStringLiteral( "name" ) );
2619  if ( it != properties.end() )
2620  {
2621  if ( saving )
2622  it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value(), pathResolver );
2623  else
2624  it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value(), pathResolver );
2625  }
2626 }
2627 
2628 void QgsRasterMarkerSymbolLayer::setPath( const QString &path )
2629 {
2630  mPath = path;
2632 }
2633 
2635 {
2636  bool aPreservedAspectRatio = preservedAspectRatio();
2637  if ( aPreservedAspectRatio && !par )
2638  {
2640  }
2641  else if ( !aPreservedAspectRatio && par )
2642  {
2643  mFixedAspectRatio = 0.0;
2644  }
2645  return preservedAspectRatio();
2646 }
2647 
2649 {
2650  if ( mDefaultAspectRatio == 0.0 )
2651  {
2653  mDefaultAspectRatio = ( !size.isNull() && size.isValid() && size.width() > 0 ) ? static_cast< double >( size.height() ) / static_cast< double >( size.width() ) : 0.0;
2654  }
2655  return mDefaultAspectRatio;
2656 }
2657 
2659 {
2660  return QStringLiteral( "RasterMarker" );
2661 }
2662 
2664 {
2665  QPainter *p = context.renderContext().painter();
2666  if ( !p )
2667  return;
2668 
2669  bool hasDataDefinedSize = false;
2670  double scaledSize = calculateSize( context, hasDataDefinedSize );
2671  double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
2672  bool hasDataDefinedAspectRatio = false;
2673  double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
2674  double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
2675 
2676  //don't render symbols with size below one or above 10,000 pixels
2677  if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2678  {
2679  return;
2680  }
2681 
2682  QString path = mPath;
2684  {
2685  context.setOriginalValueVariable( mPath );
2687  if ( preservedAspectRatio() && path != mPath )
2688  {
2689  QSize size = QgsApplication::imageCache()->originalSize( path );
2690  if ( !size.isNull() && size.isValid() && size.width() > 0 )
2691  {
2692  height = width * ( static_cast< double >( size.height() ) / static_cast< double >( size.width() ) );
2693  }
2694  }
2695  }
2696 
2697  if ( path.isEmpty() )
2698  return;
2699 
2700  p->save();
2701 
2702  QPointF outputOffset;
2703  double angle = 0.0;
2704  calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
2705 
2706  p->translate( point + outputOffset );
2707 
2708  bool rotated = !qgsDoubleNear( angle, 0 );
2709  if ( rotated )
2710  p->rotate( angle );
2711 
2712  double opacity = mOpacity;
2714  {
2717  }
2718  opacity *= context.opacity();
2719 
2720  bool cached;
2721  QImage img = QgsApplication::imageCache()->pathAsImage( path, QSize( width, preservedAspectRatio() ? 0 : width * aspectRatio ), preservedAspectRatio(), opacity, cached );
2722  if ( !img.isNull() )
2723  {
2724  if ( context.selected() )
2726 
2727  p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2728  }
2729 
2730  p->restore();
2731 }
2732 
2733 double QgsRasterMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2734 {
2735  double scaledSize = mSize;
2737 
2738  bool ok = true;
2739  if ( hasDataDefinedSize )
2740  {
2741  context.setOriginalValueVariable( mSize );
2743  }
2744  else
2745  {
2747  if ( hasDataDefinedSize )
2748  {
2749  context.setOriginalValueVariable( mSize );
2751  }
2752  }
2753 
2754  if ( hasDataDefinedSize && ok )
2755  {
2756  switch ( mScaleMethod )
2757  {
2758  case QgsSymbol::ScaleArea:
2759  scaledSize = std::sqrt( scaledSize );
2760  break;
2762  break;
2763  }
2764  }
2765 
2766  return scaledSize;
2767 }
2768 
2769 double QgsRasterMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2770 {
2772  if ( !hasDataDefinedAspectRatio )
2773  return mFixedAspectRatio;
2774 
2776  return 0.0;
2777 
2778  double scaledAspectRatio = mDefaultAspectRatio;
2779  if ( mFixedAspectRatio > 0.0 )
2780  scaledAspectRatio = mFixedAspectRatio;
2781 
2782  double defaultHeight = mSize * scaledAspectRatio;
2783  scaledAspectRatio = defaultHeight / scaledSize;
2784 
2785  bool ok = true;
2786  double scaledHeight = scaledSize * scaledAspectRatio;
2788  {
2789  context.setOriginalValueVariable( defaultHeight );
2790  scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyHeight, context.renderContext().expressionContext(), defaultHeight, &ok );
2791  }
2792 
2793  if ( hasDataDefinedAspectRatio && ok )
2794  {
2795  switch ( mScaleMethod )
2796  {
2797  case QgsSymbol::ScaleArea:
2798  scaledHeight = sqrt( scaledHeight );
2799  break;
2801  break;
2802  }
2803  }
2804 
2805  scaledAspectRatio = scaledHeight / scaledSize;
2806 
2807  return scaledAspectRatio;
2808 }
2809 
2810 void QgsRasterMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
2811 {
2812  //offset
2813  double offsetX = 0;
2814  double offsetY = 0;
2815  markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
2816  offset = QPointF( offsetX, offsetY );
2817 
2818  angle = mAngle + mLineAngle;
2820  {
2821  context.setOriginalValueVariable( mAngle );
2823  }
2824 
2826  if ( hasDataDefinedRotation )
2827  {
2828  const QgsFeature *f = context.feature();
2829  if ( f )
2830  {
2831  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
2832  {
2833  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2834  angle += m2p.mapRotation();
2835  }
2836  }
2837  }
2838 
2839  if ( angle )
2840  offset = _rotatedOffset( offset, angle );
2841 }
2842 
2843 
2845 {
2846  QgsStringMap map;
2847  map[QStringLiteral( "imageFile" )] = mPath;
2848  map[QStringLiteral( "size" )] = QString::number( mSize );
2849  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2850  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2851  map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2852  map[QStringLiteral( "angle" )] = QString::number( mAngle );
2853  map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
2854  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2855  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2856  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2857  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2858  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
2859  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
2860  return map;
2861 }
2862 
2864 {
2867  m->setOpacity( mOpacity );
2868  m->setOffset( mOffset );
2869  m->setOffsetUnit( mOffsetUnit );
2871  m->setSizeUnit( mSizeUnit );
2876  copyPaintEffect( m );
2877  return m;
2878 }
2879 
2881 {
2883 }
2884 
2886 {
2888 }
2889 
2891 {
2892  bool hasDataDefinedSize = false;
2893  double scaledSize = calculateSize( context, hasDataDefinedSize );
2894  double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
2895  bool hasDataDefinedAspectRatio = false;
2896  double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
2897  double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
2898 
2899  //don't render symbols with size below one or above 10,000 pixels
2900  if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
2901  {
2902  return QRectF();
2903  }
2904 
2905  QPointF outputOffset;
2906  double angle = 0.0;
2907  calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
2908 
2909  QMatrix transform;
2910 
2911  // move to the desired position
2912  transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
2913 
2914  if ( !qgsDoubleNear( angle, 0.0 ) )
2915  transform.rotate( angle );
2916 
2917  QRectF symbolBounds = transform.mapRect( QRectF( -width / 2.0,
2918  -height / 2.0,
2919  width,
2920  height ) );
2921 
2922  return symbolBounds;
2923 }
2924 
2926 
2927 QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QString chr, double pointSize, const QColor &color, double angle )
2928 {
2929  mFontFamily = fontFamily;
2930  mString = chr;
2931  mColor = color;
2932  mAngle = angle;
2933  mSize = pointSize;
2934  mOrigSize = pointSize;
2936  mOffset = QPointF( 0, 0 );
2938  mStrokeColor = DEFAULT_FONTMARKER_BORDERCOLOR;
2939  mStrokeWidth = 0.0;
2940  mStrokeWidthUnit = QgsUnitTypes::RenderMillimeters;
2941  mPenJoinStyle = DEFAULT_FONTMARKER_JOINSTYLE;
2942 }
2943 
2945 {
2946  delete mFontMetrics;
2947 }
2948 
2950 {
2951  QString fontFamily = DEFAULT_FONTMARKER_FONT;
2952  QString string = DEFAULT_FONTMARKER_CHR;
2953  double pointSize = DEFAULT_FONTMARKER_SIZE;
2955  double angle = DEFAULT_FONTMARKER_ANGLE;
2956 
2957  if ( props.contains( QStringLiteral( "font" ) ) )
2958  fontFamily = props[QStringLiteral( "font" )];
2959  if ( props.contains( QStringLiteral( "chr" ) ) && props[QStringLiteral( "chr" )].length() > 0 )
2960  string = props[QStringLiteral( "chr" )];
2961  if ( props.contains( QStringLiteral( "size" ) ) )
2962  pointSize = props[QStringLiteral( "size" )].toDouble();
2963  if ( props.contains( QStringLiteral( "color" ) ) )
2964  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] );
2965  if ( props.contains( QStringLiteral( "angle" ) ) )
2966  angle = props[QStringLiteral( "angle" )].toDouble();
2967 
2968  QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( fontFamily, string, pointSize, color, angle );
2969 
2970  if ( props.contains( QStringLiteral( "outline_color" ) ) )
2971  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )] ) );
2972  if ( props.contains( QStringLiteral( "outline_width" ) ) )
2973  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
2974  if ( props.contains( QStringLiteral( "offset" ) ) )
2975  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
2976  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
2977  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
2978  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2979  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
2980  if ( props.contains( QStringLiteral( "size_unit" ) ) )
2981  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
2982  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
2983  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
2984  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
2985  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )] ) );
2986  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
2987  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )] ) );
2988  if ( props.contains( QStringLiteral( "joinstyle" ) ) )
2989  m->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )] ) );
2990  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2991  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2992  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2993  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2994 
2995  m->restoreOldDataDefinedProperties( props );
2996 
2997  return m;
2998 }
2999 
3001 {
3002  return QStringLiteral( "FontMarker" );
3003 }
3004 
3006 {
3007  QColor brushColor = mColor;
3008  QColor penColor = mStrokeColor;
3009 
3010  brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
3011  penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
3012 
3013  mBrush = QBrush( brushColor );
3014  mPen = QPen( penColor );
3015  mPen.setJoinStyle( mPenJoinStyle );
3016  mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
3017 
3018  mFont = QFont( mFontFamily );
3019  const double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3020  mNonZeroFontSize = !qgsDoubleNear( sizePixels, 0.0 );
3021  // if a non zero, but small pixel size results, round up to 2 pixels so that a "dot" is at least visible
3022  // (if we set a <=1 pixel size here Qt will reset the font to a default size, leading to much too large symbols)
3023  mFont.setPixelSize( std::max( 2, static_cast< int >( std::round( sizePixels ) ) ) );
3024  delete mFontMetrics;
3025  mFontMetrics = new QFontMetrics( mFont );
3026  mChrWidth = mFontMetrics->width( mString );
3027  mChrOffset = QPointF( mChrWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3028  mOrigSize = mSize; // save in case the size would be data defined
3029 
3030  // use caching only when not using a data defined character
3032  if ( mUseCachedPath )
3033  {
3034  QPointF chrOffset = mChrOffset;
3035  double chrWidth;
3036  QString charToRender = characterToRender( context, chrOffset, chrWidth );
3037  mCachedPath = QPainterPath();
3038  mCachedPath.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3039  }
3040 }
3041 
3043 {
3044  Q_UNUSED( context )
3045 }
3046 
3047 QString QgsFontMarkerSymbolLayer::characterToRender( QgsSymbolRenderContext &context, QPointF &charOffset, double &charWidth )
3048 {
3049  charOffset = mChrOffset;
3050  QString stringToRender = mString;
3052  {
3053  context.setOriginalValueVariable( mString );
3055  if ( stringToRender != mString )
3056  {
3057  charWidth = mFontMetrics->width( stringToRender );
3058  charOffset = QPointF( charWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3059  }
3060  }
3061  return stringToRender;
3062 }
3063 
3064 void QgsFontMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
3065  double scaledSize,
3066  bool &hasDataDefinedRotation,
3067  QPointF &offset,
3068  double &angle ) const
3069 {
3070  //offset
3071  double offsetX = 0;
3072  double offsetY = 0;
3073  markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
3074  offset = QPointF( offsetX, offsetY );
3075 
3076  //angle
3077  bool ok = true;
3078  angle = mAngle + mLineAngle;
3079  bool usingDataDefinedRotation = false;
3081  {
3082  context.setOriginalValueVariable( angle );
3084  usingDataDefinedRotation = ok;
3085  }
3086 
3087  hasDataDefinedRotation = context.renderHints() & QgsSymbol::DynamicRotation || usingDataDefinedRotation;
3088  if ( hasDataDefinedRotation )
3089  {
3090  // For non-point markers, "dataDefinedRotation" means following the
3091  // shape (shape-data defined). For them, "field-data defined" does
3092  // not work at all. TODO: if "field-data defined" ever gets implemented
3093  // we'll need a way to distinguish here between the two, possibly
3094  // using another flag in renderHints()
3095  const QgsFeature *f = context.feature();
3096  if ( f )
3097  {
3098  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
3099  {
3100  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3101  angle += m2p.mapRotation();
3102  }
3103  }
3104  }
3105 
3106  if ( angle )
3107  offset = _rotatedOffset( offset, angle );
3108 }
3109 
3110 double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context )
3111 {
3112  double scaledSize = mSize;
3113  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
3114 
3115  bool ok = true;
3116  if ( hasDataDefinedSize )
3117  {
3118  context.setOriginalValueVariable( mSize );
3120  }
3121 
3122  if ( hasDataDefinedSize && ok )
3123  {
3124  switch ( mScaleMethod )
3125  {
3126  case QgsSymbol::ScaleArea:
3127  scaledSize = std::sqrt( scaledSize );
3128  break;
3130  break;
3131  }
3132  }
3133  return scaledSize;
3134 }
3135 
3137 {
3138  QPainter *p = context.renderContext().painter();
3139  if ( !p || !mNonZeroFontSize )
3140  return;
3141 
3142  QTransform transform;
3143 
3144  bool ok;
3145  QColor brushColor = mColor;
3147  {
3150  }
3151  brushColor = context.selected() ? context.renderContext().selectionColor() : brushColor;
3152  brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
3153  mBrush.setColor( brushColor );
3154 
3155  QColor penColor = mStrokeColor;
3157  {
3160  }
3161  penColor.setAlphaF( penColor.alphaF() * context.opacity() );
3162 
3163  double penWidth = context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3165  {
3166  context.setOriginalValueVariable( mStrokeWidth );
3167  double strokeWidth = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyStrokeWidth, context.renderContext().expressionContext(), mStrokeWidth, &ok );
3168  if ( ok )
3169  {
3170  penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3171  }
3172  }
3173 
3175  {
3178  if ( ok )
3179  {
3180  mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
3181  }
3182  }
3183 
3184  p->save();
3185  p->setBrush( mBrush );
3186  if ( !qgsDoubleNear( penWidth, 0.0 ) )
3187  {
3188  mPen.setColor( penColor );
3189  mPen.setWidthF( penWidth );
3190  p->setPen( mPen );
3191  }
3192  else
3193  {
3194  p->setPen( Qt::NoPen );
3195  }
3196 
3197  QPointF chrOffset = mChrOffset;
3198  double chrWidth;
3199  QString charToRender = characterToRender( context, chrOffset, chrWidth );
3200 
3201  double sizeToRender = calculateSize( context );
3202 
3203  bool hasDataDefinedRotation = false;
3204  QPointF offset;
3205  double angle = 0;
3206  calculateOffsetAndRotation( context, sizeToRender, hasDataDefinedRotation, offset, angle );
3207 
3208  p->translate( point.x() + offset.x(), point.y() + offset.y() );
3209 
3210  if ( !qgsDoubleNear( angle, 0.0 ) )
3211  transform.rotate( angle );
3212 
3213  if ( !qgsDoubleNear( sizeToRender, mOrigSize ) )
3214  {
3215  double s = sizeToRender / mOrigSize;
3216  transform.scale( s, s );
3217  }
3218 
3219  if ( mUseCachedPath )
3220  {
3221  p->drawPath( transform.map( mCachedPath ) );
3222  }
3223  else
3224  {
3225  QPainterPath path;
3226  path.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3227  p->drawPath( transform.map( path ) );
3228  }
3229 
3230  p->restore();
3231 }
3232 
3234 {
3235  QgsStringMap props;
3236  props[QStringLiteral( "font" )] = mFontFamily;
3237  props[QStringLiteral( "chr" )] = mString;
3238  props[QStringLiteral( "size" )] = QString::number( mSize );
3239  props[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3240  props[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3241  props[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
3242  props[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
3243  props[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
3244  props[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
3245  props[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
3246  props[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
3247  props[QStringLiteral( "angle" )] = QString::number( mAngle );
3248  props[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3249  props[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3250  props[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3251  props[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3252  props[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3253  return props;
3254 }
3255 
3257 {
3258  QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( mFontFamily, mString, mSize, mColor, mAngle );
3259  m->setStrokeColor( mStrokeColor );
3260  m->setStrokeWidth( mStrokeWidth );
3261  m->setStrokeWidthUnit( mStrokeWidthUnit );
3262  m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
3263  m->setPenJoinStyle( mPenJoinStyle );
3264  m->setOffset( mOffset );
3265  m->setOffsetUnit( mOffsetUnit );
3267  m->setSizeUnit( mSizeUnit );
3272  copyPaintEffect( m );
3273  return m;
3274 }
3275 
3276 void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
3277 {
3278  // <Graphic>
3279  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
3280  element.appendChild( graphicElem );
3281 
3282  QString fontPath = QStringLiteral( "ttf://%1" ).arg( mFontFamily );
3283  int markIndex = !mString.isEmpty() ? mString.at( 0 ).unicode() : 0;
3285  QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, QStringLiteral( "ttf" ), &markIndex, mColor, size );
3286 
3287  // <Rotation>
3288  QString angleFunc;
3289  bool ok;
3290  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
3291  if ( !ok )
3292  {
3293  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mAngle );
3294  }
3295  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
3296  {
3297  angleFunc = QString::number( angle + mAngle );
3298  }
3299  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
3300 
3301  // <Displacement>
3302  QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
3303  QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
3304 }
3305 
3307 {
3308  QPointF chrOffset = mChrOffset;
3309  double chrWidth = mChrWidth;
3310  //calculate width of rendered character
3311  ( void )characterToRender( context, chrOffset, chrWidth );
3312 
3313  if ( !mFontMetrics )
3314  mFontMetrics = new QFontMetrics( mFont );
3315 
3316  double scaledSize = calculateSize( context );
3317  if ( !qgsDoubleNear( scaledSize, mOrigSize ) )
3318  {
3319  chrWidth *= scaledSize / mOrigSize;
3320  }
3321 
3322  bool hasDataDefinedRotation = false;
3323  QPointF offset;
3324  double angle = 0;
3325  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
3326  scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3327 
3328  QMatrix transform;
3329 
3330  // move to the desired position
3331  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
3332 
3333  if ( !qgsDoubleNear( angle, 0.0 ) )
3334  transform.rotate( angle );
3335 
3336  QRectF symbolBounds = transform.mapRect( QRectF( -chrWidth / 2.0,
3337  -scaledSize / 2.0,
3338  chrWidth,
3339  scaledSize ) );
3340  return symbolBounds;
3341 }
3342 
3344 {
3345  QgsDebugMsg( QStringLiteral( "Entered." ) );
3346 
3347  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
3348  if ( graphicElem.isNull() )
3349  return nullptr;
3350 
3351  QString name, format;
3352  QColor color;
3353  double size;
3354  int chr;
3355 
3356  if ( !QgsSymbolLayerUtils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
3357  return nullptr;
3358 
3359  if ( !name.startsWith( QLatin1String( "ttf://" ) ) || format != QLatin1String( "ttf" ) )
3360  return nullptr;
3361 
3362  QString fontFamily = name.mid( 6 );
3363 
3364  double angle = 0.0;
3365  QString angleFunc;
3366  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
3367  {
3368  bool ok;
3369  double d = angleFunc.toDouble( &ok );
3370  if ( ok )
3371  angle = d;
3372  }
3373 
3374  QPointF offset;
3375  QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, offset );
3376 
3377  QString uom = element.attribute( QStringLiteral( "uom" ) );
3378  offset.setX( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.x() ) );
3379  offset.setY( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.y() ) );
3380  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
3381 
3382  QgsMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( fontFamily, QChar( chr ), size, color );
3383  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
3384  m->setAngle( angle );
3385  m->setOffset( offset );
3386  return m;
3387 }
3388 
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)
QPicture svgAsPicture(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool forceVectorOutput=false, double fixedAspectRatio=0)
Gets SVG as QPicture&.
#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:773
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.
QImage svgAsImage(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool &fitsInCache, double fixedAspectRatio=0)
Gets SVG as QImage.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsFontMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
Right facing arrow head (unfilled, lines only)
static QgsFillSymbol * createSimple(const QgsStringMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties. ...
Definition: qgssymbol.cpp:1226
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:265
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:121
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
static QString encodeShape(QgsSimpleMarkerSymbolLayerBase::Shape shape)
Encodes a shape to its string representation.
QgsMapUnitScale mapUnitScale() const override
QMap< QString, QString > QgsStringMap
Definition: qgis.h:597
Rotation of symbol may be changed during rendering and symbol should not be cached.
Definition: qgssymbol.h:104
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:1167
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:710
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.
QSize originalSize(const QString &path) const
Returns the original size (in pixels) of the image at the specified path.
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:667
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:723
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.
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.
void containsParams(const QString &path, bool &hasFillParam, QColor &defaultFillColor, bool &hasStrokeParam, QColor &defaultStrokeColor, bool &hasStrokeWidthParam, double &defaultStrokeWidth) const
Tests if an svg file contains parameters for fill, stroke color, stroke width.
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.
QImage pathAsImage(const QString &path, const QSize size, const bool keepAspectRatio, const double opacity, bool &fitsInCache)
Returns the specified path rendered as an image.
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.
Qt::PenJoinStyle mPenJoinStyle
Stroke pen join style.
const QgsFeature * feature() const
Returns the current feature being rendered.
Definition: qgssymbol.h:748
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...
QByteArray svgContent(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0)
Gets SVG content.
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:125
QgsGeometry geometry
Definition: qgsfeature.h:67
VerticalAnchorPoint mVerticalAnchorPoint
Vertical anchor point.
void stopRender(QgsSymbolRenderContext &context) override
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.
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)
QSizeF svgViewboxSize(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0)
Calculates the viewbox size of a (possibly cached) SVG file.
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:113
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:735
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...