QGIS API Documentation  3.4.3-Madeira (2f64a3c)
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 "qgsrendercontext.h"
23 #include "qgslogger.h"
24 #include "qgssvgcache.h"
25 #include "qgsunittypes.h"
26 
27 #include <QPainter>
28 #include <QSvgRenderer>
29 #include <QFileInfo>
30 #include <QDir>
31 #include <QDomDocument>
32 #include <QDomElement>
33 
34 #include <cmath>
35 
36 Q_GUI_EXPORT extern int qt_defaultDpiX();
37 Q_GUI_EXPORT extern int qt_defaultDpiY();
38 
39 static void _fixQPictureDPI( QPainter *p )
40 {
41  // QPicture makes an assumption that we drawing to it with system DPI.
42  // Then when being drawn, it scales the painter. The following call
43  // negates the effect. There is no way of setting QPicture's DPI.
44  // See QTBUG-20361
45  p->scale( static_cast< double >( qt_defaultDpiX() ) / p->device()->logicalDpiX(),
46  static_cast< double >( qt_defaultDpiY() ) / p->device()->logicalDpiY() );
47 }
48 
49 
51 
52 
53 //
54 // QgsSimpleMarkerSymbolLayerBase
55 //
56 
57 QList<QgsSimpleMarkerSymbolLayerBase::Shape> QgsSimpleMarkerSymbolLayerBase::availableShapes()
58 {
59  QList< Shape > shapes;
60  shapes << Square
61  << Diamond
62  << Pentagon
63  << Hexagon
64  << Triangle
66  << Star
67  << Arrow
68  << Circle
69  << Cross
70  << CrossFill
71  << Cross2
72  << Line
73  << ArrowHead
75  << SemiCircle
76  << ThirdCircle
77  << QuarterCircle
78  << QuarterSquare
79  << HalfSquare
83  return shapes;
84 }
85 
87  : mShape( shape )
88 {
89  mSize = size;
90  mAngle = angle;
91  mOffset = QPointF( 0, 0 );
95 }
96 
98 {
99  switch ( shape )
100  {
101  case Square:
102  case Diamond:
103  case Pentagon:
104  case Hexagon:
105  case Triangle:
106  case EquilateralTriangle:
107  case Star:
108  case Arrow:
109  case Circle:
110  case CrossFill:
111  case ArrowHeadFilled:
112  case SemiCircle:
113  case ThirdCircle:
114  case QuarterCircle:
115  case QuarterSquare:
116  case HalfSquare:
117  case DiagonalHalfSquare:
118  case RightHalfTriangle:
119  case LeftHalfTriangle:
120  return true;
121 
122  case Cross:
123  case Cross2:
124  case Line:
125  case ArrowHead:
126  return false;
127  }
128  return true;
129 }
130 
132 {
133  bool hasDataDefinedRotation = context.renderHints() & QgsSymbol::DynamicRotation
135  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
136 
137  // use either QPolygonF or QPainterPath for drawing
138  if ( !prepareMarkerShape( mShape ) ) // drawing as a polygon
139  {
140  prepareMarkerPath( mShape ); // drawing as a painter path
141  }
142 
143  QTransform transform;
144 
145  // scale the shape (if the size is not going to be modified)
146  if ( !hasDataDefinedSize )
147  {
148  double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
149  double half = scaledSize / 2.0;
150  transform.scale( half, half );
151  }
152 
153  // rotate if the rotation is not going to be changed during the rendering
154  if ( !hasDataDefinedRotation && !qgsDoubleNear( mAngle, 0.0 ) )
155  {
156  transform.rotate( mAngle );
157  }
158 
159  if ( !mPolygon.isEmpty() )
160  mPolygon = transform.map( mPolygon );
161  else
162  mPath = transform.map( mPath );
163 
165 }
166 
168 {
169  Q_UNUSED( context );
170 }
171 
173 {
174  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
175  //of the rendered point!
176 
177  QPainter *p = context.renderContext().painter();
178  if ( !p )
179  {
180  return;
181  }
182 
183  bool hasDataDefinedSize = false;
184  double scaledSize = calculateSize( context, hasDataDefinedSize );
185 
186  bool hasDataDefinedRotation = false;
187  QPointF offset;
188  double angle = 0;
189  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
190 
191  //data defined shape?
192  bool createdNewPath = false;
193  bool ok = true;
194  Shape symbol = mShape;
196  {
197  context.setOriginalValueVariable( encodeShape( symbol ) );
199  if ( exprVal.isValid() )
200  {
201  Shape decoded = decodeShape( exprVal.toString(), &ok );
202  if ( ok )
203  {
204  symbol = decoded;
205 
206  if ( !prepareMarkerShape( symbol ) ) // drawing as a polygon
207  {
208  prepareMarkerPath( symbol ); // drawing as a painter path
209  }
210  createdNewPath = true;
211  }
212  }
213  else
214  {
215  symbol = mShape;
216  }
217  }
218 
219  QTransform transform;
220 
221  // move to the desired position
222  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
223 
224  // resize if necessary
225  if ( hasDataDefinedSize || createdNewPath )
226  {
227  double s = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
228  double half = s / 2.0;
229  transform.scale( half, half );
230  }
231 
232  if ( !qgsDoubleNear( angle, 0.0 ) && ( hasDataDefinedRotation || createdNewPath ) )
233  transform.rotate( angle );
234 
235  //need to pass: symbol, polygon, path
236 
237  QPolygonF polygon;
238  QPainterPath path;
239  if ( !mPolygon.isEmpty() )
240  {
241  polygon = transform.map( mPolygon );
242  }
243  else
244  {
245  path = transform.map( mPath );
246  }
247  draw( context, symbol, polygon, path );
248 }
249 
251 {
252  bool hasDataDefinedSize = false;
253  double scaledSize = calculateSize( context, hasDataDefinedSize );
254 
255  bool hasDataDefinedRotation = false;
256  QPointF offset;
257  double angle = 0;
258  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
259 
260  scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
261 
262  QTransform transform;
263 
264  // move to the desired position
265  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
266 
267  if ( !qgsDoubleNear( angle, 0.0 ) )
268  transform.rotate( angle );
269 
270  return transform.mapRect( QRectF( -scaledSize / 2.0,
271  -scaledSize / 2.0,
272  scaledSize,
273  scaledSize ) );
274 }
275 
277 {
278  if ( ok )
279  *ok = true;
280  QString cleaned = name.toLower().trimmed();
281 
282  if ( cleaned == QLatin1String( "square" ) || cleaned == QLatin1String( "rectangle" ) )
283  return Square;
284  else if ( cleaned == QLatin1String( "diamond" ) )
285  return Diamond;
286  else if ( cleaned == QLatin1String( "pentagon" ) )
287  return Pentagon;
288  else if ( cleaned == QLatin1String( "hexagon" ) )
289  return Hexagon;
290  else if ( cleaned == QLatin1String( "triangle" ) )
291  return Triangle;
292  else if ( cleaned == QLatin1String( "equilateral_triangle" ) )
293  return EquilateralTriangle;
294  else if ( cleaned == QLatin1String( "star" ) || cleaned == QLatin1String( "regular_star" ) )
295  return Star;
296  else if ( cleaned == QLatin1String( "arrow" ) )
297  return Arrow;
298  else if ( cleaned == QLatin1String( "circle" ) )
299  return Circle;
300  else if ( cleaned == QLatin1String( "cross" ) )
301  return Cross;
302  else if ( cleaned == QLatin1String( "cross_fill" ) )
303  return CrossFill;
304  else if ( cleaned == QLatin1String( "cross2" ) || cleaned == QLatin1String( "x" ) )
305  return Cross2;
306  else if ( cleaned == QLatin1String( "line" ) )
307  return Line;
308  else if ( cleaned == QLatin1String( "arrowhead" ) )
309  return ArrowHead;
310  else if ( cleaned == QLatin1String( "filled_arrowhead" ) )
311  return ArrowHeadFilled;
312  else if ( cleaned == QLatin1String( "semi_circle" ) )
313  return SemiCircle;
314  else if ( cleaned == QLatin1String( "third_circle" ) )
315  return ThirdCircle;
316  else if ( cleaned == QLatin1String( "quarter_circle" ) )
317  return QuarterCircle;
318  else if ( cleaned == QLatin1String( "quarter_square" ) )
319  return QuarterSquare;
320  else if ( cleaned == QLatin1String( "half_square" ) )
321  return HalfSquare;
322  else if ( cleaned == QLatin1String( "diagonal_half_square" ) )
323  return DiagonalHalfSquare;
324  else if ( cleaned == QLatin1String( "right_half_triangle" ) )
325  return RightHalfTriangle;
326  else if ( cleaned == QLatin1String( "left_half_triangle" ) )
327  return LeftHalfTriangle;
328 
329  if ( ok )
330  *ok = false;
331  return Circle;
332 }
333 
335 {
336  switch ( shape )
337  {
338  case Square:
339  return QStringLiteral( "square" );
340  case QuarterSquare:
341  return QStringLiteral( "quarter_square" );
342  case HalfSquare:
343  return QStringLiteral( "half_square" );
344  case DiagonalHalfSquare:
345  return QStringLiteral( "diagonal_half_square" );
346  case Diamond:
347  return QStringLiteral( "diamond" );
348  case Pentagon:
349  return QStringLiteral( "pentagon" );
350  case Hexagon:
351  return QStringLiteral( "hexagon" );
352  case Triangle:
353  return QStringLiteral( "triangle" );
354  case EquilateralTriangle:
355  return QStringLiteral( "equilateral_triangle" );
356  case LeftHalfTriangle:
357  return QStringLiteral( "left_half_triangle" );
358  case RightHalfTriangle:
359  return QStringLiteral( "right_half_triangle" );
360  case Star:
361  return QStringLiteral( "star" );
362  case Arrow:
363  return QStringLiteral( "arrow" );
364  case ArrowHeadFilled:
365  return QStringLiteral( "filled_arrowhead" );
366  case CrossFill:
367  return QStringLiteral( "cross_fill" );
368  case Circle:
369  return QStringLiteral( "circle" );
370  case Cross:
371  return QStringLiteral( "cross" );
372  case Cross2:
373  return QStringLiteral( "cross2" );
374  case Line:
375  return QStringLiteral( "line" );
376  case ArrowHead:
377  return QStringLiteral( "arrowhead" );
378  case SemiCircle:
379  return QStringLiteral( "semi_circle" );
380  case ThirdCircle:
381  return QStringLiteral( "third_circle" );
382  case QuarterCircle:
383  return QStringLiteral( "quarter_circle" );
384  }
385  return QString();
386 }
387 
389 {
390  return shapeToPolygon( shape, mPolygon );
391 }
392 
394 {
395  polygon.clear();
396 
397  switch ( shape )
398  {
399  case Square:
400  polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 1, 1 ) ) );
401  return true;
402 
403  case QuarterSquare:
404  polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 0 ) ) );
405  return true;
406 
407  case HalfSquare:
408  polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 1 ) ) );
409  return true;
410 
411  case DiagonalHalfSquare:
412  polygon << QPointF( -1, -1 ) << QPointF( 1, 1 ) << QPointF( -1, 1 ) << QPointF( -1, -1 );
413  return true;
414 
415  case Diamond:
416  polygon << QPointF( -1, 0 ) << QPointF( 0, 1 )
417  << QPointF( 1, 0 ) << QPointF( 0, -1 ) << QPointF( -1, 0 );
418  return true;
419 
420  case Pentagon:
421  /* angular-representation of hardcoded values used
422  polygon << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288.0 ) ) )
423  << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) )
424  << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) )
425  << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) )
426  << QPointF( 0, -1 ); */
427  polygon << QPointF( -0.9511, -0.3090 )
428  << QPointF( -0.5878, 0.8090 )
429  << QPointF( 0.5878, 0.8090 )
430  << QPointF( 0.9511, -0.3090 )
431  << QPointF( 0, -1 )
432  << QPointF( -0.9511, -0.3090 );
433  return true;
434 
435  case Hexagon:
436  /* angular-representation of hardcoded values used
437  polygon << QPointF( std::sin( DEG2RAD( 300.0 ) ), - std::cos( DEG2RAD( 300.0 ) ) )
438  << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
439  << QPointF( std::sin( DEG2RAD( 180.0 ) ), - std::cos( DEG2RAD( 180.0 ) ) )
440  << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
441  << QPointF( std::sin( DEG2RAD( 60.0 ) ), - std::cos( DEG2RAD( 60.0 ) ) )
442  << QPointF( 0, -1 ); */
443  polygon << QPointF( -0.8660, -0.5 )
444  << QPointF( -0.8660, 0.5 )
445  << QPointF( 0, 1 )
446  << QPointF( 0.8660, 0.5 )
447  << QPointF( 0.8660, -0.5 )
448  << QPointF( 0, -1 )
449  << QPointF( -0.8660, -0.5 );
450  return true;
451 
452  case Triangle:
453  polygon << QPointF( -1, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
454  return true;
455 
456  case EquilateralTriangle:
457  /* angular-representation of hardcoded values used
458  polygon << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
459  << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
460  << QPointF( 0, -1 ); */
461  polygon << QPointF( -0.8660, 0.5 )
462  << QPointF( 0.8660, 0.5 )
463  << QPointF( 0, -1 )
464  << QPointF( -0.8660, 0.5 );
465  return true;
466 
467  case LeftHalfTriangle:
468  polygon << QPointF( 0, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( 0, 1 );
469  return true;
470 
471  case RightHalfTriangle:
472  polygon << QPointF( -1, 1 ) << QPointF( 0, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
473  return true;
474 
475  case Star:
476  {
477  double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
478 
479  polygon << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ) // 324
480  << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288 ) ) ) // 288
481  << QPointF( inner_r * std::sin( DEG2RAD( 252.0 ) ), - inner_r * std::cos( DEG2RAD( 252.0 ) ) ) // 252
482  << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) ) // 216
483  << QPointF( 0, inner_r ) // 180
484  << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) ) // 144
485  << QPointF( inner_r * std::sin( DEG2RAD( 108.0 ) ), - inner_r * std::cos( DEG2RAD( 108.0 ) ) ) // 108
486  << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) ) // 72
487  << QPointF( inner_r * std::sin( DEG2RAD( 36.0 ) ), - inner_r * std::cos( DEG2RAD( 36.0 ) ) ) // 36
488  << QPointF( 0, -1 )
489  << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ); // 324; // 0
490  return true;
491  }
492 
493  case Arrow:
494  polygon << QPointF( 0, -1 )
495  << QPointF( 0.5, -0.5 )
496  << QPointF( 0.25, -0.5 )
497  << QPointF( 0.25, 1 )
498  << QPointF( -0.25, 1 )
499  << QPointF( -0.25, -0.5 )
500  << QPointF( -0.5, -0.5 )
501  << QPointF( 0, -1 );
502  return true;
503 
504  case ArrowHeadFilled:
505  polygon << QPointF( 0, 0 ) << QPointF( -1, 1 ) << QPointF( -1, -1 ) << QPointF( 0, 0 );
506  return true;
507 
508  case CrossFill:
509  polygon << QPointF( -1, -0.2 )
510  << QPointF( -1, -0.2 )
511  << QPointF( -1, 0.2 )
512  << QPointF( -0.2, 0.2 )
513  << QPointF( -0.2, 1 )
514  << QPointF( 0.2, 1 )
515  << QPointF( 0.2, 0.2 )
516  << QPointF( 1, 0.2 )
517  << QPointF( 1, -0.2 )
518  << QPointF( 0.2, -0.2 )
519  << QPointF( 0.2, -1 )
520  << QPointF( -0.2, -1 )
521  << QPointF( -0.2, -0.2 )
522  << QPointF( -1, -0.2 );
523  return true;
524 
525  case Circle:
526  case Cross:
527  case Cross2:
528  case Line:
529  case ArrowHead:
530  case SemiCircle:
531  case ThirdCircle:
532  case QuarterCircle:
533  return false;
534  }
535 
536  return false;
537 }
538 
540 {
541  mPath = QPainterPath();
542 
543  switch ( symbol )
544  {
545  case Circle:
546 
547  mPath.addEllipse( QRectF( -1, -1, 2, 2 ) ); // x,y,w,h
548  return true;
549 
550  case SemiCircle:
551  mPath.arcTo( -1, -1, 2, 2, 0, 180 );
552  mPath.lineTo( 0, 0 );
553  return true;
554 
555  case ThirdCircle:
556  mPath.arcTo( -1, -1, 2, 2, 90, 120 );
557  mPath.lineTo( 0, 0 );
558  return true;
559 
560  case QuarterCircle:
561  mPath.arcTo( -1, -1, 2, 2, 90, 90 );
562  mPath.lineTo( 0, 0 );
563  return true;
564 
565  case Cross:
566  mPath.moveTo( -1, 0 );
567  mPath.lineTo( 1, 0 ); // horizontal
568  mPath.moveTo( 0, -1 );
569  mPath.lineTo( 0, 1 ); // vertical
570  return true;
571 
572  case Cross2:
573  mPath.moveTo( -1, -1 );
574  mPath.lineTo( 1, 1 );
575  mPath.moveTo( 1, -1 );
576  mPath.lineTo( -1, 1 );
577  return true;
578 
579  case Line:
580  mPath.moveTo( 0, -1 );
581  mPath.lineTo( 0, 1 ); // vertical line
582  return true;
583 
584  case ArrowHead:
585  mPath.moveTo( -1, -1 );
586  mPath.lineTo( 0, 0 );
587  mPath.lineTo( -1, 1 );
588  return true;
589 
590  case Square:
591  case QuarterSquare:
592  case HalfSquare:
593  case DiagonalHalfSquare:
594  case Diamond:
595  case Pentagon:
596  case Hexagon:
597  case Triangle:
598  case EquilateralTriangle:
599  case LeftHalfTriangle:
600  case RightHalfTriangle:
601  case Star:
602  case Arrow:
603  case ArrowHeadFilled:
604  case CrossFill:
605  return false;
606  }
607  return false;
608 }
609 
610 double QgsSimpleMarkerSymbolLayerBase::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
611 {
612  double scaledSize = mSize;
613 
615  bool ok = true;
616  if ( hasDataDefinedSize )
617  {
618  context.setOriginalValueVariable( mSize );
620  mSize, &ok );
621  }
622 
623  if ( hasDataDefinedSize && ok )
624  {
625  switch ( mScaleMethod )
626  {
628  scaledSize = std::sqrt( scaledSize );
629  break;
631  break;
632  }
633  }
634 
635  return scaledSize;
636 }
637 
638 void QgsSimpleMarkerSymbolLayerBase::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle ) const
639 {
640  //offset
641  double offsetX = 0;
642  double offsetY = 0;
643  markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
644  offset = QPointF( offsetX, offsetY );
645 
646  //angle
647  bool ok = true;
648  angle = mAngle + mLineAngle;
649  bool usingDataDefinedRotation = false;
651  {
652  context.setOriginalValueVariable( angle );
654  usingDataDefinedRotation = ok;
655  }
656 
657  hasDataDefinedRotation = context.renderHints() & QgsSymbol::DynamicRotation || usingDataDefinedRotation;
658  if ( hasDataDefinedRotation )
659  {
660  // For non-point markers, "dataDefinedRotation" means following the
661  // shape (shape-data defined). For them, "field-data defined" does
662  // not work at all. TODO: if "field-data defined" ever gets implemented
663  // we'll need a way to distinguish here between the two, possibly
664  // using another flag in renderHints()
665  const QgsFeature *f = context.feature();
666  if ( f )
667  {
668  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
669  {
670  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
671  angle += m2p.mapRotation();
672  }
673  }
674  }
675 
676  if ( angle )
677  offset = _rotatedOffset( offset, angle );
678 }
679 
680 
681 //
682 // QgsSimpleMarkerSymbolLayer
683 //
684 
686  : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
687  , mStrokeColor( strokeColor )
688  , mPenJoinStyle( penJoinStyle )
689 {
690  mColor = color;
691 }
692 
694 {
695  Shape shape = Circle;
698  Qt::PenJoinStyle penJoinStyle = DEFAULT_SIMPLEMARKER_JOINSTYLE;
702 
703  if ( props.contains( QStringLiteral( "name" ) ) )
704  {
705  shape = decodeShape( props[QStringLiteral( "name" )] );
706  }
707  if ( props.contains( QStringLiteral( "color" ) ) )
708  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] );
709  if ( props.contains( QStringLiteral( "color_border" ) ) )
710  {
711  //pre 2.5 projects use "color_border"
712  strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color_border" )] );
713  }
714  else if ( props.contains( QStringLiteral( "outline_color" ) ) )
715  {
716  strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )] );
717  }
718  else if ( props.contains( QStringLiteral( "line_color" ) ) )
719  {
720  strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )] );
721  }
722  if ( props.contains( QStringLiteral( "joinstyle" ) ) )
723  {
724  penJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )] );
725  }
726  if ( props.contains( QStringLiteral( "size" ) ) )
727  size = props[QStringLiteral( "size" )].toDouble();
728  if ( props.contains( QStringLiteral( "angle" ) ) )
729  angle = props[QStringLiteral( "angle" )].toDouble();
730  if ( props.contains( QStringLiteral( "scale_method" ) ) )
731  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )] );
732 
733  QgsSimpleMarkerSymbolLayer *m = new QgsSimpleMarkerSymbolLayer( shape, size, angle, scaleMethod, color, strokeColor, penJoinStyle );
734  if ( props.contains( QStringLiteral( "offset" ) ) )
735  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
736  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
737  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
738  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
739  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
740  if ( props.contains( QStringLiteral( "size_unit" ) ) )
741  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
742  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
743  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
744 
745  if ( props.contains( QStringLiteral( "outline_style" ) ) )
746  {
747  m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "outline_style" )] ) );
748  }
749  else if ( props.contains( QStringLiteral( "line_style" ) ) )
750  {
751  m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "line_style" )] ) );
752  }
753  if ( props.contains( QStringLiteral( "outline_width" ) ) )
754  {
755  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
756  }
757  else if ( props.contains( QStringLiteral( "line_width" ) ) )
758  {
759  m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
760  }
761  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
762  {
763  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )] ) );
764  }
765  if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
766  {
767  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )] ) );
768  }
769  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
770  {
771  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )] ) );
772  }
773 
774  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
775  {
776  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
777  }
778  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
779  {
780  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
781  }
782 
784 
785  return m;
786 }
787 
788 
790 {
791  return QStringLiteral( "SimpleMarker" );
792 }
793 
795 {
797 
798  QColor brushColor = mColor;
799  QColor penColor = mStrokeColor;
800 
801  brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
802  penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
803 
804  mBrush = QBrush( brushColor );
805  mPen = QPen( penColor );
806  mPen.setStyle( mStrokeStyle );
807  mPen.setJoinStyle( mPenJoinStyle );
809 
810  QColor selBrushColor = context.renderContext().selectionColor();
811  QColor selPenColor = selBrushColor == mColor ? selBrushColor : mStrokeColor;
812  if ( context.opacity() < 1 )
813  {
814  selBrushColor.setAlphaF( context.opacity() );
815  selPenColor.setAlphaF( context.opacity() );
816  }
817  mSelBrush = QBrush( selBrushColor );
818  mSelPen = QPen( selPenColor );
819  mSelPen.setStyle( mStrokeStyle );
821 
823  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
824 
825  // use caching only when:
826  // - size, rotation, shape, color, stroke color is not data-defined
827  // - drawing to screen (not printer)
828  mUsingCache = !hasDataDefinedRotation && !hasDataDefinedSize && !context.renderContext().forceVectorOutput()
832 
833  if ( !shapeIsFilled( mShape ) )
834  {
835  // some markers can't be drawn as a polygon (circle, cross)
836  // For these set the selected stroke color to the selected color
837  mSelPen.setColor( selBrushColor );
838  }
839 
840 
841  if ( mUsingCache )
842  {
843  if ( !prepareCache( context ) )
844  {
845  mUsingCache = false;
846  }
847  }
848  else
849  {
850  mCache = QImage();
851  mSelCache = QImage();
852  }
853 }
854 
855 
857 {
858  double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
859 
860  // calculate necessary image size for the cache
861  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
862  int imageSize = ( static_cast< int >( scaledSize ) + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width
863  double center = imageSize / 2.0;
864 
865  if ( imageSize > MAXIMUM_CACHE_WIDTH )
866  {
867  return false;
868  }
869 
870  mCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
871  mCache.fill( 0 );
872 
873  bool needsBrush = shapeIsFilled( mShape );
874 
875  QPainter p;
876  p.begin( &mCache );
877  p.setRenderHint( QPainter::Antialiasing );
878  p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
879  p.setPen( mPen );
880  p.translate( QPointF( center, center ) );
881  drawMarker( &p, context );
882  p.end();
883 
884  // Construct the selected version of the Cache
885 
886  QColor selColor = context.renderContext().selectionColor();
887 
888  mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
889  mSelCache.fill( 0 );
890 
891  p.begin( &mSelCache );
892  p.setRenderHint( QPainter::Antialiasing );
893  p.setBrush( needsBrush ? mSelBrush : Qt::NoBrush );
894  p.setPen( mSelPen );
895  p.translate( QPointF( center, center ) );
896  drawMarker( &p, context );
897  p.end();
898 
899  // Check that the selected version is different. If not, then re-render,
900  // filling the background with the selection color and using the normal
901  // colors for the symbol .. could be ugly!
902 
903  if ( mSelCache == mCache )
904  {
905  p.begin( &mSelCache );
906  p.setRenderHint( QPainter::Antialiasing );
907  p.fillRect( 0, 0, imageSize, imageSize, selColor );
908  p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
909  p.setPen( mPen );
910  p.translate( QPointF( center, center ) );
911  drawMarker( &p, context );
912  p.end();
913  }
914 
915  return true;
916 }
917 
918 void QgsSimpleMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, QgsSimpleMarkerSymbolLayerBase::Shape shape, const QPolygonF &polygon, const QPainterPath &path )
919 {
920  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
921  //of the rendered point!
922 
923  QPainter *p = context.renderContext().painter();
924  if ( !p )
925  {
926  return;
927  }
928 
929  bool ok = true;
931  {
934  if ( ok )
935  mBrush.setColor( c );
936  }
938  {
941  if ( ok )
942  {
943  mPen.setColor( c );
944  mSelPen.setColor( c );
945  }
946  }
948  {
951  if ( ok )
952  {
953  mPen.setWidthF( context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
955  }
956  }
958  {
961  if ( ok )
962  {
963  mPen.setStyle( QgsSymbolLayerUtils::decodePenStyle( strokeStyle ) );
964  mSelPen.setStyle( QgsSymbolLayerUtils::decodePenStyle( strokeStyle ) );
965  }
966  }
968  {
971  if ( ok )
972  {
973  mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
974  mSelPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
975  }
976  }
977 
978  if ( shapeIsFilled( shape ) )
979  {
980  p->setBrush( context.selected() ? mSelBrush : mBrush );
981  }
982  else
983  {
984  p->setBrush( Qt::NoBrush );
985  }
986  p->setPen( context.selected() ? mSelPen : mPen );
987 
988  if ( !polygon.isEmpty() )
989  p->drawPolygon( polygon );
990  else
991  p->drawPath( path );
992 }
993 
995 {
996  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
997  //of the rendered point!
998 
999  QPainter *p = context.renderContext().painter();
1000  if ( !p )
1001  {
1002  return;
1003  }
1004 
1005  if ( mUsingCache )
1006  {
1007  QImage &img = context.selected() ? mSelCache : mCache;
1008  double s = img.width();
1009 
1010  bool hasDataDefinedSize = false;
1011  double scaledSize = calculateSize( context, hasDataDefinedSize );
1012 
1013  bool hasDataDefinedRotation = false;
1014  QPointF offset;
1015  double angle = 0;
1016  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
1017 
1018  p->drawImage( QRectF( point.x() - s / 2.0 + offset.x(),
1019  point.y() - s / 2.0 + offset.y(),
1020  s, s ), img );
1021  }
1022  else
1023  {
1025  }
1026 }
1027 
1029 {
1030  QgsStringMap map;
1031  map[QStringLiteral( "name" )] = encodeShape( mShape );
1032  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
1033  map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
1034  map[QStringLiteral( "size" )] = QString::number( mSize );
1035  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1036  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1037  map[QStringLiteral( "angle" )] = QString::number( mAngle );
1038  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1039  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1040  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1041  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1042  map[QStringLiteral( "outline_style" )] = QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle );
1043  map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
1044  map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
1045  map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
1046  map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
1047  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1048  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1049  return map;
1050 }
1051 
1053 {
1055  m->setOffset( mOffset );
1056  m->setSizeUnit( mSizeUnit );
1058  m->setOffsetUnit( mOffsetUnit );
1067  copyPaintEffect( m );
1068  return m;
1069 }
1070 
1071 void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
1072 {
1073  // <Graphic>
1074  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
1075  element.appendChild( graphicElem );
1076 
1079  QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, encodeShape( mShape ), mColor, mStrokeColor, mStrokeStyle, strokeWidth, size );
1080 
1081  // <Rotation>
1082  QString angleFunc;
1083  bool ok;
1084  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
1085  if ( !ok )
1086  {
1087  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mAngle );
1088  }
1089  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
1090  {
1091  angleFunc = QString::number( angle + mAngle );
1092  }
1093  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
1094 
1095  // <Displacement>
1097  QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
1098 }
1099 
1100 QString QgsSimpleMarkerSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
1101 {
1102  Q_UNUSED( mmScaleFactor );
1103  Q_UNUSED( mapUnitScaleFactor );
1104 #if 0
1105  QString ogrType = "3"; //default is circle
1106  if ( mName == "square" )
1107  {
1108  ogrType = "5";
1109  }
1110  else if ( mName == "triangle" )
1111  {
1112  ogrType = "7";
1113  }
1114  else if ( mName == "star" )
1115  {
1116  ogrType = "9";
1117  }
1118  else if ( mName == "circle" )
1119  {
1120  ogrType = "3";
1121  }
1122  else if ( mName == "cross" )
1123  {
1124  ogrType = "0";
1125  }
1126  else if ( mName == "x" || mName == "cross2" )
1127  {
1128  ogrType = "1";
1129  }
1130  else if ( mName == "line" )
1131  {
1132  ogrType = "10";
1133  }
1134 
1135  QString ogrString;
1136  ogrString.append( "SYMBOL(" );
1137  ogrString.append( "id:" );
1138  ogrString.append( '\"' );
1139  ogrString.append( "ogr-sym-" );
1140  ogrString.append( ogrType );
1141  ogrString.append( '\"' );
1142  ogrString.append( ",c:" );
1143  ogrString.append( mColor.name() );
1144  ogrString.append( ",o:" );
1145  ogrString.append( mStrokeColor.name() );
1146  ogrString.append( QString( ",s:%1mm" ).arg( mSize ) );
1147  ogrString.append( ')' );
1148  return ogrString;
1149 #endif //0
1150 
1151  QString ogrString;
1152  ogrString.append( "PEN(" );
1153  ogrString.append( "c:" );
1154  ogrString.append( mColor.name() );
1155  ogrString.append( ",w:" );
1156  ogrString.append( QString::number( mSize ) );
1157  ogrString.append( "mm" );
1158  ogrString.append( ")" );
1159  return ogrString;
1160 }
1161 
1163 {
1164  QgsDebugMsg( QStringLiteral( "Entered." ) );
1165 
1166  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
1167  if ( graphicElem.isNull() )
1168  return nullptr;
1169 
1170  QString name = QStringLiteral( "square" );
1171  QColor color, strokeColor;
1172  double strokeWidth, size;
1173  Qt::PenStyle strokeStyle;
1174 
1175  if ( !QgsSymbolLayerUtils::wellKnownMarkerFromSld( graphicElem, name, color, strokeColor, strokeStyle, strokeWidth, size ) )
1176  return nullptr;
1177 
1178  double angle = 0.0;
1179  QString angleFunc;
1180  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
1181  {
1182  bool ok;
1183  double d = angleFunc.toDouble( &ok );
1184  if ( ok )
1185  angle = d;
1186  }
1187 
1188  QPointF offset;
1189  QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, offset );
1190 
1191  Shape shape = decodeShape( name );
1192 
1193  QString uom = element.attribute( QStringLiteral( "uom" ) );
1194  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
1195  offset.setX( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.x() ) );
1196  offset.setY( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.y() ) );
1197 
1199  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
1200  m->setColor( color );
1201  m->setStrokeColor( strokeColor );
1202  m->setAngle( angle );
1203  m->setOffset( offset );
1204  m->setStrokeStyle( strokeStyle );
1205  m->setStrokeWidth( strokeWidth );
1206  return m;
1207 }
1208 
1210 {
1211  Q_UNUSED( context );
1212 
1213  if ( mPolygon.count() != 0 )
1214  {
1215  p->drawPolygon( mPolygon );
1216  }
1217  else
1218  {
1219  p->drawPath( mPath );
1220  }
1221 }
1222 
1223 bool QgsSimpleMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
1224 {
1225  Q_UNUSED( mmMapUnitScaleFactor );
1226 
1227  //data defined size?
1228  double size = mSize;
1229 
1230  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
1231 
1232  //data defined size
1233  bool ok = true;
1234  if ( hasDataDefinedSize )
1235  {
1237 
1238  if ( ok )
1239  {
1240  switch ( mScaleMethod )
1241  {
1242  case QgsSymbol::ScaleArea:
1243  size = std::sqrt( size );
1244  break;
1246  break;
1247  }
1248  }
1249 
1251  }
1253  {
1255  }
1256  double halfSize = size / 2.0;
1257 
1258  //strokeWidth
1259  double strokeWidth = mStrokeWidth;
1260 
1262  {
1265  }
1268  {
1270  }
1271 
1272  //color
1273  QColor pc = mPen.color();
1274  QColor bc = mBrush.color();
1276  {
1279  }
1281  {
1284  }
1285 
1286  //offset
1287  double offsetX = 0;
1288  double offsetY = 0;
1289  markerOffset( context, offsetX, offsetY );
1290  offsetX *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1291  offsetY *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1292 
1293 
1294  QPointF off( offsetX, offsetY );
1295 
1296  //angle
1297  double angle = mAngle + mLineAngle;
1299  {
1300  context.setOriginalValueVariable( mAngle );
1302  }
1303 
1304  Shape shape = mShape;
1306  {
1307  context.setOriginalValueVariable( encodeShape( shape ) );
1308  QString shapeName = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyName, context.renderContext().expressionContext(), QString(), &ok );
1309  if ( ok )
1310  {
1311  shape = decodeShape( shapeName, &ok );
1312  if ( !ok )
1313  shape = mShape;
1314  }
1315  }
1316 
1317  if ( angle )
1318  off = _rotatedOffset( off, angle );
1319 
1321 
1322  QTransform t;
1323  t.translate( shift.x() + off.x(), shift.y() - off.y() );
1324 
1325  if ( !qgsDoubleNear( angle, 0.0 ) )
1326  t.rotate( angle );
1327 
1328  QPolygonF polygon;
1329  if ( shapeToPolygon( shape, polygon ) )
1330  {
1331  t.scale( halfSize, -halfSize );
1332 
1333  polygon = t.map( polygon );
1334 
1335  QgsPointSequence p;
1336  p.reserve( polygon.size() );
1337  for ( int i = 0; i < polygon.size(); i++ )
1338  {
1339  p << QgsPoint( polygon[i] );
1340  }
1341 
1342  if ( mBrush.style() != Qt::NoBrush )
1343  e.writePolygon( QgsRingSequence() << p, layerName, QStringLiteral( "SOLID" ), bc );
1344  if ( mPen.style() != Qt::NoPen )
1345  e.writePolyline( p, layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1346  }
1347  else if ( shape == Circle )
1348  {
1349  shift += QPointF( off.x(), -off.y() );
1350  if ( mBrush.style() != Qt::NoBrush )
1351  e.writeFilledCircle( layerName, bc, QgsPoint( shift ), halfSize );
1352  if ( mPen.style() != Qt::NoPen )
1353  e.writeCircle( layerName, pc, QgsPoint( shift ), halfSize, QStringLiteral( "CONTINUOUS" ), strokeWidth );
1354  }
1355  else if ( shape == Line )
1356  {
1357  QPointF pt1 = t.map( QPointF( 0, -halfSize ) );
1358  QPointF pt2 = t.map( QPointF( 0, halfSize ) );
1359 
1360  if ( mPen.style() != Qt::NoPen )
1361  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1362  }
1363  else if ( shape == Cross )
1364  {
1365  if ( mPen.style() != Qt::NoPen )
1366  {
1367  QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
1368  QPointF pt2 = t.map( QPointF( halfSize, 0 ) );
1369  QPointF pt3 = t.map( QPointF( 0, -halfSize ) );
1370  QPointF pt4 = t.map( QPointF( 0, halfSize ) );
1371 
1372  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1373  e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1374  }
1375  }
1376  else if ( shape == Cross2 )
1377  {
1378  if ( mPen.style() != Qt::NoPen )
1379  {
1380  QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
1381  QPointF pt2 = t.map( QPointF( halfSize, halfSize ) );
1382  QPointF pt3 = t.map( QPointF( halfSize, -halfSize ) );
1383  QPointF pt4 = t.map( QPointF( -halfSize, halfSize ) );
1384 
1385  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1386  e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1387  }
1388  }
1389  else if ( shape == ArrowHead )
1390  {
1391  if ( mPen.style() != Qt::NoPen )
1392  {
1393  QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
1394  QPointF pt2 = t.map( QPointF( 0, 0 ) );
1395  QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
1396 
1397  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1398  e.writeLine( QgsPoint( pt3 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1399  }
1400  }
1401  else
1402  {
1403  QgsDebugMsg( QStringLiteral( "Unsupported dxf marker name %1" ).arg( encodeShape( shape ) ) );
1404  return false;
1405  }
1406 
1407  return true;
1408 }
1409 
1410 
1412 {
1414  mStrokeWidthUnit = unit;
1415 }
1416 
1418 {
1420  {
1421  return mStrokeWidthUnit;
1422  }
1424 }
1425 
1427 {
1429  mStrokeWidthMapUnitScale = scale;
1430 }
1431 
1433 {
1435  {
1436  return mStrokeWidthMapUnitScale;
1437  }
1438  return QgsMapUnitScale();
1439 }
1440 
1442 {
1443  QRectF symbolBounds = QgsSimpleMarkerSymbolLayerBase::bounds( point, context );
1444 
1445  // need to account for stroke width
1446  double penWidth = 0.0;
1447  bool ok = true;
1449  {
1452  if ( ok )
1453  {
1454  penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
1455  }
1456  }
1458  {
1461  if ( ok && strokeStyle == QLatin1String( "no" ) )
1462  {
1463  penWidth = 0.0;
1464  }
1465  }
1466  //antialiasing, add 1 pixel
1467  penWidth += 1;
1468 
1469  //extend bounds by pen width / 2.0
1470  symbolBounds.adjust( -penWidth / 2.0, -penWidth / 2.0,
1471  penWidth / 2.0, penWidth / 2.0 );
1472 
1473  return symbolBounds;
1474 }
1475 
1477 {
1478  if ( shapeIsFilled( mShape ) )
1479  {
1480  setFillColor( color );
1481  }
1482  else
1483  {
1484  setStrokeColor( color );
1485  }
1486 }
1487 
1489 {
1490  if ( shapeIsFilled( mShape ) )
1491  {
1492  return fillColor();
1493  }
1494  else
1495  {
1496  return strokeColor();
1497  }
1498 }
1499 
1500 
1501 
1502 
1503 //
1504 // QgsFilledMarkerSymbolLayer
1505 //
1506 
1508  : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
1509 {
1510  mFill.reset( static_cast<QgsFillSymbol *>( QgsFillSymbol::createSimple( QgsStringMap() ) ) );
1511 }
1512 
1514 {
1515  QString name = DEFAULT_SIMPLEMARKER_NAME;
1519 
1520  if ( props.contains( QStringLiteral( "name" ) ) )
1521  name = props[QStringLiteral( "name" )];
1522  if ( props.contains( QStringLiteral( "size" ) ) )
1523  size = props[QStringLiteral( "size" )].toDouble();
1524  if ( props.contains( QStringLiteral( "angle" ) ) )
1525  angle = props[QStringLiteral( "angle" )].toDouble();
1526  if ( props.contains( QStringLiteral( "scale_method" ) ) )
1527  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )] );
1528 
1529  QgsFilledMarkerSymbolLayer *m = new QgsFilledMarkerSymbolLayer( decodeShape( name ), size, angle, scaleMethod );
1530  if ( props.contains( QStringLiteral( "offset" ) ) )
1531  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
1532  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1533  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
1534  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1535  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
1536  if ( props.contains( QStringLiteral( "size_unit" ) ) )
1537  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
1538  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1539  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
1540  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1541  {
1542  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1543  }
1544  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1545  {
1546  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1547  }
1548 
1550 
1551  m->restoreOldDataDefinedProperties( props );
1552 
1553  return m;
1554 }
1555 
1557 {
1558  return QStringLiteral( "FilledMarker" );
1559 }
1560 
1562 {
1563  if ( mFill )
1564  {
1565  mFill->startRender( context.renderContext(), context.fields() );
1566  }
1567 
1569 }
1570 
1572 {
1573  if ( mFill )
1574  {
1575  mFill->stopRender( context.renderContext() );
1576  }
1577 }
1578 
1580 {
1581  QgsStringMap map;
1582  map[QStringLiteral( "name" )] = encodeShape( mShape );
1583  map[QStringLiteral( "size" )] = QString::number( mSize );
1584  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1585  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1586  map[QStringLiteral( "angle" )] = QString::number( mAngle );
1587  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1588  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1589  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1590  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1591  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1592  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1593 
1594  if ( mFill )
1595  {
1596  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mFill->color() );
1597  }
1598  return map;
1599 }
1600 
1602 {
1604  copyPaintEffect( m );
1606  m->setSubSymbol( mFill->clone() );
1607  return m;
1608 }
1609 
1611 {
1612  return mFill.get();
1613 }
1614 
1616 {
1617  if ( symbol && symbol->type() == QgsSymbol::Fill )
1618  {
1619  mFill.reset( static_cast<QgsFillSymbol *>( symbol ) );
1620  return true;
1621  }
1622  else
1623  {
1624  delete symbol;
1625  return false;
1626  }
1627 }
1628 
1630 {
1631  if ( mFill )
1632  {
1633  return QgsSymbolLayerUtils::estimateMaxSymbolBleed( mFill.get(), context );
1634  }
1635  return 0;
1636 }
1637 
1639 {
1640  QSet<QString> attr = QgsSimpleMarkerSymbolLayerBase::usedAttributes( context );
1641  if ( mFill )
1642  attr.unite( mFill->usedAttributes( context ) );
1643  return attr;
1644 }
1645 
1647 {
1648  mColor = c;
1649  if ( mFill )
1650  mFill->setColor( c );
1651 }
1652 
1654 {
1655  return mFill ? mFill->color() : mColor;
1656 }
1657 
1658 void QgsFilledMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, QgsSimpleMarkerSymbolLayerBase::Shape shape, const QPolygonF &polygon, const QPainterPath &path )
1659 {
1660  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1661  //of the rendered point!
1662 
1663  QPainter *p = context.renderContext().painter();
1664  if ( !p )
1665  {
1666  return;
1667  }
1668 
1669  if ( shapeIsFilled( shape ) )
1670  {
1671  p->setBrush( Qt::red );
1672  }
1673  else
1674  {
1675  p->setBrush( Qt::NoBrush );
1676  }
1677  p->setPen( Qt::black );
1678 
1679  if ( !polygon.isEmpty() )
1680  {
1681  mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
1682  }
1683  else
1684  {
1685  QPolygonF poly = path.toFillPolygon();
1686  mFill->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
1687  }
1688 
1689 
1690 }
1691 
1692 
1694 
1695 
1697 {
1698  mPath = path;
1699  mSize = size;
1700  mAngle = angle;
1701  mOffset = QPointF( 0, 0 );
1703  mStrokeWidth = 0.2;
1704  mStrokeWidthUnit = QgsUnitTypes::RenderMillimeters;
1705  mColor = QColor( 35, 35, 35 );
1706  mStrokeColor = QColor( 35, 35, 35 );
1707  updateDefaultAspectRatio();
1708 }
1709 
1710 
1712 {
1713  QString name;
1714  double size = DEFAULT_SVGMARKER_SIZE;
1715  double angle = DEFAULT_SVGMARKER_ANGLE;
1717 
1718  if ( props.contains( QStringLiteral( "name" ) ) )
1719  name = props[QStringLiteral( "name" )];
1720  if ( props.contains( QStringLiteral( "size" ) ) )
1721  size = props[QStringLiteral( "size" )].toDouble();
1722  if ( props.contains( QStringLiteral( "angle" ) ) )
1723  angle = props[QStringLiteral( "angle" )].toDouble();
1724  if ( props.contains( QStringLiteral( "scale_method" ) ) )
1725  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )] );
1726 
1727  QgsSvgMarkerSymbolLayer *m = new QgsSvgMarkerSymbolLayer( name, size, angle, scaleMethod );
1728 
1729  //we only check the svg default parameters if necessary, since it could be expensive
1730  if ( !props.contains( QStringLiteral( "fill" ) ) && !props.contains( QStringLiteral( "color" ) ) && !props.contains( QStringLiteral( "outline" ) ) &&
1731  !props.contains( QStringLiteral( "outline_color" ) ) && !props.contains( QStringLiteral( "outline-width" ) ) && !props.contains( QStringLiteral( "outline_width" ) ) )
1732  {
1733  QColor fillColor, strokeColor;
1734  double fillOpacity = 1.0;
1735  double strokeOpacity = 1.0;
1736  double strokeWidth;
1737  bool hasFillParam = false, hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
1738  bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
1739  QgsApplication::svgCache()->containsParams( name, hasFillParam, hasDefaultFillColor, fillColor,
1740  hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
1741  hasStrokeParam, hasDefaultStrokeColor, strokeColor,
1742  hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
1743  hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
1744  if ( hasDefaultFillColor )
1745  {
1746  m->setFillColor( fillColor );
1747  }
1748  if ( hasDefaultFillOpacity )
1749  {
1750  QColor c = m->fillColor();
1751  c.setAlphaF( fillOpacity );
1752  m->setFillColor( c );
1753  }
1754  if ( hasDefaultStrokeColor )
1755  {
1756  m->setStrokeColor( strokeColor );
1757  }
1758  if ( hasDefaultStrokeWidth )
1759  {
1760  m->setStrokeWidth( strokeWidth );
1761  }
1762  if ( hasDefaultStrokeOpacity )
1763  {
1764  QColor c = m->strokeColor();
1765  c.setAlphaF( strokeOpacity );
1766  m->setStrokeColor( c );
1767  }
1768  }
1769 
1770  if ( props.contains( QStringLiteral( "size_unit" ) ) )
1771  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
1772  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1773  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
1774  if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
1775  m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
1776  if ( props.contains( QStringLiteral( "offset" ) ) )
1777  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
1778  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1779  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
1780  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1781  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
1782  if ( props.contains( QStringLiteral( "fill" ) ) )
1783  {
1784  //pre 2.5 projects used "fill"
1785  m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "fill" )] ) );
1786  }
1787  else if ( props.contains( QStringLiteral( "color" ) ) )
1788  {
1789  m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] ) );
1790  }
1791  if ( props.contains( QStringLiteral( "outline" ) ) )
1792  {
1793  //pre 2.5 projects used "outline"
1794  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline" )] ) );
1795  }
1796  else if ( props.contains( QStringLiteral( "outline_color" ) ) )
1797  {
1798  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )] ) );
1799  }
1800  else if ( props.contains( QStringLiteral( "line_color" ) ) )
1801  {
1802  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )] ) );
1803  }
1804 
1805  if ( props.contains( QStringLiteral( "outline-width" ) ) )
1806  {
1807  //pre 2.5 projects used "outline-width"
1808  m->setStrokeWidth( props[QStringLiteral( "outline-width" )].toDouble() );
1809  }
1810  else if ( props.contains( QStringLiteral( "outline_width" ) ) )
1811  {
1812  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
1813  }
1814  else if ( props.contains( QStringLiteral( "line_width" ) ) )
1815  {
1816  m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
1817  }
1818 
1819  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
1820  {
1821  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )] ) );
1822  }
1823  else if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
1824  {
1825  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )] ) );
1826  }
1827  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
1828  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )] ) );
1829 
1830  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1831  {
1832  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1833  }
1834  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1835  {
1836  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1837  }
1838 
1839  m->restoreOldDataDefinedProperties( props );
1840 
1842 
1843  return m;
1844 }
1845 
1847 {
1848  QgsStringMap::iterator it = properties.find( QStringLiteral( "name" ) );
1849  if ( it != properties.end() )
1850  {
1851  if ( saving )
1852  it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value(), pathResolver );
1853  else
1854  it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value(), pathResolver );
1855  }
1856 }
1857 
1858 void QgsSvgMarkerSymbolLayer::setPath( const QString &path )
1859 {
1860  mPath = path;
1861  QColor defaultFillColor, defaultStrokeColor;
1862  double strokeWidth, fillOpacity, strokeOpacity;
1863  bool hasFillParam = false, hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
1864  bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
1865  QgsApplication::svgCache()->containsParams( path, hasFillParam, hasDefaultFillColor, defaultFillColor,
1866  hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
1867  hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
1868  hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
1869  hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
1870 
1871  double newFillOpacity = hasFillOpacityParam ? fillColor().alphaF() : 1.0;
1872  double newStrokeOpacity = hasStrokeOpacityParam ? strokeColor().alphaF() : 1.0;
1873 
1874  if ( hasDefaultFillColor )
1875  {
1876  defaultFillColor.setAlphaF( newFillOpacity );
1877  setFillColor( defaultFillColor );
1878  }
1879  if ( hasDefaultFillOpacity )
1880  {
1881  QColor c = fillColor();
1882  c.setAlphaF( fillOpacity );
1883  setFillColor( c );
1884  }
1885  if ( hasDefaultStrokeColor )
1886  {
1887  defaultStrokeColor.setAlphaF( newStrokeOpacity );
1888  setStrokeColor( defaultStrokeColor );
1889  }
1890  if ( hasDefaultStrokeWidth )
1891  {
1892  setStrokeWidth( strokeWidth );
1893  }
1894  if ( hasDefaultStrokeOpacity )
1895  {
1896  QColor c = strokeColor();
1897  c.setAlphaF( strokeOpacity );
1898  setStrokeColor( c );
1899  }
1900 
1901  updateDefaultAspectRatio();
1902 }
1903 
1905 {
1906  if ( mDefaultAspectRatio == 0.0 )
1907  {
1908  //size
1909  double size = mSize;
1910  //assume 88 dpi as standard value
1911  double widthScaleFactor = 3.465;
1912  QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( mPath, size, mColor, mStrokeColor, mStrokeWidth, widthScaleFactor );
1913  // set default aspect ratio
1914  mDefaultAspectRatio = svgViewbox.isValid() ? svgViewbox.height() / svgViewbox.width() : 0.0;
1915  }
1916  return mDefaultAspectRatio;
1917 }
1918 
1920 {
1921  bool aPreservedAspectRatio = preservedAspectRatio();
1922  if ( aPreservedAspectRatio && !par )
1923  {
1924  mFixedAspectRatio = mDefaultAspectRatio;
1925  }
1926  else if ( !aPreservedAspectRatio && par )
1927  {
1928  mFixedAspectRatio = 0.0;
1929  }
1930  return preservedAspectRatio();
1931 }
1932 
1933 
1935 {
1936  return QStringLiteral( "SvgMarker" );
1937 }
1938 
1940 {
1941  QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
1942  Q_UNUSED( context );
1943 }
1944 
1946 {
1947  Q_UNUSED( context );
1948 }
1949 
1951 {
1952  QPainter *p = context.renderContext().painter();
1953  if ( !p )
1954  return;
1955 
1956  bool hasDataDefinedSize = false;
1957  double scaledSize = calculateSize( context, hasDataDefinedSize );
1958  double size = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
1959 
1960  //don't render symbols with size below one or above 10,000 pixels
1961  if ( static_cast< int >( size ) < 1 || 10000.0 < size )
1962  {
1963  return;
1964  }
1965 
1966  p->save();
1967 
1968  bool hasDataDefinedAspectRatio = false;
1969  double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
1970 
1971  QPointF outputOffset;
1972  double angle = 0.0;
1973  calculateOffsetAndRotation( context, scaledSize, outputOffset, angle );
1974 
1975  p->translate( point + outputOffset );
1976 
1977  bool rotated = !qgsDoubleNear( angle, 0 );
1978  if ( rotated )
1979  p->rotate( angle );
1980 
1981  QString path = mPath;
1983  {
1984  context.setOriginalValueVariable( mPath );
1986  context.renderContext().pathResolver() );
1987  }
1988 
1989  double strokeWidth = mStrokeWidth;
1991  {
1992  context.setOriginalValueVariable( mStrokeWidth );
1994  }
1995  strokeWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
1996 
1997  QColor fillColor = mColor;
1999  {
2002  }
2003 
2004  QColor strokeColor = mStrokeColor;
2006  {
2009  }
2010 
2011  bool fitsInCache = true;
2012  bool usePict = true;
2013  double hwRatio = 1.0;
2014  if ( !context.renderContext().forceVectorOutput() && !rotated )
2015  {
2016  QImage img = QgsApplication::svgCache()->svgAsImage( path, size, fillColor, strokeColor, strokeWidth,
2017  context.renderContext().scaleFactor(), fitsInCache, aspectRatio );
2018  if ( fitsInCache && img.width() > 1 )
2019  {
2020  usePict = false;
2021  //consider transparency
2022  if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2023  {
2024  QImage transparentImage = img.copy();
2025  QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2026  p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
2027  hwRatio = static_cast< double >( transparentImage.height() ) / static_cast< double >( transparentImage.width() );
2028  }
2029  else
2030  {
2031  p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2032  hwRatio = static_cast< double >( img.height() ) / static_cast< double >( img.width() );
2033  }
2034  }
2035  }
2036 
2037  if ( usePict || !fitsInCache )
2038  {
2039  p->setOpacity( context.opacity() );
2040  QPicture pct = QgsApplication::svgCache()->svgAsPicture( path, size, fillColor, strokeColor, strokeWidth,
2041  context.renderContext().scaleFactor(), context.renderContext().forceVectorOutput(), aspectRatio );
2042  if ( pct.width() > 1 )
2043  {
2044  p->save();
2045  _fixQPictureDPI( p );
2046  p->drawPicture( 0, 0, pct );
2047  p->restore();
2048  hwRatio = static_cast< double >( pct.height() ) / static_cast< double >( pct.width() );
2049  }
2050  }
2051 
2052  if ( context.selected() )
2053  {
2054  QPen pen( context.renderContext().selectionColor() );
2055  double penWidth = context.renderContext().convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters );
2056  if ( penWidth > size / 20 )
2057  {
2058  // keep the pen width from covering symbol
2059  penWidth = size / 20;
2060  }
2061  double penOffset = penWidth / 2;
2062  pen.setWidth( penWidth );
2063  p->setPen( pen );
2064  p->setBrush( Qt::NoBrush );
2065  double wSize = size + penOffset;
2066  double hSize = size * hwRatio + penOffset;
2067  p->drawRect( QRectF( -wSize / 2.0, -hSize / 2.0, wSize, hSize ) );
2068  }
2069 
2070  p->restore();
2071 
2073  {
2074  // workaround issue with nested QPictures forgetting antialiasing flag - see https://issues.qgis.org/issues/14960
2075  p->setRenderHint( QPainter::Antialiasing );
2076  }
2077 
2078 }
2079 
2080 double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2081 {
2082  double scaledSize = mSize;
2084 
2085  bool ok = true;
2086  if ( hasDataDefinedSize )
2087  {
2088  context.setOriginalValueVariable( mSize );
2090  }
2091  else
2092  {
2094  if ( hasDataDefinedSize )
2095  {
2096  context.setOriginalValueVariable( mSize );
2098  }
2099  }
2100 
2101  if ( hasDataDefinedSize && ok )
2102  {
2103  switch ( mScaleMethod )
2104  {
2105  case QgsSymbol::ScaleArea:
2106  scaledSize = std::sqrt( scaledSize );
2107  break;
2109  break;
2110  }
2111  }
2112 
2113  return scaledSize;
2114 }
2115 
2116 double QgsSvgMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2117 {
2119  if ( !hasDataDefinedAspectRatio )
2120  return mFixedAspectRatio;
2121 
2122  if ( !mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyHeight ) && mFixedAspectRatio <= 0.0 )
2123  return 0.0;
2124 
2125  double scaledAspectRatio = mDefaultAspectRatio;
2126  if ( mFixedAspectRatio > 0.0 )
2127  scaledAspectRatio = mFixedAspectRatio;
2128 
2129  double defaultHeight = mSize * scaledAspectRatio;
2130  scaledAspectRatio = defaultHeight / scaledSize;
2131 
2132  bool ok = true;
2133  double scaledHeight = scaledSize * scaledAspectRatio;
2135  {
2136  context.setOriginalValueVariable( defaultHeight );
2137  scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyHeight, context.renderContext().expressionContext(), defaultHeight, &ok );
2138  }
2139 
2140  if ( hasDataDefinedAspectRatio && ok )
2141  {
2142  switch ( mScaleMethod )
2143  {
2144  case QgsSymbol::ScaleArea:
2145  scaledHeight = sqrt( scaledHeight );
2146  break;
2148  break;
2149  }
2150  }
2151 
2152  scaledAspectRatio = scaledHeight / scaledSize;
2153 
2154  return scaledAspectRatio;
2155 }
2156 
2157 void QgsSvgMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledSize, QPointF &offset, double &angle ) const
2158 {
2159  //offset
2160  double offsetX = 0;
2161  double offsetY = 0;
2162  markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
2163  offset = QPointF( offsetX, offsetY );
2164 
2165  angle = mAngle + mLineAngle;
2167  {
2168  context.setOriginalValueVariable( mAngle );
2170  }
2171 
2173  if ( hasDataDefinedRotation )
2174  {
2175  // For non-point markers, "dataDefinedRotation" means following the
2176  // shape (shape-data defined). For them, "field-data defined" does
2177  // not work at all. TODO: if "field-data defined" ever gets implemented
2178  // we'll need a way to distinguish here between the two, possibly
2179  // using another flag in renderHints()
2180  const QgsFeature *f = context.feature();
2181  if ( f )
2182  {
2183  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
2184  {
2185  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2186  angle += m2p.mapRotation();
2187  }
2188  }
2189  }
2190 
2191  if ( angle )
2192  offset = _rotatedOffset( offset, angle );
2193 }
2194 
2195 
2197 {
2198  QgsStringMap map;
2199  map[QStringLiteral( "name" )] = mPath;
2200  map[QStringLiteral( "size" )] = QString::number( mSize );
2201  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2202  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2203  map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2204  map[QStringLiteral( "angle" )] = QString::number( mAngle );
2205  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2206  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2207  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2208  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2209  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
2210  map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
2211  map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
2212  map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2213  map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2214  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
2215  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
2216  return map;
2217 }
2218 
2220 {
2222  m->setFixedAspectRatio( mFixedAspectRatio );
2223  m->setColor( mColor );
2224  m->setStrokeColor( mStrokeColor );
2225  m->setStrokeWidth( mStrokeWidth );
2226  m->setStrokeWidthUnit( mStrokeWidthUnit );
2227  m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
2228  m->setOffset( mOffset );
2229  m->setOffsetUnit( mOffsetUnit );
2231  m->setSizeUnit( mSizeUnit );
2236  copyPaintEffect( m );
2237  return m;
2238 }
2239 
2241 {
2243  mStrokeWidthUnit = unit;
2244 }
2245 
2247 {
2249  if ( unit != mStrokeWidthUnit )
2250  {
2252  }
2253  return unit;
2254 }
2255 
2257 {
2259  mStrokeWidthMapUnitScale = scale;
2260 }
2261 
2263 {
2264  if ( QgsMarkerSymbolLayer::mapUnitScale() == mStrokeWidthMapUnitScale )
2265  {
2266  return mStrokeWidthMapUnitScale;
2267  }
2268  return QgsMapUnitScale();
2269 }
2270 
2271 void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
2272 {
2273  // <Graphic>
2274  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2275  element.appendChild( graphicElem );
2276 
2277  // encode a parametric SVG reference
2279  double strokeWidth = QgsSymbolLayerUtils::rescaleUom( mStrokeWidth, mStrokeWidthUnit, props );
2280  QgsSymbolLayerUtils::parametricSvgToSld( doc, graphicElem, mPath, mColor, size, mStrokeColor, strokeWidth );
2281 
2282  // <Rotation>
2283  QString angleFunc;
2284  bool ok;
2285  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2286  if ( !ok )
2287  {
2288  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mAngle );
2289  }
2290  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2291  {
2292  angleFunc = QString::number( angle + mAngle );
2293  }
2294 
2295  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2296 
2297  // <Displacement>
2298  QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2299  QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
2300 }
2301 
2303 {
2304  QgsDebugMsg( QStringLiteral( "Entered." ) );
2305 
2306  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
2307  if ( graphicElem.isNull() )
2308  return nullptr;
2309 
2310  QString path, mimeType;
2311  QColor fillColor;
2312  double size;
2313 
2314  if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
2315  return nullptr;
2316 
2317  QString uom = element.attribute( QStringLiteral( "uom" ) );
2318  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
2319 
2320  if ( mimeType != QLatin1String( "image/svg+xml" ) )
2321  return nullptr;
2322 
2323  double angle = 0.0;
2324  QString angleFunc;
2325  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2326  {
2327  bool ok;
2328  double d = angleFunc.toDouble( &ok );
2329  if ( ok )
2330  angle = d;
2331  }
2332 
2333  QPointF offset;
2334  QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, offset );
2335 
2336  QgsSvgMarkerSymbolLayer *m = new QgsSvgMarkerSymbolLayer( path, size );
2337  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
2338  m->setFillColor( fillColor );
2339  //m->setStrokeColor( strokeColor );
2340  //m->setStrokeWidth( strokeWidth );
2341  m->setAngle( angle );
2342  m->setOffset( offset );
2343  return m;
2344 }
2345 
2346 bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
2347 {
2348  Q_UNUSED( layerName );
2349  Q_UNUSED( shift ); //todo...
2350 
2351  //size
2352  double size = mSize;
2353 
2354  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
2355 
2356  bool ok = true;
2357  if ( hasDataDefinedSize )
2358  {
2359  context.setOriginalValueVariable( mSize );
2361  }
2362 
2363  if ( hasDataDefinedSize && ok )
2364  {
2365  switch ( mScaleMethod )
2366  {
2367  case QgsSymbol::ScaleArea:
2368  size = std::sqrt( size );
2369  break;
2371  break;
2372  }
2373  }
2374 
2376  {
2377  size *= mmMapUnitScaleFactor;
2378  }
2379 
2380  double halfSize = size / 2.0;
2381 
2382  //offset, angle
2383  QPointF offset = mOffset;
2384 
2386  {
2388  QString offsetString = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext(), QString(), &ok );
2389  if ( ok )
2390  offset = QgsSymbolLayerUtils::decodePoint( offsetString );
2391  }
2392  double offsetX = offset.x();
2393  double offsetY = offset.y();
2394 
2395  QPointF outputOffset( offsetX, offsetY );
2396 
2397  double angle = mAngle + mLineAngle;
2399  {
2400  context.setOriginalValueVariable( mAngle );
2402  }
2403  //angle = -angle; //rotation in Qt is counterclockwise
2404  if ( angle )
2405  outputOffset = _rotatedOffset( outputOffset, angle );
2406 
2407  outputOffset *= e.mapUnitScaleFactor( e.symbologyScale(), mOffsetUnit, e.mapUnits(), context.renderContext().mapToPixel().mapUnitsPerPixel() );
2408 
2409  QString path = mPath;
2411  {
2412  context.setOriginalValueVariable( mPath );
2414  context.renderContext().pathResolver() );
2415  }
2416 
2417  double strokeWidth = mStrokeWidth;
2419  {
2420  context.setOriginalValueVariable( mStrokeWidth );
2422  }
2423  strokeWidth *= e.mapUnitScaleFactor( e.symbologyScale(), mStrokeWidthUnit, e.mapUnits(), context.renderContext().mapToPixel().mapUnitsPerPixel() );
2424 
2425  QColor fillColor = mColor;
2427  {
2430  }
2431 
2432  QColor strokeColor = mStrokeColor;
2434  {
2437  }
2438 
2439  const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, size, fillColor, strokeColor, strokeWidth,
2440  context.renderContext().scaleFactor(), mFixedAspectRatio );
2441 
2442  //if current entry image is 0: cache image for entry
2443  // checks to see if image will fit into cache
2444  //update stats for memory usage
2445  QSvgRenderer r( svgContent );
2446  if ( !r.isValid() )
2447  {
2448  return false;
2449  }
2450 
2451  QgsDxfPaintDevice pd( &e );
2452  pd.setDrawingSize( QSizeF( r.defaultSize() ) );
2453 
2454  QPainter p;
2455  p.begin( &pd );
2456  if ( !qgsDoubleNear( angle, 0.0 ) )
2457  {
2458  p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
2459  p.rotate( angle );
2460  p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
2461  }
2462  pd.setShift( shift + QPointF( outputOffset.x(), -outputOffset.y() ) );
2463  pd.setOutputSize( QRectF( -halfSize, -halfSize, size, size ) );
2464  pd.setLayer( layerName );
2465  r.render( &p );
2466  p.end();
2467  return true;
2468 }
2469 
2471 {
2472  bool hasDataDefinedSize = false;
2473  double scaledSize = calculateSize( context, hasDataDefinedSize );
2474  scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
2475 
2476  //don't render symbols with size below one or above 10,000 pixels
2477  if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
2478  {
2479  return QRectF();
2480  }
2481 
2482  QPointF outputOffset;
2483  double angle = 0.0;
2484  calculateOffsetAndRotation( context, scaledSize, outputOffset, angle );
2485 
2486  QString path = mPath;
2488  {
2489  context.setOriginalValueVariable( mPath );
2491  context.renderContext().pathResolver() );
2492  }
2493 
2494  double strokeWidth = mStrokeWidth;
2496  {
2497  context.setOriginalValueVariable( mStrokeWidth );
2499  }
2500  strokeWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
2501 
2502  //need to get colors to take advantage of cached SVGs
2503  QColor fillColor = mColor;
2505  {
2508  }
2509 
2510  QColor strokeColor = mStrokeColor;
2512  {
2515  }
2516 
2517  QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledSize, fillColor, strokeColor, strokeWidth,
2518  context.renderContext().scaleFactor(), mFixedAspectRatio );
2519 
2520  double scaledHeight = svgViewbox.isValid() ? scaledSize * svgViewbox.height() / svgViewbox.width() : scaledSize;
2521 
2522  QMatrix transform;
2523 
2524  // move to the desired position
2525  transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
2526 
2527  if ( !qgsDoubleNear( angle, 0.0 ) )
2528  transform.rotate( angle );
2529 
2530  //antialiasing
2531  strokeWidth += 1.0 / 2.0;
2532 
2533  QRectF symbolBounds = transform.mapRect( QRectF( -scaledSize / 2.0,
2534  -scaledHeight / 2.0,
2535  scaledSize,
2536  scaledHeight ) );
2537 
2538  //extend bounds by pen width / 2.0
2539  symbolBounds.adjust( -strokeWidth / 2.0, -strokeWidth / 2.0,
2540  strokeWidth / 2.0, strokeWidth / 2.0 );
2541 
2542  return symbolBounds;
2543 
2544 }
2545 
2547 
2548 QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QChar chr, double pointSize, const QColor &color, double angle )
2549 {
2550  mFontFamily = fontFamily;
2551  mChr = chr;
2552  mColor = color;
2553  mAngle = angle;
2554  mSize = pointSize;
2555  mOrigSize = pointSize;
2557  mOffset = QPointF( 0, 0 );
2559  mStrokeColor = DEFAULT_FONTMARKER_BORDERCOLOR;
2560  mStrokeWidth = 0.0;
2561  mStrokeWidthUnit = QgsUnitTypes::RenderMillimeters;
2562  mPenJoinStyle = DEFAULT_FONTMARKER_JOINSTYLE;
2563 }
2564 
2566 {
2567  delete mFontMetrics;
2568 }
2569 
2571 {
2572  QString fontFamily = DEFAULT_FONTMARKER_FONT;
2573  QChar chr = DEFAULT_FONTMARKER_CHR;
2574  double pointSize = DEFAULT_FONTMARKER_SIZE;
2576  double angle = DEFAULT_FONTMARKER_ANGLE;
2577 
2578  if ( props.contains( QStringLiteral( "font" ) ) )
2579  fontFamily = props[QStringLiteral( "font" )];
2580  if ( props.contains( QStringLiteral( "chr" ) ) && props[QStringLiteral( "chr" )].length() > 0 )
2581  chr = props[QStringLiteral( "chr" )].at( 0 );
2582  if ( props.contains( QStringLiteral( "size" ) ) )
2583  pointSize = props[QStringLiteral( "size" )].toDouble();
2584  if ( props.contains( QStringLiteral( "color" ) ) )
2585  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] );
2586  if ( props.contains( QStringLiteral( "angle" ) ) )
2587  angle = props[QStringLiteral( "angle" )].toDouble();
2588 
2589  QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( fontFamily, chr, pointSize, color, angle );
2590 
2591  if ( props.contains( QStringLiteral( "outline_color" ) ) )
2592  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )] ) );
2593  if ( props.contains( QStringLiteral( "outline_width" ) ) )
2594  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
2595  if ( props.contains( QStringLiteral( "offset" ) ) )
2596  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
2597  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
2598  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
2599  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2600  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
2601  if ( props.contains( QStringLiteral( "size_unit" ) ) )
2602  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
2603  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
2604  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
2605  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
2606  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )] ) );
2607  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
2608  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )] ) );
2609  if ( props.contains( QStringLiteral( "joinstyle" ) ) )
2610  m->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )] ) );
2611  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2612  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2613  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2614  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2615 
2616  m->restoreOldDataDefinedProperties( props );
2617 
2618  return m;
2619 }
2620 
2622 {
2623  return QStringLiteral( "FontMarker" );
2624 }
2625 
2627 {
2628  QColor brushColor = mColor;
2629  QColor penColor = mStrokeColor;
2630 
2631  brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
2632  penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
2633 
2634  mBrush = QBrush( brushColor );
2635  mPen = QPen( penColor );
2636  mPen.setJoinStyle( mPenJoinStyle );
2637  mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
2638 
2639  mFont = QFont( mFontFamily );
2640  mFont.setPixelSize( context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale ) );
2641  delete mFontMetrics;
2642  mFontMetrics = new QFontMetrics( mFont );
2643  mChrWidth = mFontMetrics->width( mChr );
2644  mChrOffset = QPointF( mChrWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
2645  mOrigSize = mSize; // save in case the size would be data defined
2646 }
2647 
2649 {
2650  Q_UNUSED( context );
2651 }
2652 
2653 QString QgsFontMarkerSymbolLayer::characterToRender( QgsSymbolRenderContext &context, QPointF &charOffset, double &charWidth )
2654 {
2655  charOffset = mChrOffset;
2656  QString charToRender = mChr;
2658  {
2659  context.setOriginalValueVariable( mChr );
2661  if ( charToRender != mChr )
2662  {
2663  charWidth = mFontMetrics->width( charToRender );
2664  charOffset = QPointF( charWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
2665  }
2666  }
2667  return charToRender;
2668 }
2669 
2670 void QgsFontMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
2671  double scaledSize,
2672  bool &hasDataDefinedRotation,
2673  QPointF &offset,
2674  double &angle ) const
2675 {
2676  //offset
2677  double offsetX = 0;
2678  double offsetY = 0;
2679  markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
2680  offset = QPointF( offsetX, offsetY );
2681 
2682  //angle
2683  bool ok = true;
2684  angle = mAngle + mLineAngle;
2685  bool usingDataDefinedRotation = false;
2687  {
2688  context.setOriginalValueVariable( angle );
2690  usingDataDefinedRotation = ok;
2691  }
2692 
2693  hasDataDefinedRotation = context.renderHints() & QgsSymbol::DynamicRotation || usingDataDefinedRotation;
2694  if ( hasDataDefinedRotation )
2695  {
2696  // For non-point markers, "dataDefinedRotation" means following the
2697  // shape (shape-data defined). For them, "field-data defined" does
2698  // not work at all. TODO: if "field-data defined" ever gets implemented
2699  // we'll need a way to distinguish here between the two, possibly
2700  // using another flag in renderHints()
2701  const QgsFeature *f = context.feature();
2702  if ( f )
2703  {
2704  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
2705  {
2706  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2707  angle += m2p.mapRotation();
2708  }
2709  }
2710  }
2711 
2712  if ( angle )
2713  offset = _rotatedOffset( offset, angle );
2714 }
2715 
2716 double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context )
2717 {
2718  double scaledSize = mSize;
2719  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
2720 
2721  bool ok = true;
2722  if ( hasDataDefinedSize )
2723  {
2724  context.setOriginalValueVariable( mSize );
2726  }
2727 
2728  if ( hasDataDefinedSize && ok )
2729  {
2730  switch ( mScaleMethod )
2731  {
2732  case QgsSymbol::ScaleArea:
2733  scaledSize = std::sqrt( scaledSize );
2734  break;
2736  break;
2737  }
2738  }
2739  return scaledSize;
2740 }
2741 
2743 {
2744  QPainter *p = context.renderContext().painter();
2745  if ( !p )
2746  return;
2747 
2748  QTransform transform;
2749 
2750  bool ok;
2751  QColor brushColor = mColor;
2753  {
2756  }
2757  brushColor = context.selected() ? context.renderContext().selectionColor() : brushColor;
2758  brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
2759  mBrush.setColor( brushColor );
2760 
2761  QColor penColor = mStrokeColor;
2763  {
2766  }
2767  penColor.setAlphaF( penColor.alphaF() * context.opacity() );
2768 
2769  double penWidth = context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
2771  {
2772  context.setOriginalValueVariable( mStrokeWidth );
2773  double strokeWidth = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyStrokeWidth, context.renderContext().expressionContext(), mStrokeWidth, &ok );
2774  if ( ok )
2775  {
2776  penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
2777  }
2778  }
2779 
2781  {
2784  if ( ok )
2785  {
2786  mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
2787  }
2788  }
2789 
2790  p->setBrush( mBrush );
2791  if ( !qgsDoubleNear( penWidth, 0.0 ) )
2792  {
2793  mPen.setColor( penColor );
2794  mPen.setWidthF( penWidth );
2795  p->setPen( mPen );
2796  }
2797  else
2798  {
2799  p->setPen( Qt::NoPen );
2800  }
2801  p->save();
2802 
2803  QPointF chrOffset = mChrOffset;
2804  double chrWidth;
2805  QString charToRender = characterToRender( context, chrOffset, chrWidth );
2806 
2807  double sizeToRender = calculateSize( context );
2808 
2809  bool hasDataDefinedRotation = false;
2810  QPointF offset;
2811  double angle = 0;
2812  calculateOffsetAndRotation( context, sizeToRender, hasDataDefinedRotation, offset, angle );
2813 
2814  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
2815 
2816  if ( !qgsDoubleNear( angle, 0.0 ) )
2817  transform.rotate( angle );
2818 
2819  if ( !qgsDoubleNear( sizeToRender, mOrigSize ) )
2820  {
2821  double s = sizeToRender / mOrigSize;
2822  transform.scale( s, s );
2823  }
2824 
2825  QPainterPath path;
2826  path.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
2827  p->drawPath( transform.map( path ) );
2828  p->restore();
2829 }
2830 
2832 {
2833  QgsStringMap props;
2834  props[QStringLiteral( "font" )] = mFontFamily;
2835  props[QStringLiteral( "chr" )] = mChr;
2836  props[QStringLiteral( "size" )] = QString::number( mSize );
2837  props[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2838  props[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2839  props[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
2840  props[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
2841  props[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
2842  props[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2843  props[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2844  props[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
2845  props[QStringLiteral( "angle" )] = QString::number( mAngle );
2846  props[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2847  props[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2848  props[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2849  props[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
2850  props[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
2851  return props;
2852 }
2853 
2855 {
2856  QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( mFontFamily, mChr, mSize, mColor, mAngle );
2857  m->setStrokeColor( mStrokeColor );
2858  m->setStrokeWidth( mStrokeWidth );
2859  m->setStrokeWidthUnit( mStrokeWidthUnit );
2860  m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
2861  m->setPenJoinStyle( mPenJoinStyle );
2862  m->setOffset( mOffset );
2863  m->setOffsetUnit( mOffsetUnit );
2865  m->setSizeUnit( mSizeUnit );
2870  copyPaintEffect( m );
2871  return m;
2872 }
2873 
2874 void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
2875 {
2876  // <Graphic>
2877  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2878  element.appendChild( graphicElem );
2879 
2880  QString fontPath = QStringLiteral( "ttf://%1" ).arg( mFontFamily );
2881  int markIndex = mChr.unicode();
2883  QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, QStringLiteral( "ttf" ), &markIndex, mColor, size );
2884 
2885  // <Rotation>
2886  QString angleFunc;
2887  bool ok;
2888  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2889  if ( !ok )
2890  {
2891  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mAngle );
2892  }
2893  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2894  {
2895  angleFunc = QString::number( angle + mAngle );
2896  }
2897  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2898 
2899  // <Displacement>
2900  QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2901  QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
2902 }
2903 
2905 {
2906  QPointF chrOffset = mChrOffset;
2907  double chrWidth = mChrWidth;
2908  //calculate width of rendered character
2909  ( void )characterToRender( context, chrOffset, chrWidth );
2910 
2911  if ( !mFontMetrics )
2912  mFontMetrics = new QFontMetrics( mFont );
2913 
2914  double scaledSize = calculateSize( context );
2915  if ( !qgsDoubleNear( scaledSize, mOrigSize ) )
2916  {
2917  chrWidth *= scaledSize / mOrigSize;
2918  }
2919 
2920  bool hasDataDefinedRotation = false;
2921  QPointF offset;
2922  double angle = 0;
2923  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
2924  scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
2925 
2926  QMatrix transform;
2927 
2928  // move to the desired position
2929  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
2930 
2931  if ( !qgsDoubleNear( angle, 0.0 ) )
2932  transform.rotate( angle );
2933 
2934  QRectF symbolBounds = transform.mapRect( QRectF( -chrWidth / 2.0,
2935  -scaledSize / 2.0,
2936  chrWidth,
2937  scaledSize ) );
2938  return symbolBounds;
2939 }
2940 
2942 {
2943  QgsDebugMsg( QStringLiteral( "Entered." ) );
2944 
2945  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
2946  if ( graphicElem.isNull() )
2947  return nullptr;
2948 
2949  QString name, format;
2950  QColor color;
2951  double size;
2952  int chr;
2953 
2954  if ( !QgsSymbolLayerUtils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
2955  return nullptr;
2956 
2957  if ( !name.startsWith( QLatin1String( "ttf://" ) ) || format != QLatin1String( "ttf" ) )
2958  return nullptr;
2959 
2960  QString fontFamily = name.mid( 6 );
2961 
2962  double angle = 0.0;
2963  QString angleFunc;
2964  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2965  {
2966  bool ok;
2967  double d = angleFunc.toDouble( &ok );
2968  if ( ok )
2969  angle = d;
2970  }
2971 
2972  QPointF offset;
2973  QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, offset );
2974 
2975  QString uom = element.attribute( QStringLiteral( "uom" ) );
2976  offset.setX( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.x() ) );
2977  offset.setY( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.y() ) );
2978  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
2979 
2980  QgsMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( fontFamily, chr, size, color );
2981  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
2982  m->setAngle( angle );
2983  m->setOffset( offset );
2984  return m;
2985 }
2986 
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:150
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
Sets the stroke width map unit scale.
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.
void setColor(const QColor &c) override
The fill color.
QColor mStrokeColor
Stroke color.
QgsFields fields() const
Fields of the layer.
Definition: qgssymbol.h:653
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
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
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:1156
void startRender(QgsSymbolRenderContext &context) override
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:278
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.
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
Flags flags() const
Returns combination of flags used for rendering.
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.
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:570
Rotation of symbol may be changed during rendering and symbol should not be cached.
Definition: qgssymbol.h:104
QgsFontMarkerSymbolLayer(const QString &fontFamily=DEFAULT_FONTMARKER_FONT, QChar chr=DEFAULT_FONTMARKER_CHR, double pointSize=DEFAULT_FONTMARKER_SIZE, const QColor &color=DEFAULT_FONTMARKER_COLOR, double angle=DEFAULT_FONTMARKER_ANGLE)
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 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:1097
static QString encodePenStyle(Qt::PenStyle style)
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:36
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.
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.
void stopRender(QgsSymbolRenderContext &context) override
bool mUsingCache
True if using cached images of markers for drawing.
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
qreal opacity() const
Returns the opacity for the symbol.
Definition: qgssymbol.h:602
double size() const
Returns the symbol size.
double mStrokeWidth
Stroke width.
QBrush mBrush
QBrush corresponding to marker&#39;s fill style.
QgsSimpleMarkerSymbolLayerBase(QgsSimpleMarkerSymbolLayerBase::Shape shape=Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, QgsSymbol::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructor for QgsSimpleMarkerSymbolLayerBase.
void setHorizontalAnchorPoint(HorizontalAnchorPoint h)
Sets the horizontal anchor point for positioning the symbol.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
static void resolvePaths(QgsStringMap &properties, const QgsPathResolver &pathResolver, bool saving)
Turns relative paths in properties map to absolute when reading and vice versa when writing...
void setSizeMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol&#39;s size.
#define DEFAULT_FONTMARKER_JOINSTYLE
void setLayer(const QString &layer)
static double mapUnitScaleFactor(double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits, double mapUnitsPerPixel=1.0)
Returns scale factor for conversion to map units.
QColor color() const override
The fill color.
void calculateOffsetAndRotation(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle) const
Calculates the marker offset and rotation.
virtual QColor color() const
The fill color.
static void createRotationElement(QDomDocument &doc, QDomElement &element, const QString &rotationFunc)
void setFillColor(const QColor &color) override
Set fill color.
QColor fillColor() const override
Gets fill color.
QColor selectionColor() const
#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.
double mapUnitsPerPixel() const
Returns current map units per pixel.
QPen mSelPen
QPen to use as stroke of selected symbols.
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:572
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)
QgsWkbTypes::GeometryType type() const
Returns type of the geometry as a QgsWkbTypes::GeometryType.
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
Definition: qgssymbol.h:611
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.
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.
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
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)
QPainter * painter()
Returns the destination QPainter for the render operation.
virtual void setFillColor(const QColor &color)
Set fill color.
const QgsMapToPixel & mapToPixel() const
QColor color() const override
The fill color.
SymbolType type() const
Returns the symbol&#39;s type.
Definition: qgssymbol.h:120
QImage mCache
Cached image of marker, if using cached version.
QgsSvgMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
QgsSimpleMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
Struct for storing maximum and minimum scales for measurements in map units.
void setMapUnitScale(const QgsMapUnitScale &scale) override
#define DEFAULT_SIMPLEMARKER_ANGLE
#define DEFAULT_SIMPLEMARKER_COLOR
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void startRender(QgsSymbolRenderContext &context) override
QgsSimpleMarkerSymbolLayer(QgsSimpleMarkerSymbolLayerBase::Shape shape=Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, QgsSymbol::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD, const QColor &color=DEFAULT_SIMPLEMARKER_COLOR, const QColor &strokeColor=DEFAULT_SIMPLEMARKER_BORDERCOLOR, Qt::PenJoinStyle penJoinStyle=DEFAULT_SIMPLEMARKER_JOINSTYLE)
Constructor for QgsSimpleMarkerSymbolLayer.
QgsSymbol::ScaleMethod mScaleMethod
Marker size scaling method.
Qt::PenJoinStyle mPenJoinStyle
Stroke pen join style.
const QgsFeature * feature() const
Current feature being rendered - may be null.
Definition: qgssymbol.h:628
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
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.
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
QString layerType() const override
Returns a string that represents this layer type.
#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
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:110
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:618
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...