QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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 {
1649  return true;
1650  if ( mFill && mFill->hasDataDefinedProperties() )
1651  return true;
1652  return false;
1653 }
1654 
1656 {
1657  mColor = c;
1658  if ( mFill )
1659  mFill->setColor( c );
1660 }
1661 
1663 {
1664  return mFill ? mFill->color() : mColor;
1665 }
1666 
1667 void QgsFilledMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, QgsSimpleMarkerSymbolLayerBase::Shape shape, const QPolygonF &polygon, const QPainterPath &path )
1668 {
1669  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1670  //of the rendered point!
1671 
1672  QPainter *p = context.renderContext().painter();
1673  if ( !p )
1674  {
1675  return;
1676  }
1677 
1678  if ( shapeIsFilled( shape ) )
1679  {
1680  p->setBrush( Qt::red );
1681  }
1682  else
1683  {
1684  p->setBrush( Qt::NoBrush );
1685  }
1686  p->setPen( Qt::black );
1687 
1688  if ( !polygon.isEmpty() )
1689  {
1690  mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
1691  }
1692  else
1693  {
1694  QPolygonF poly = path.toFillPolygon();
1695  mFill->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
1696  }
1697 
1698 
1699 }
1700 
1701 
1703 
1704 
1706 {
1707  mPath = path;
1708  mSize = size;
1709  mAngle = angle;
1710  mOffset = QPointF( 0, 0 );
1712  mStrokeWidth = 0.2;
1713  mStrokeWidthUnit = QgsUnitTypes::RenderMillimeters;
1714  mColor = QColor( 35, 35, 35 );
1715  mStrokeColor = QColor( 35, 35, 35 );
1716  updateDefaultAspectRatio();
1717 }
1718 
1719 
1721 {
1722  QString name;
1723  double size = DEFAULT_SVGMARKER_SIZE;
1724  double angle = DEFAULT_SVGMARKER_ANGLE;
1726 
1727  if ( props.contains( QStringLiteral( "name" ) ) )
1728  name = props[QStringLiteral( "name" )];
1729  if ( props.contains( QStringLiteral( "size" ) ) )
1730  size = props[QStringLiteral( "size" )].toDouble();
1731  if ( props.contains( QStringLiteral( "angle" ) ) )
1732  angle = props[QStringLiteral( "angle" )].toDouble();
1733  if ( props.contains( QStringLiteral( "scale_method" ) ) )
1734  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )] );
1735 
1736  QgsSvgMarkerSymbolLayer *m = new QgsSvgMarkerSymbolLayer( name, size, angle, scaleMethod );
1737 
1738  //we only check the svg default parameters if necessary, since it could be expensive
1739  if ( !props.contains( QStringLiteral( "fill" ) ) && !props.contains( QStringLiteral( "color" ) ) && !props.contains( QStringLiteral( "outline" ) ) &&
1740  !props.contains( QStringLiteral( "outline_color" ) ) && !props.contains( QStringLiteral( "outline-width" ) ) && !props.contains( QStringLiteral( "outline_width" ) ) )
1741  {
1742  QColor fillColor, strokeColor;
1743  double fillOpacity = 1.0;
1744  double strokeOpacity = 1.0;
1745  double strokeWidth;
1746  bool hasFillParam = false, hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
1747  bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
1748  QgsApplication::svgCache()->containsParams( name, hasFillParam, hasDefaultFillColor, fillColor,
1749  hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
1750  hasStrokeParam, hasDefaultStrokeColor, strokeColor,
1751  hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
1752  hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
1753  if ( hasDefaultFillColor )
1754  {
1755  m->setFillColor( fillColor );
1756  }
1757  if ( hasDefaultFillOpacity )
1758  {
1759  QColor c = m->fillColor();
1760  c.setAlphaF( fillOpacity );
1761  m->setFillColor( c );
1762  }
1763  if ( hasDefaultStrokeColor )
1764  {
1765  m->setStrokeColor( strokeColor );
1766  }
1767  if ( hasDefaultStrokeWidth )
1768  {
1769  m->setStrokeWidth( strokeWidth );
1770  }
1771  if ( hasDefaultStrokeOpacity )
1772  {
1773  QColor c = m->strokeColor();
1774  c.setAlphaF( strokeOpacity );
1775  m->setStrokeColor( c );
1776  }
1777  }
1778 
1779  if ( props.contains( QStringLiteral( "size_unit" ) ) )
1780  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
1781  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1782  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
1783  if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
1784  m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
1785  if ( props.contains( QStringLiteral( "offset" ) ) )
1786  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
1787  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1788  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
1789  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1790  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
1791  if ( props.contains( QStringLiteral( "fill" ) ) )
1792  {
1793  //pre 2.5 projects used "fill"
1794  m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "fill" )] ) );
1795  }
1796  else if ( props.contains( QStringLiteral( "color" ) ) )
1797  {
1798  m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] ) );
1799  }
1800  if ( props.contains( QStringLiteral( "outline" ) ) )
1801  {
1802  //pre 2.5 projects used "outline"
1803  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline" )] ) );
1804  }
1805  else if ( props.contains( QStringLiteral( "outline_color" ) ) )
1806  {
1807  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )] ) );
1808  }
1809  else if ( props.contains( QStringLiteral( "line_color" ) ) )
1810  {
1811  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )] ) );
1812  }
1813 
1814  if ( props.contains( QStringLiteral( "outline-width" ) ) )
1815  {
1816  //pre 2.5 projects used "outline-width"
1817  m->setStrokeWidth( props[QStringLiteral( "outline-width" )].toDouble() );
1818  }
1819  else if ( props.contains( QStringLiteral( "outline_width" ) ) )
1820  {
1821  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
1822  }
1823  else if ( props.contains( QStringLiteral( "line_width" ) ) )
1824  {
1825  m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
1826  }
1827 
1828  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
1829  {
1830  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )] ) );
1831  }
1832  else if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
1833  {
1834  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )] ) );
1835  }
1836  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
1837  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )] ) );
1838 
1839  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1840  {
1841  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1842  }
1843  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1844  {
1845  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1846  }
1847 
1848  m->restoreOldDataDefinedProperties( props );
1849 
1851 
1852  return m;
1853 }
1854 
1856 {
1857  QgsStringMap::iterator it = properties.find( QStringLiteral( "name" ) );
1858  if ( it != properties.end() )
1859  {
1860  if ( saving )
1861  it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value(), pathResolver );
1862  else
1863  it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value(), pathResolver );
1864  }
1865 }
1866 
1867 void QgsSvgMarkerSymbolLayer::setPath( const QString &path )
1868 {
1869  mPath = path;
1870  QColor defaultFillColor, defaultStrokeColor;
1871  double strokeWidth, fillOpacity, strokeOpacity;
1872  bool hasFillParam = false, hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
1873  bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
1874  QgsApplication::svgCache()->containsParams( path, hasFillParam, hasDefaultFillColor, defaultFillColor,
1875  hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
1876  hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
1877  hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
1878  hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
1879 
1880  double newFillOpacity = hasFillOpacityParam ? fillColor().alphaF() : 1.0;
1881  double newStrokeOpacity = hasStrokeOpacityParam ? strokeColor().alphaF() : 1.0;
1882 
1883  if ( hasDefaultFillColor )
1884  {
1885  defaultFillColor.setAlphaF( newFillOpacity );
1886  setFillColor( defaultFillColor );
1887  }
1888  if ( hasDefaultFillOpacity )
1889  {
1890  QColor c = fillColor();
1891  c.setAlphaF( fillOpacity );
1892  setFillColor( c );
1893  }
1894  if ( hasDefaultStrokeColor )
1895  {
1896  defaultStrokeColor.setAlphaF( newStrokeOpacity );
1897  setStrokeColor( defaultStrokeColor );
1898  }
1899  if ( hasDefaultStrokeWidth )
1900  {
1901  setStrokeWidth( strokeWidth );
1902  }
1903  if ( hasDefaultStrokeOpacity )
1904  {
1905  QColor c = strokeColor();
1906  c.setAlphaF( strokeOpacity );
1907  setStrokeColor( c );
1908  }
1909 
1910  updateDefaultAspectRatio();
1911 }
1912 
1914 {
1915  if ( mDefaultAspectRatio == 0.0 )
1916  {
1917  //size
1918  double size = mSize;
1919  //assume 88 dpi as standard value
1920  double widthScaleFactor = 3.465;
1921  QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( mPath, size, mColor, mStrokeColor, mStrokeWidth, widthScaleFactor );
1922  // set default aspect ratio
1923  mDefaultAspectRatio = svgViewbox.isValid() ? svgViewbox.height() / svgViewbox.width() : 0.0;
1924  }
1925  return mDefaultAspectRatio;
1926 }
1927 
1929 {
1930  bool aPreservedAspectRatio = preservedAspectRatio();
1931  if ( aPreservedAspectRatio && !par )
1932  {
1933  mFixedAspectRatio = mDefaultAspectRatio;
1934  }
1935  else if ( !aPreservedAspectRatio && par )
1936  {
1937  mFixedAspectRatio = 0.0;
1938  }
1939  return preservedAspectRatio();
1940 }
1941 
1942 
1944 {
1945  return QStringLiteral( "SvgMarker" );
1946 }
1947 
1949 {
1950  QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
1951  Q_UNUSED( context );
1952 }
1953 
1955 {
1956  Q_UNUSED( context );
1957 }
1958 
1960 {
1961  QPainter *p = context.renderContext().painter();
1962  if ( !p )
1963  return;
1964 
1965  bool hasDataDefinedSize = false;
1966  double scaledSize = calculateSize( context, hasDataDefinedSize );
1967  double size = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
1968 
1969  //don't render symbols with size below one or above 10,000 pixels
1970  if ( static_cast< int >( size ) < 1 || 10000.0 < size )
1971  {
1972  return;
1973  }
1974 
1975  p->save();
1976 
1977  bool hasDataDefinedAspectRatio = false;
1978  double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
1979 
1980  QPointF outputOffset;
1981  double angle = 0.0;
1982  calculateOffsetAndRotation( context, scaledSize, outputOffset, angle );
1983 
1984  p->translate( point + outputOffset );
1985 
1986  bool rotated = !qgsDoubleNear( angle, 0 );
1987  if ( rotated )
1988  p->rotate( angle );
1989 
1990  QString path = mPath;
1992  {
1993  context.setOriginalValueVariable( mPath );
1995  context.renderContext().pathResolver() );
1996  }
1997 
1998  double strokeWidth = mStrokeWidth;
2000  {
2001  context.setOriginalValueVariable( mStrokeWidth );
2003  }
2004  strokeWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
2005 
2006  QColor fillColor = mColor;
2008  {
2011  }
2012 
2013  QColor strokeColor = mStrokeColor;
2015  {
2018  }
2019 
2020  bool fitsInCache = true;
2021  bool usePict = true;
2022  double hwRatio = 1.0;
2023  if ( !context.renderContext().forceVectorOutput() && !rotated )
2024  {
2025  QImage img = QgsApplication::svgCache()->svgAsImage( path, size, fillColor, strokeColor, strokeWidth,
2026  context.renderContext().scaleFactor(), fitsInCache, aspectRatio );
2027  if ( fitsInCache && img.width() > 1 )
2028  {
2029  usePict = false;
2030  //consider transparency
2031  if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2032  {
2033  QImage transparentImage = img.copy();
2034  QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2035  p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
2036  hwRatio = static_cast< double >( transparentImage.height() ) / static_cast< double >( transparentImage.width() );
2037  }
2038  else
2039  {
2040  p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2041  hwRatio = static_cast< double >( img.height() ) / static_cast< double >( img.width() );
2042  }
2043  }
2044  }
2045 
2046  if ( usePict || !fitsInCache )
2047  {
2048  p->setOpacity( context.opacity() );
2049  QPicture pct = QgsApplication::svgCache()->svgAsPicture( path, size, fillColor, strokeColor, strokeWidth,
2050  context.renderContext().scaleFactor(), context.renderContext().forceVectorOutput(), aspectRatio );
2051  if ( pct.width() > 1 )
2052  {
2053  p->save();
2054  _fixQPictureDPI( p );
2055  p->drawPicture( 0, 0, pct );
2056  p->restore();
2057  hwRatio = static_cast< double >( pct.height() ) / static_cast< double >( pct.width() );
2058  }
2059  }
2060 
2061  if ( context.selected() )
2062  {
2063  QPen pen( context.renderContext().selectionColor() );
2064  double penWidth = context.renderContext().convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters );
2065  if ( penWidth > size / 20 )
2066  {
2067  // keep the pen width from covering symbol
2068  penWidth = size / 20;
2069  }
2070  double penOffset = penWidth / 2;
2071  pen.setWidth( penWidth );
2072  p->setPen( pen );
2073  p->setBrush( Qt::NoBrush );
2074  double wSize = size + penOffset;
2075  double hSize = size * hwRatio + penOffset;
2076  p->drawRect( QRectF( -wSize / 2.0, -hSize / 2.0, wSize, hSize ) );
2077  }
2078 
2079  p->restore();
2080 
2082  {
2083  // workaround issue with nested QPictures forgetting antialiasing flag - see https://issues.qgis.org/issues/14960
2084  p->setRenderHint( QPainter::Antialiasing );
2085  }
2086 
2087 }
2088 
2089 double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2090 {
2091  double scaledSize = mSize;
2093 
2094  bool ok = true;
2095  if ( hasDataDefinedSize )
2096  {
2097  context.setOriginalValueVariable( mSize );
2099  }
2100  else
2101  {
2103  if ( hasDataDefinedSize )
2104  {
2105  context.setOriginalValueVariable( mSize );
2107  }
2108  }
2109 
2110  if ( hasDataDefinedSize && ok )
2111  {
2112  switch ( mScaleMethod )
2113  {
2114  case QgsSymbol::ScaleArea:
2115  scaledSize = std::sqrt( scaledSize );
2116  break;
2118  break;
2119  }
2120  }
2121 
2122  return scaledSize;
2123 }
2124 
2125 double QgsSvgMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2126 {
2128  if ( !hasDataDefinedAspectRatio )
2129  return mFixedAspectRatio;
2130 
2131  if ( !mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyHeight ) && mFixedAspectRatio <= 0.0 )
2132  return 0.0;
2133 
2134  double scaledAspectRatio = mDefaultAspectRatio;
2135  if ( mFixedAspectRatio > 0.0 )
2136  scaledAspectRatio = mFixedAspectRatio;
2137 
2138  double defaultHeight = mSize * scaledAspectRatio;
2139  scaledAspectRatio = defaultHeight / scaledSize;
2140 
2141  bool ok = true;
2142  double scaledHeight = scaledSize * scaledAspectRatio;
2144  {
2145  context.setOriginalValueVariable( defaultHeight );
2146  scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyHeight, context.renderContext().expressionContext(), defaultHeight, &ok );
2147  }
2148 
2149  if ( hasDataDefinedAspectRatio && ok )
2150  {
2151  switch ( mScaleMethod )
2152  {
2153  case QgsSymbol::ScaleArea:
2154  scaledHeight = sqrt( scaledHeight );
2155  break;
2157  break;
2158  }
2159  }
2160 
2161  scaledAspectRatio = scaledHeight / scaledSize;
2162 
2163  return scaledAspectRatio;
2164 }
2165 
2166 void QgsSvgMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledSize, QPointF &offset, double &angle ) const
2167 {
2168  //offset
2169  double offsetX = 0;
2170  double offsetY = 0;
2171  markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
2172  offset = QPointF( offsetX, offsetY );
2173 
2174  angle = mAngle + mLineAngle;
2176  {
2177  context.setOriginalValueVariable( mAngle );
2179  }
2180 
2182  if ( hasDataDefinedRotation )
2183  {
2184  // For non-point markers, "dataDefinedRotation" means following the
2185  // shape (shape-data defined). For them, "field-data defined" does
2186  // not work at all. TODO: if "field-data defined" ever gets implemented
2187  // we'll need a way to distinguish here between the two, possibly
2188  // using another flag in renderHints()
2189  const QgsFeature *f = context.feature();
2190  if ( f )
2191  {
2192  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
2193  {
2194  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2195  angle += m2p.mapRotation();
2196  }
2197  }
2198  }
2199 
2200  if ( angle )
2201  offset = _rotatedOffset( offset, angle );
2202 }
2203 
2204 
2206 {
2207  QgsStringMap map;
2208  map[QStringLiteral( "name" )] = mPath;
2209  map[QStringLiteral( "size" )] = QString::number( mSize );
2210  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2211  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2212  map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2213  map[QStringLiteral( "angle" )] = QString::number( mAngle );
2214  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2215  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2216  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2217  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2218  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
2219  map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
2220  map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
2221  map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2222  map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2223  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
2224  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
2225  return map;
2226 }
2227 
2229 {
2231  m->setFixedAspectRatio( mFixedAspectRatio );
2232  m->setColor( mColor );
2233  m->setStrokeColor( mStrokeColor );
2234  m->setStrokeWidth( mStrokeWidth );
2235  m->setStrokeWidthUnit( mStrokeWidthUnit );
2236  m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
2237  m->setOffset( mOffset );
2238  m->setOffsetUnit( mOffsetUnit );
2240  m->setSizeUnit( mSizeUnit );
2245  copyPaintEffect( m );
2246  return m;
2247 }
2248 
2250 {
2252  mStrokeWidthUnit = unit;
2253 }
2254 
2256 {
2258  if ( unit != mStrokeWidthUnit )
2259  {
2261  }
2262  return unit;
2263 }
2264 
2266 {
2268  mStrokeWidthMapUnitScale = scale;
2269 }
2270 
2272 {
2273  if ( QgsMarkerSymbolLayer::mapUnitScale() == mStrokeWidthMapUnitScale )
2274  {
2275  return mStrokeWidthMapUnitScale;
2276  }
2277  return QgsMapUnitScale();
2278 }
2279 
2280 void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
2281 {
2282  // <Graphic>
2283  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2284  element.appendChild( graphicElem );
2285 
2286  // encode a parametric SVG reference
2288  double strokeWidth = QgsSymbolLayerUtils::rescaleUom( mStrokeWidth, mStrokeWidthUnit, props );
2289  QgsSymbolLayerUtils::parametricSvgToSld( doc, graphicElem, mPath, mColor, size, mStrokeColor, strokeWidth );
2290 
2291  // <Rotation>
2292  QString angleFunc;
2293  bool ok;
2294  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2295  if ( !ok )
2296  {
2297  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mAngle );
2298  }
2299  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2300  {
2301  angleFunc = QString::number( angle + mAngle );
2302  }
2303 
2304  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2305 
2306  // <Displacement>
2307  QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2308  QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
2309 }
2310 
2312 {
2313  QgsDebugMsg( QStringLiteral( "Entered." ) );
2314 
2315  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
2316  if ( graphicElem.isNull() )
2317  return nullptr;
2318 
2319  QString path, mimeType;
2320  QColor fillColor;
2321  double size;
2322 
2323  if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
2324  return nullptr;
2325 
2326  QString uom = element.attribute( QStringLiteral( "uom" ) );
2327  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
2328 
2329  if ( mimeType != QLatin1String( "image/svg+xml" ) )
2330  return nullptr;
2331 
2332  double angle = 0.0;
2333  QString angleFunc;
2334  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2335  {
2336  bool ok;
2337  double d = angleFunc.toDouble( &ok );
2338  if ( ok )
2339  angle = d;
2340  }
2341 
2342  QPointF offset;
2343  QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, offset );
2344 
2345  QgsSvgMarkerSymbolLayer *m = new QgsSvgMarkerSymbolLayer( path, size );
2346  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
2347  m->setFillColor( fillColor );
2348  //m->setStrokeColor( strokeColor );
2349  //m->setStrokeWidth( strokeWidth );
2350  m->setAngle( angle );
2351  m->setOffset( offset );
2352  return m;
2353 }
2354 
2355 bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
2356 {
2357  //size
2358  double size = mSize;
2359 
2360  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
2361 
2362  bool ok = true;
2363  if ( hasDataDefinedSize )
2364  {
2365  context.setOriginalValueVariable( mSize );
2367  }
2368 
2369  if ( hasDataDefinedSize && ok )
2370  {
2371  switch ( mScaleMethod )
2372  {
2373  case QgsSymbol::ScaleArea:
2374  size = std::sqrt( size );
2375  break;
2377  break;
2378  }
2379  }
2380 
2382  {
2383  size *= mmMapUnitScaleFactor;
2384  }
2385 
2386  //offset, angle
2387  QPointF offset = mOffset;
2388 
2390  {
2392  QString offsetString = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext(), QString(), &ok );
2393  if ( ok )
2394  offset = QgsSymbolLayerUtils::decodePoint( offsetString );
2395  }
2396  double offsetX = offset.x();
2397  double offsetY = offset.y();
2398 
2399  QPointF outputOffset( offsetX, offsetY );
2400 
2401  double angle = mAngle + mLineAngle;
2403  {
2404  context.setOriginalValueVariable( mAngle );
2406  }
2407 
2408  if ( angle )
2409  outputOffset = _rotatedOffset( outputOffset, angle );
2410 
2411  outputOffset *= e.mapUnitScaleFactor( e.symbologyScale(), mOffsetUnit, e.mapUnits(), context.renderContext().mapToPixel().mapUnitsPerPixel() );
2412 
2413  QString path = mPath;
2415  {
2416  context.setOriginalValueVariable( mPath );
2418  context.renderContext().pathResolver() );
2419  }
2420 
2421  double strokeWidth = mStrokeWidth;
2423  {
2424  context.setOriginalValueVariable( mStrokeWidth );
2426  }
2427  strokeWidth *= e.mapUnitScaleFactor( e.symbologyScale(), mStrokeWidthUnit, e.mapUnits(), context.renderContext().mapToPixel().mapUnitsPerPixel() );
2428 
2429  QColor fillColor = mColor;
2431  {
2434  }
2435 
2436  QColor strokeColor = mStrokeColor;
2438  {
2441  }
2442 
2443  const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, size, fillColor, strokeColor, strokeWidth,
2444  context.renderContext().scaleFactor(), mFixedAspectRatio );
2445 
2446  QSvgRenderer r( svgContent );
2447  if ( !r.isValid() )
2448  return false;
2449 
2450  QgsDxfPaintDevice pd( &e );
2451  pd.setDrawingSize( QSizeF( r.defaultSize() ) );
2452 
2453  QSizeF outSize( r.defaultSize() );
2454  outSize.scale( size, size, Qt::KeepAspectRatio );
2455 
2456  QPainter p;
2457  p.begin( &pd );
2458  if ( !qgsDoubleNear( angle, 0.0 ) )
2459  {
2460  p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
2461  p.rotate( angle );
2462  p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
2463  }
2464  pd.setShift( shift + QPointF( outputOffset.x(), -outputOffset.y() ) );
2465  pd.setOutputSize( QRectF( -outSize.width() / 2.0, -outSize.height() / 2.0, outSize.width(), outSize.height() ) );
2466  pd.setLayer( layerName );
2467  r.render( &p );
2468  p.end();
2469  return true;
2470 }
2471 
2473 {
2474  bool hasDataDefinedSize = false;
2475  double scaledSize = calculateSize( context, hasDataDefinedSize );
2476  scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
2477 
2478  //don't render symbols with size below one or above 10,000 pixels
2479  if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
2480  {
2481  return QRectF();
2482  }
2483 
2484  QPointF outputOffset;
2485  double angle = 0.0;
2486  calculateOffsetAndRotation( context, scaledSize, outputOffset, angle );
2487 
2488  QString path = mPath;
2490  {
2491  context.setOriginalValueVariable( mPath );
2493  context.renderContext().pathResolver() );
2494  }
2495 
2496  double strokeWidth = mStrokeWidth;
2498  {
2499  context.setOriginalValueVariable( mStrokeWidth );
2501  }
2502  strokeWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
2503 
2504  //need to get colors to take advantage of cached SVGs
2505  QColor fillColor = mColor;
2507  {
2510  }
2511 
2512  QColor strokeColor = mStrokeColor;
2514  {
2517  }
2518 
2519  QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledSize, fillColor, strokeColor, strokeWidth,
2520  context.renderContext().scaleFactor(), mFixedAspectRatio );
2521 
2522  double scaledHeight = svgViewbox.isValid() ? scaledSize * svgViewbox.height() / svgViewbox.width() : scaledSize;
2523 
2524  QMatrix transform;
2525 
2526  // move to the desired position
2527  transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
2528 
2529  if ( !qgsDoubleNear( angle, 0.0 ) )
2530  transform.rotate( angle );
2531 
2532  //antialiasing
2533  strokeWidth += 1.0 / 2.0;
2534 
2535  QRectF symbolBounds = transform.mapRect( QRectF( -scaledSize / 2.0,
2536  -scaledHeight / 2.0,
2537  scaledSize,
2538  scaledHeight ) );
2539 
2540  //extend bounds by pen width / 2.0
2541  symbolBounds.adjust( -strokeWidth / 2.0, -strokeWidth / 2.0,
2542  strokeWidth / 2.0, strokeWidth / 2.0 );
2543 
2544  return symbolBounds;
2545 
2546 }
2547 
2549 
2550 QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QChar chr, double pointSize, const QColor &color, double angle )
2551 {
2552  mFontFamily = fontFamily;
2553  mChr = chr;
2554  mColor = color;
2555  mAngle = angle;
2556  mSize = pointSize;
2557  mOrigSize = pointSize;
2559  mOffset = QPointF( 0, 0 );
2561  mStrokeColor = DEFAULT_FONTMARKER_BORDERCOLOR;
2562  mStrokeWidth = 0.0;
2563  mStrokeWidthUnit = QgsUnitTypes::RenderMillimeters;
2564  mPenJoinStyle = DEFAULT_FONTMARKER_JOINSTYLE;
2565 }
2566 
2568 {
2569  delete mFontMetrics;
2570 }
2571 
2573 {
2574  QString fontFamily = DEFAULT_FONTMARKER_FONT;
2575  QChar chr = DEFAULT_FONTMARKER_CHR;
2576  double pointSize = DEFAULT_FONTMARKER_SIZE;
2578  double angle = DEFAULT_FONTMARKER_ANGLE;
2579 
2580  if ( props.contains( QStringLiteral( "font" ) ) )
2581  fontFamily = props[QStringLiteral( "font" )];
2582  if ( props.contains( QStringLiteral( "chr" ) ) && props[QStringLiteral( "chr" )].length() > 0 )
2583  chr = props[QStringLiteral( "chr" )].at( 0 );
2584  if ( props.contains( QStringLiteral( "size" ) ) )
2585  pointSize = props[QStringLiteral( "size" )].toDouble();
2586  if ( props.contains( QStringLiteral( "color" ) ) )
2587  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] );
2588  if ( props.contains( QStringLiteral( "angle" ) ) )
2589  angle = props[QStringLiteral( "angle" )].toDouble();
2590 
2591  QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( fontFamily, chr, pointSize, color, angle );
2592 
2593  if ( props.contains( QStringLiteral( "outline_color" ) ) )
2594  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )] ) );
2595  if ( props.contains( QStringLiteral( "outline_width" ) ) )
2596  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
2597  if ( props.contains( QStringLiteral( "offset" ) ) )
2598  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
2599  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
2600  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
2601  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2602  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
2603  if ( props.contains( QStringLiteral( "size_unit" ) ) )
2604  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
2605  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
2606  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
2607  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
2608  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )] ) );
2609  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
2610  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )] ) );
2611  if ( props.contains( QStringLiteral( "joinstyle" ) ) )
2612  m->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )] ) );
2613  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2614  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2615  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2616  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2617 
2618  m->restoreOldDataDefinedProperties( props );
2619 
2620  return m;
2621 }
2622 
2624 {
2625  return QStringLiteral( "FontMarker" );
2626 }
2627 
2629 {
2630  QColor brushColor = mColor;
2631  QColor penColor = mStrokeColor;
2632 
2633  brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
2634  penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
2635 
2636  mBrush = QBrush( brushColor );
2637  mPen = QPen( penColor );
2638  mPen.setJoinStyle( mPenJoinStyle );
2639  mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
2640 
2641  mFont = QFont( mFontFamily );
2642  const double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
2643  mNonZeroFontSize = !qgsDoubleNear( sizePixels, 0.0 );
2644  // if a non zero, but small pixel size results, round up to 2 pixels so that a "dot" is at least visible
2645  // (if we set a <=1 pixel size here Qt will reset the font to a default size, leading to much too large symbols)
2646  mFont.setPixelSize( std::max( 2, static_cast< int >( std::round( sizePixels ) ) ) );
2647  delete mFontMetrics;
2648  mFontMetrics = new QFontMetrics( mFont );
2649  mChrWidth = mFontMetrics->width( mChr );
2650  mChrOffset = QPointF( mChrWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
2651  mOrigSize = mSize; // save in case the size would be data defined
2652 
2653  // use caching only when not using a data defined character
2655  if ( mUseCachedPath )
2656  {
2657  QPointF chrOffset = mChrOffset;
2658  double chrWidth;
2659  QString charToRender = characterToRender( context, chrOffset, chrWidth );
2660  mCachedPath = QPainterPath();
2661  mCachedPath.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
2662  }
2663 }
2664 
2666 {
2667  Q_UNUSED( context );
2668 }
2669 
2670 QString QgsFontMarkerSymbolLayer::characterToRender( QgsSymbolRenderContext &context, QPointF &charOffset, double &charWidth )
2671 {
2672  charOffset = mChrOffset;
2673  QString charToRender = mChr;
2675  {
2676  context.setOriginalValueVariable( mChr );
2678  if ( charToRender != mChr )
2679  {
2680  charWidth = mFontMetrics->width( charToRender );
2681  charOffset = QPointF( charWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
2682  }
2683  }
2684  return charToRender;
2685 }
2686 
2687 void QgsFontMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
2688  double scaledSize,
2689  bool &hasDataDefinedRotation,
2690  QPointF &offset,
2691  double &angle ) const
2692 {
2693  //offset
2694  double offsetX = 0;
2695  double offsetY = 0;
2696  markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
2697  offset = QPointF( offsetX, offsetY );
2698 
2699  //angle
2700  bool ok = true;
2701  angle = mAngle + mLineAngle;
2702  bool usingDataDefinedRotation = false;
2704  {
2705  context.setOriginalValueVariable( angle );
2707  usingDataDefinedRotation = ok;
2708  }
2709 
2710  hasDataDefinedRotation = context.renderHints() & QgsSymbol::DynamicRotation || usingDataDefinedRotation;
2711  if ( hasDataDefinedRotation )
2712  {
2713  // For non-point markers, "dataDefinedRotation" means following the
2714  // shape (shape-data defined). For them, "field-data defined" does
2715  // not work at all. TODO: if "field-data defined" ever gets implemented
2716  // we'll need a way to distinguish here between the two, possibly
2717  // using another flag in renderHints()
2718  const QgsFeature *f = context.feature();
2719  if ( f )
2720  {
2721  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
2722  {
2723  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2724  angle += m2p.mapRotation();
2725  }
2726  }
2727  }
2728 
2729  if ( angle )
2730  offset = _rotatedOffset( offset, angle );
2731 }
2732 
2733 double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context )
2734 {
2735  double scaledSize = mSize;
2736  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
2737 
2738  bool ok = true;
2739  if ( hasDataDefinedSize )
2740  {
2741  context.setOriginalValueVariable( mSize );
2743  }
2744 
2745  if ( hasDataDefinedSize && ok )
2746  {
2747  switch ( mScaleMethod )
2748  {
2749  case QgsSymbol::ScaleArea:
2750  scaledSize = std::sqrt( scaledSize );
2751  break;
2753  break;
2754  }
2755  }
2756  return scaledSize;
2757 }
2758 
2760 {
2761  QPainter *p = context.renderContext().painter();
2762  if ( !p || !mNonZeroFontSize )
2763  return;
2764 
2765  QTransform transform;
2766 
2767  bool ok;
2768  QColor brushColor = mColor;
2770  {
2773  }
2774  brushColor = context.selected() ? context.renderContext().selectionColor() : brushColor;
2775  brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
2776  mBrush.setColor( brushColor );
2777 
2778  QColor penColor = mStrokeColor;
2780  {
2783  }
2784  penColor.setAlphaF( penColor.alphaF() * context.opacity() );
2785 
2786  double penWidth = context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
2788  {
2789  context.setOriginalValueVariable( mStrokeWidth );
2790  double strokeWidth = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyStrokeWidth, context.renderContext().expressionContext(), mStrokeWidth, &ok );
2791  if ( ok )
2792  {
2793  penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
2794  }
2795  }
2796 
2798  {
2801  if ( ok )
2802  {
2803  mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
2804  }
2805  }
2806 
2807  p->save();
2808  p->setBrush( mBrush );
2809  if ( !qgsDoubleNear( penWidth, 0.0 ) )
2810  {
2811  mPen.setColor( penColor );
2812  mPen.setWidthF( penWidth );
2813  p->setPen( mPen );
2814  }
2815  else
2816  {
2817  p->setPen( Qt::NoPen );
2818  }
2819 
2820  QPointF chrOffset = mChrOffset;
2821  double chrWidth;
2822  QString charToRender = characterToRender( context, chrOffset, chrWidth );
2823 
2824  double sizeToRender = calculateSize( context );
2825 
2826  bool hasDataDefinedRotation = false;
2827  QPointF offset;
2828  double angle = 0;
2829  calculateOffsetAndRotation( context, sizeToRender, hasDataDefinedRotation, offset, angle );
2830 
2831  p->translate( point.x() + offset.x(), point.y() + offset.y() );
2832 
2833  if ( !qgsDoubleNear( angle, 0.0 ) )
2834  transform.rotate( angle );
2835 
2836  if ( !qgsDoubleNear( sizeToRender, mOrigSize ) )
2837  {
2838  double s = sizeToRender / mOrigSize;
2839  transform.scale( s, s );
2840  }
2841 
2842  if ( mUseCachedPath )
2843  {
2844  p->drawPath( transform.map( mCachedPath ) );
2845  }
2846  else
2847  {
2848  QPainterPath path;
2849  path.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
2850  p->drawPath( transform.map( path ) );
2851  }
2852 
2853  p->restore();
2854 }
2855 
2857 {
2858  QgsStringMap props;
2859  props[QStringLiteral( "font" )] = mFontFamily;
2860  props[QStringLiteral( "chr" )] = mChr;
2861  props[QStringLiteral( "size" )] = QString::number( mSize );
2862  props[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2863  props[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2864  props[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
2865  props[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
2866  props[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
2867  props[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2868  props[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2869  props[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
2870  props[QStringLiteral( "angle" )] = QString::number( mAngle );
2871  props[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2872  props[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2873  props[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2874  props[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
2875  props[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
2876  return props;
2877 }
2878 
2880 {
2881  QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( mFontFamily, mChr, mSize, mColor, mAngle );
2882  m->setStrokeColor( mStrokeColor );
2883  m->setStrokeWidth( mStrokeWidth );
2884  m->setStrokeWidthUnit( mStrokeWidthUnit );
2885  m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
2886  m->setPenJoinStyle( mPenJoinStyle );
2887  m->setOffset( mOffset );
2888  m->setOffsetUnit( mOffsetUnit );
2890  m->setSizeUnit( mSizeUnit );
2895  copyPaintEffect( m );
2896  return m;
2897 }
2898 
2899 void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
2900 {
2901  // <Graphic>
2902  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2903  element.appendChild( graphicElem );
2904 
2905  QString fontPath = QStringLiteral( "ttf://%1" ).arg( mFontFamily );
2906  int markIndex = mChr.unicode();
2908  QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, QStringLiteral( "ttf" ), &markIndex, mColor, size );
2909 
2910  // <Rotation>
2911  QString angleFunc;
2912  bool ok;
2913  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2914  if ( !ok )
2915  {
2916  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mAngle );
2917  }
2918  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2919  {
2920  angleFunc = QString::number( angle + mAngle );
2921  }
2922  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2923 
2924  // <Displacement>
2925  QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2926  QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
2927 }
2928 
2930 {
2931  QPointF chrOffset = mChrOffset;
2932  double chrWidth = mChrWidth;
2933  //calculate width of rendered character
2934  ( void )characterToRender( context, chrOffset, chrWidth );
2935 
2936  if ( !mFontMetrics )
2937  mFontMetrics = new QFontMetrics( mFont );
2938 
2939  double scaledSize = calculateSize( context );
2940  if ( !qgsDoubleNear( scaledSize, mOrigSize ) )
2941  {
2942  chrWidth *= scaledSize / mOrigSize;
2943  }
2944 
2945  bool hasDataDefinedRotation = false;
2946  QPointF offset;
2947  double angle = 0;
2948  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
2949  scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
2950 
2951  QMatrix transform;
2952 
2953  // move to the desired position
2954  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
2955 
2956  if ( !qgsDoubleNear( angle, 0.0 ) )
2957  transform.rotate( angle );
2958 
2959  QRectF symbolBounds = transform.mapRect( QRectF( -chrWidth / 2.0,
2960  -scaledSize / 2.0,
2961  chrWidth,
2962  scaledSize ) );
2963  return symbolBounds;
2964 }
2965 
2967 {
2968  QgsDebugMsg( QStringLiteral( "Entered." ) );
2969 
2970  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
2971  if ( graphicElem.isNull() )
2972  return nullptr;
2973 
2974  QString name, format;
2975  QColor color;
2976  double size;
2977  int chr;
2978 
2979  if ( !QgsSymbolLayerUtils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
2980  return nullptr;
2981 
2982  if ( !name.startsWith( QLatin1String( "ttf://" ) ) || format != QLatin1String( "ttf" ) )
2983  return nullptr;
2984 
2985  QString fontFamily = name.mid( 6 );
2986 
2987  double angle = 0.0;
2988  QString angleFunc;
2989  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2990  {
2991  bool ok;
2992  double d = angleFunc.toDouble( &ok );
2993  if ( ok )
2994  angle = d;
2995  }
2996 
2997  QPointF offset;
2998  QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, offset );
2999 
3000  QString uom = element.attribute( QStringLiteral( "uom" ) );
3001  offset.setX( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.x() ) );
3002  offset.setY( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.y() ) );
3003  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
3004 
3005  QgsMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( fontFamily, chr, size, color );
3006  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
3007  m->setAngle( angle );
3008  m->setOffset( offset );
3009  return m;
3010 }
3011 
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
double calculateSize(QgsSymbolRenderContext &context, bool &hasDataDefinedSize) const
Calculates the desired size of the marker, considering data defined size overrides.
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
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.
static QgsSymbol::ScaleMethod decodeScaleMethod(const QString &str)
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 setColor(const QColor &c) override
The fill color.
QColor mStrokeColor
Stroke color.
virtual QColor strokeColor() const
Gets stroke color.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
Qt::PenStyle mStrokeStyle
Stroke style.
QgsSymbol::RenderHints renderHints() const
Returns the rendering hint flags for the symbol.
Definition: qgssymbol.h:618
Calculate scale by the diameter.
Definition: qgssymbol.h:97
double symbologyScale() const
Returns the reference scale for output.
Definition: qgsdxfexport.h:150
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:61
A paint device for drawing into dxf files.
#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)
QColor selectionColor() const
Returns the color to use when rendering selected features.
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.
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...
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.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
Right facing arrow head (unfilled, lines only)
QPointF offset() const
Returns the marker&#39;s offset, which is the horizontal and vertical displacement which the rendered mar...
static QgsFillSymbol * createSimple(const QgsStringMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties. ...
Definition: qgssymbol.cpp:1150
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)
double size() const
Returns the symbol size.
QgsUnitTypes::RenderUnit mSizeUnit
Marker size unit.
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 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
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
QPointF mOffset
Marker offset.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
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
void clipValueToMapUnitScale(double &value, const QgsMapUnitScale &scale, double pixelToMMFactor) const
Clips value to scale minimum/maximum.
static QString encodeShape(QgsSimpleMarkerSymbolLayerBase::Shape shape)
Encodes a shape to its string representation.
QgsMapUnitScale mapUnitScale() const override
QMap< QString, QString > QgsStringMap
Definition: qgis.h:577
double mapRotation() const
Returns current map rotation in degrees.
void calculateOffsetAndRotation(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle) const
Calculates the marker offset and rotation.
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.
#define DEFAULT_FONTMARKER_SIZE
QColor strokeColor() const override
Gets stroke color.
void setPenJoinStyle(Qt::PenJoinStyle style)
Sets the stroke join style.
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.
double angle() const
Returns the rotation angle for the marker, in degrees clockwise from north.
void startRender(QgsSymbolRenderContext &context) override
void markerOffset(QgsSymbolRenderContext &context, double &offsetX, double &offsetY) const
Calculates the required marker offset, including both the symbol offset and any displacement required...
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:1091
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.
QgsUnitTypes::DistanceUnit mapUnits() const
Retrieve map units.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void setStrokeColor(const QColor &color) override
Set stroke color.
void copyDataDefinedProperties(QgsSymbolLayer *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
QgsUnitTypes::RenderUnit mOffsetUnit
Offset units.
double updateDefaultAspectRatio()
Calculates the default marker aspect ratio between width and height.
void setColor(const QColor &color) override
The fill color.
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties...
void stopRender(QgsSymbolRenderContext &context) override
QgsSymbol::ScaleMethod scaleMethod() const
Returns the method to use for scaling the marker&#39;s size.
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()
virtual bool hasDataDefinedProperties() const
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties...
Calculate scale by the area.
Definition: qgssymbol.h:96
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
bool forceVectorOutput() const
Returns TRUE if rendering operations should use vector operations instead of any faster raster shortc...
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.
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.
#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 mapUnitsPerPixel() const
Returns current map units per pixel.
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...
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.
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)
bool shapeToPolygon(Shape shape, QPolygonF &polygon) const
Creates a polygon representing the specified shape.
double strokeWidth() const
Returns the width of the marker&#39;s stroke.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void startRender(QgsSymbolRenderContext &context) override
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
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.
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...
VerticalAnchorPoint
Symbol vertical anchor points.
QgsFields fields() const
Fields of the layer.
Definition: qgssymbol.h:653
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()
virtual QColor fillColor() const
Gets fill color.
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
QgsExpressionContext & expressionContext()
Gets the expression context.
qreal opacity() const
Returns the opacity for the symbol.
Definition: qgssymbol.h:602
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
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
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.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsFontMarkerSymbolLayer from an SLD XML element.
Qt::PenStyle strokeStyle() const
Returns the marker&#39;s stroke style (e.g., solid, dashed, etc)
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...
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol&#39;s size.
Stroke style (eg solid, dashed)
#define DEFAULT_SIMPLEMARKER_BORDERCOLOR
double calculateAspectRatio(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio) const
Calculates the marker aspect ratio between width and height.
Fill symbol.
Definition: qgssymbol.h:87
Contains information about the context of a rendering operation.
Abstract base class for marker symbol layers.
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.
QColor color() const override
The fill color.
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.
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns the set of attributes referenced by the layer.
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...
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:197
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.
const QgsFeature * feature() const
Current feature being rendered - may be null.
Definition: qgssymbol.h:628
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)
SymbolType type() const
Returns the symbol&#39;s type.
Definition: qgssymbol.h:120
void writeSldMarker(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
Writes the symbol layer definition as a SLD XML element.
Flags flags() const
Returns combination of flags used for rendering.
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)
const QgsMapToPixel & mapToPixel() const
Returns the context&#39;s map to pixel transform, which transforms between map coordinates and device coo...
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
Qt::PenJoinStyle penJoinStyle() const
Returns the marker&#39;s stroke join style (e.g., miter, bevel, etc).
bool writeDxf(QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift=QPointF(0.0, 0.0)) const override
write as DXF
void copyPaintEffect(QgsSymbolLayer *destLayer) const
Copies paint effect of this layer to another symbol layer.
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.
bool selected() const
Definition: qgssymbol.h:611
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.
QgsSimpleMarkerSymbolLayerBase::Shape shape() const
Returns the shape for the rendered marker symbol.
#define DEFAULT_SVGMARKER_SIZE
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.
const QgsPathResolver & pathResolver() const
Returns the path resolver for conversion between relative and absolute paths during rendering operati...
#define DEFAULT_SVGMARKER_ANGLE
virtual QColor color() const
The fill color.
QgsWkbTypes::GeometryType type() const
Returns type of the geometry as a QgsWkbTypes::GeometryType.
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 setOutputSize(const QRectF &r)
void setVerticalAnchorPoint(VerticalAnchorPoint v)
Sets the vertical anchor point for positioning the symbol.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...