QGIS API Documentation  3.2.0-Bonn (bc43194)
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( "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  return m;
1206 }
1207 
1209 {
1210  Q_UNUSED( context );
1211 
1212  if ( mPolygon.count() != 0 )
1213  {
1214  p->drawPolygon( mPolygon );
1215  }
1216  else
1217  {
1218  p->drawPath( mPath );
1219  }
1220 }
1221 
1222 bool QgsSimpleMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
1223 {
1224  Q_UNUSED( mmMapUnitScaleFactor );
1225 
1226  //data defined size?
1227  double size = mSize;
1228 
1229  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
1230 
1231  //data defined size
1232  bool ok = true;
1233  if ( hasDataDefinedSize )
1234  {
1236 
1237  if ( ok )
1238  {
1239  switch ( mScaleMethod )
1240  {
1241  case QgsSymbol::ScaleArea:
1242  size = std::sqrt( size );
1243  break;
1245  break;
1246  }
1247  }
1248 
1250  }
1252  {
1254  }
1255  double halfSize = size / 2.0;
1256 
1257  //strokeWidth
1258  double strokeWidth = mStrokeWidth;
1259 
1261  {
1264  }
1267  {
1269  }
1270 
1271  //color
1272  QColor pc = mPen.color();
1273  QColor bc = mBrush.color();
1275  {
1278  }
1280  {
1283  }
1284 
1285  //offset
1286  double offsetX = 0;
1287  double offsetY = 0;
1288  markerOffset( context, offsetX, offsetY );
1289  offsetX *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1290  offsetY *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1291 
1292 
1293  QPointF off( offsetX, offsetY );
1294 
1295  //angle
1296  double angle = mAngle + mLineAngle;
1298  {
1299  context.setOriginalValueVariable( mAngle );
1301  }
1302 
1303  Shape shape = mShape;
1305  {
1306  context.setOriginalValueVariable( encodeShape( shape ) );
1307  QString shapeName = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyName, context.renderContext().expressionContext(), QString(), &ok );
1308  if ( ok )
1309  {
1310  shape = decodeShape( shapeName, &ok );
1311  if ( !ok )
1312  shape = mShape;
1313  }
1314  }
1315 
1316  if ( angle )
1317  off = _rotatedOffset( off, angle );
1318 
1320 
1321  QTransform t;
1322  t.translate( shift.x() + off.x(), shift.y() - off.y() );
1323 
1324  if ( !qgsDoubleNear( angle, 0.0 ) )
1325  t.rotate( angle );
1326 
1327  QPolygonF polygon;
1328  if ( shapeToPolygon( shape, polygon ) )
1329  {
1330  t.scale( halfSize, -halfSize );
1331 
1332  polygon = t.map( polygon );
1333 
1334  QgsPointSequence p;
1335  p.reserve( polygon.size() );
1336  for ( int i = 0; i < polygon.size(); i++ )
1337  {
1338  p << QgsPoint( polygon[i] );
1339  }
1340 
1341  if ( mBrush.style() != Qt::NoBrush )
1342  e.writePolygon( QgsRingSequence() << p, layerName, QStringLiteral( "SOLID" ), bc );
1343  if ( mPen.style() != Qt::NoPen )
1344  e.writePolyline( p, layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1345  }
1346  else if ( shape == Circle )
1347  {
1348  shift += QPointF( off.x(), -off.y() );
1349  if ( mBrush.style() != Qt::NoBrush )
1350  e.writeFilledCircle( layerName, bc, QgsPoint( shift ), halfSize );
1351  if ( mPen.style() != Qt::NoPen )
1352  e.writeCircle( layerName, pc, QgsPoint( shift ), halfSize, QStringLiteral( "CONTINUOUS" ), strokeWidth );
1353  }
1354  else if ( shape == Line )
1355  {
1356  QPointF pt1 = t.map( QPointF( 0, -halfSize ) );
1357  QPointF pt2 = t.map( QPointF( 0, halfSize ) );
1358 
1359  if ( mPen.style() != Qt::NoPen )
1360  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1361  }
1362  else if ( shape == Cross )
1363  {
1364  if ( mPen.style() != Qt::NoPen )
1365  {
1366  QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
1367  QPointF pt2 = t.map( QPointF( halfSize, 0 ) );
1368  QPointF pt3 = t.map( QPointF( 0, -halfSize ) );
1369  QPointF pt4 = t.map( QPointF( 0, halfSize ) );
1370 
1371  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1372  e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1373  }
1374  }
1375  else if ( shape == Cross2 )
1376  {
1377  if ( mPen.style() != Qt::NoPen )
1378  {
1379  QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
1380  QPointF pt2 = t.map( QPointF( halfSize, halfSize ) );
1381  QPointF pt3 = t.map( QPointF( halfSize, -halfSize ) );
1382  QPointF pt4 = t.map( QPointF( -halfSize, halfSize ) );
1383 
1384  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1385  e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1386  }
1387  }
1388  else if ( shape == ArrowHead )
1389  {
1390  if ( mPen.style() != Qt::NoPen )
1391  {
1392  QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
1393  QPointF pt2 = t.map( QPointF( 0, 0 ) );
1394  QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
1395 
1396  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1397  e.writeLine( QgsPoint( pt3 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1398  }
1399  }
1400  else
1401  {
1402  QgsDebugMsg( QString( "Unsupported dxf marker name %1" ).arg( encodeShape( shape ) ) );
1403  return false;
1404  }
1405 
1406  return true;
1407 }
1408 
1409 
1411 {
1413  mStrokeWidthUnit = unit;
1414 }
1415 
1417 {
1419  {
1420  return mStrokeWidthUnit;
1421  }
1423 }
1424 
1426 {
1428  mStrokeWidthMapUnitScale = scale;
1429 }
1430 
1432 {
1434  {
1435  return mStrokeWidthMapUnitScale;
1436  }
1437  return QgsMapUnitScale();
1438 }
1439 
1441 {
1442  QRectF symbolBounds = QgsSimpleMarkerSymbolLayerBase::bounds( point, context );
1443 
1444  // need to account for stroke width
1445  double penWidth = 0.0;
1446  bool ok = true;
1448  {
1451  if ( ok )
1452  {
1453  penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
1454  }
1455  }
1457  {
1460  if ( ok && strokeStyle == QLatin1String( "no" ) )
1461  {
1462  penWidth = 0.0;
1463  }
1464  }
1465  //antialiasing, add 1 pixel
1466  penWidth += 1;
1467 
1468  //extend bounds by pen width / 2.0
1469  symbolBounds.adjust( -penWidth / 2.0, -penWidth / 2.0,
1470  penWidth / 2.0, penWidth / 2.0 );
1471 
1472  return symbolBounds;
1473 }
1474 
1476 {
1477  if ( shapeIsFilled( mShape ) )
1478  {
1479  setFillColor( color );
1480  }
1481  else
1482  {
1483  setStrokeColor( color );
1484  }
1485 }
1486 
1488 {
1489  if ( shapeIsFilled( mShape ) )
1490  {
1491  return fillColor();
1492  }
1493  else
1494  {
1495  return strokeColor();
1496  }
1497 }
1498 
1499 
1500 
1501 
1502 //
1503 // QgsFilledMarkerSymbolLayer
1504 //
1505 
1507  : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
1508 {
1509  mFill.reset( static_cast<QgsFillSymbol *>( QgsFillSymbol::createSimple( QgsStringMap() ) ) );
1510 }
1511 
1513 {
1514  QString name = DEFAULT_SIMPLEMARKER_NAME;
1518 
1519  if ( props.contains( QStringLiteral( "name" ) ) )
1520  name = props[QStringLiteral( "name" )];
1521  if ( props.contains( QStringLiteral( "size" ) ) )
1522  size = props[QStringLiteral( "size" )].toDouble();
1523  if ( props.contains( QStringLiteral( "angle" ) ) )
1524  angle = props[QStringLiteral( "angle" )].toDouble();
1525  if ( props.contains( QStringLiteral( "scale_method" ) ) )
1526  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )] );
1527 
1528  QgsFilledMarkerSymbolLayer *m = new QgsFilledMarkerSymbolLayer( decodeShape( name ), size, angle, scaleMethod );
1529  if ( props.contains( QStringLiteral( "offset" ) ) )
1530  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
1531  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1532  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
1533  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1534  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
1535  if ( props.contains( QStringLiteral( "size_unit" ) ) )
1536  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
1537  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1538  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
1539  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1540  {
1541  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1542  }
1543  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1544  {
1545  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1546  }
1547 
1549 
1550  m->restoreOldDataDefinedProperties( props );
1551 
1552  return m;
1553 }
1554 
1556 {
1557  return QStringLiteral( "FilledMarker" );
1558 }
1559 
1561 {
1562  if ( mFill )
1563  {
1564  mFill->startRender( context.renderContext(), context.fields() );
1565  }
1566 
1568 }
1569 
1571 {
1572  if ( mFill )
1573  {
1574  mFill->stopRender( context.renderContext() );
1575  }
1576 }
1577 
1579 {
1580  QgsStringMap map;
1581  map[QStringLiteral( "name" )] = encodeShape( mShape );
1582  map[QStringLiteral( "size" )] = QString::number( mSize );
1583  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1584  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1585  map[QStringLiteral( "angle" )] = QString::number( mAngle );
1586  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1587  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1588  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1589  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1590  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1591  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1592 
1593  if ( mFill )
1594  {
1595  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mFill->color() );
1596  }
1597  return map;
1598 }
1599 
1601 {
1603  copyPaintEffect( m );
1605  m->setSubSymbol( mFill->clone() );
1606  return m;
1607 }
1608 
1610 {
1611  return mFill.get();
1612 }
1613 
1615 {
1616  if ( symbol && symbol->type() == QgsSymbol::Fill )
1617  {
1618  mFill.reset( static_cast<QgsFillSymbol *>( symbol ) );
1619  return true;
1620  }
1621  else
1622  {
1623  delete symbol;
1624  return false;
1625  }
1626 }
1627 
1629 {
1630  if ( mFill )
1631  {
1632  return QgsSymbolLayerUtils::estimateMaxSymbolBleed( mFill.get(), context );
1633  }
1634  return 0;
1635 }
1636 
1638 {
1639  QSet<QString> attr = QgsSimpleMarkerSymbolLayerBase::usedAttributes( context );
1640  if ( mFill )
1641  attr.unite( mFill->usedAttributes( context ) );
1642  return attr;
1643 }
1644 
1646 {
1647  mColor = c;
1648  if ( mFill )
1649  mFill->setColor( c );
1650 }
1651 
1653 {
1654  return mFill ? mFill->color() : mColor;
1655 }
1656 
1657 void QgsFilledMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, QgsSimpleMarkerSymbolLayerBase::Shape shape, const QPolygonF &polygon, const QPainterPath &path )
1658 {
1659  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1660  //of the rendered point!
1661 
1662  QPainter *p = context.renderContext().painter();
1663  if ( !p )
1664  {
1665  return;
1666  }
1667 
1668  if ( shapeIsFilled( shape ) )
1669  {
1670  p->setBrush( Qt::red );
1671  }
1672  else
1673  {
1674  p->setBrush( Qt::NoBrush );
1675  }
1676  p->setPen( Qt::black );
1677 
1678  if ( !polygon.isEmpty() )
1679  {
1680  mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
1681  }
1682  else
1683  {
1684  QPolygonF poly = path.toFillPolygon();
1685  mFill->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
1686  }
1687 
1688 
1689 }
1690 
1691 
1693 
1694 
1696 {
1697  mPath = path;
1698  mSize = size;
1699  mAngle = angle;
1700  mOffset = QPointF( 0, 0 );
1702  mStrokeWidth = 0.2;
1703  mStrokeWidthUnit = QgsUnitTypes::RenderMillimeters;
1704  mColor = QColor( 35, 35, 35 );
1705  mStrokeColor = QColor( 35, 35, 35 );
1706  updateDefaultAspectRatio();
1707 }
1708 
1709 
1711 {
1712  QString name;
1713  double size = DEFAULT_SVGMARKER_SIZE;
1714  double angle = DEFAULT_SVGMARKER_ANGLE;
1716 
1717  if ( props.contains( QStringLiteral( "name" ) ) )
1718  name = props[QStringLiteral( "name" )];
1719  if ( props.contains( QStringLiteral( "size" ) ) )
1720  size = props[QStringLiteral( "size" )].toDouble();
1721  if ( props.contains( QStringLiteral( "angle" ) ) )
1722  angle = props[QStringLiteral( "angle" )].toDouble();
1723  if ( props.contains( QStringLiteral( "scale_method" ) ) )
1724  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )] );
1725 
1726  QgsSvgMarkerSymbolLayer *m = new QgsSvgMarkerSymbolLayer( name, size, angle, scaleMethod );
1727 
1728  //we only check the svg default parameters if necessary, since it could be expensive
1729  if ( !props.contains( QStringLiteral( "fill" ) ) && !props.contains( QStringLiteral( "color" ) ) && !props.contains( QStringLiteral( "outline" ) ) &&
1730  !props.contains( QStringLiteral( "outline_color" ) ) && !props.contains( QStringLiteral( "outline-width" ) ) && !props.contains( QStringLiteral( "outline_width" ) ) )
1731  {
1732  QColor fillColor, strokeColor;
1733  double fillOpacity = 1.0;
1734  double strokeOpacity = 1.0;
1735  double strokeWidth;
1736  bool hasFillParam = false, hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
1737  bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
1738  QgsApplication::svgCache()->containsParams( name, hasFillParam, hasDefaultFillColor, fillColor,
1739  hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
1740  hasStrokeParam, hasDefaultStrokeColor, strokeColor,
1741  hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
1742  hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
1743  if ( hasDefaultFillColor )
1744  {
1745  m->setFillColor( fillColor );
1746  }
1747  if ( hasDefaultFillOpacity )
1748  {
1749  QColor c = m->fillColor();
1750  c.setAlphaF( fillOpacity );
1751  m->setFillColor( c );
1752  }
1753  if ( hasDefaultStrokeColor )
1754  {
1755  m->setStrokeColor( strokeColor );
1756  }
1757  if ( hasDefaultStrokeWidth )
1758  {
1759  m->setStrokeWidth( strokeWidth );
1760  }
1761  if ( hasDefaultStrokeOpacity )
1762  {
1763  QColor c = m->strokeColor();
1764  c.setAlphaF( strokeOpacity );
1765  m->setStrokeColor( c );
1766  }
1767  }
1768 
1769  if ( props.contains( QStringLiteral( "size_unit" ) ) )
1770  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
1771  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1772  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
1773  if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
1774  m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
1775  if ( props.contains( QStringLiteral( "offset" ) ) )
1776  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
1777  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1778  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
1779  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1780  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
1781  if ( props.contains( QStringLiteral( "fill" ) ) )
1782  {
1783  //pre 2.5 projects used "fill"
1784  m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "fill" )] ) );
1785  }
1786  else if ( props.contains( QStringLiteral( "color" ) ) )
1787  {
1788  m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] ) );
1789  }
1790  if ( props.contains( QStringLiteral( "outline" ) ) )
1791  {
1792  //pre 2.5 projects used "outline"
1793  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline" )] ) );
1794  }
1795  else if ( props.contains( QStringLiteral( "outline_color" ) ) )
1796  {
1797  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )] ) );
1798  }
1799  else if ( props.contains( QStringLiteral( "line_color" ) ) )
1800  {
1801  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )] ) );
1802  }
1803 
1804  if ( props.contains( QStringLiteral( "outline-width" ) ) )
1805  {
1806  //pre 2.5 projects used "outline-width"
1807  m->setStrokeWidth( props[QStringLiteral( "outline-width" )].toDouble() );
1808  }
1809  else if ( props.contains( QStringLiteral( "outline_width" ) ) )
1810  {
1811  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
1812  }
1813  else if ( props.contains( QStringLiteral( "line_width" ) ) )
1814  {
1815  m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
1816  }
1817 
1818  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
1819  {
1820  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )] ) );
1821  }
1822  else if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
1823  {
1824  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )] ) );
1825  }
1826  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
1827  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )] ) );
1828 
1829  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1830  {
1831  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1832  }
1833  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1834  {
1835  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1836  }
1837 
1838  m->restoreOldDataDefinedProperties( props );
1839 
1841 
1842  return m;
1843 }
1844 
1846 {
1847  QgsStringMap::iterator it = properties.find( QStringLiteral( "name" ) );
1848  if ( it != properties.end() )
1849  {
1850  if ( saving )
1851  it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value(), pathResolver );
1852  else
1853  it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value(), pathResolver );
1854  }
1855 }
1856 
1857 void QgsSvgMarkerSymbolLayer::setPath( const QString &path )
1858 {
1859  mPath = path;
1860  QColor defaultFillColor, defaultStrokeColor;
1861  double strokeWidth, fillOpacity, strokeOpacity;
1862  bool hasFillParam = false, hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
1863  bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
1864  QgsApplication::svgCache()->containsParams( path, hasFillParam, hasDefaultFillColor, defaultFillColor,
1865  hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
1866  hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
1867  hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
1868  hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
1869 
1870  double newFillOpacity = hasFillOpacityParam ? fillColor().alphaF() : 1.0;
1871  double newStrokeOpacity = hasStrokeOpacityParam ? strokeColor().alphaF() : 1.0;
1872 
1873  if ( hasDefaultFillColor )
1874  {
1875  defaultFillColor.setAlphaF( newFillOpacity );
1876  setFillColor( defaultFillColor );
1877  }
1878  if ( hasDefaultFillOpacity )
1879  {
1880  QColor c = fillColor();
1881  c.setAlphaF( fillOpacity );
1882  setFillColor( c );
1883  }
1884  if ( hasDefaultStrokeColor )
1885  {
1886  defaultStrokeColor.setAlphaF( newStrokeOpacity );
1887  setStrokeColor( defaultStrokeColor );
1888  }
1889  if ( hasDefaultStrokeWidth )
1890  {
1891  setStrokeWidth( strokeWidth );
1892  }
1893  if ( hasDefaultStrokeOpacity )
1894  {
1895  QColor c = strokeColor();
1896  c.setAlphaF( strokeOpacity );
1897  setStrokeColor( c );
1898  }
1899 
1900  updateDefaultAspectRatio();
1901 }
1902 
1904 {
1905  if ( mDefaultAspectRatio == 0.0 )
1906  {
1907  //size
1908  double size = mSize;
1909  //assume 88 dpi as standard value
1910  double widthScaleFactor = 3.465;
1911  QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( mPath, size, mColor, mStrokeColor, mStrokeWidth, widthScaleFactor );
1912  // set default aspect ratio
1913  mDefaultAspectRatio = svgViewbox.isValid() ? svgViewbox.height() / svgViewbox.width() : 0.0;
1914  }
1915  return mDefaultAspectRatio;
1916 }
1917 
1919 {
1920  bool aPreservedAspectRatio = preservedAspectRatio();
1921  if ( aPreservedAspectRatio && !par )
1922  {
1923  mFixedAspectRatio = mDefaultAspectRatio;
1924  }
1925  else if ( !aPreservedAspectRatio && par )
1926  {
1927  mFixedAspectRatio = 0.0;
1928  }
1929  return preservedAspectRatio();
1930 }
1931 
1932 
1934 {
1935  return QStringLiteral( "SvgMarker" );
1936 }
1937 
1939 {
1940  QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
1941  Q_UNUSED( context );
1942 }
1943 
1945 {
1946  Q_UNUSED( context );
1947 }
1948 
1950 {
1951  QPainter *p = context.renderContext().painter();
1952  if ( !p )
1953  return;
1954 
1955  bool hasDataDefinedSize = false;
1956  double scaledSize = calculateSize( context, hasDataDefinedSize );
1957  double size = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
1958 
1959  //don't render symbols with size below one or above 10,000 pixels
1960  if ( static_cast< int >( size ) < 1 || 10000.0 < size )
1961  {
1962  return;
1963  }
1964 
1965  p->save();
1966 
1967  bool hasDataDefinedAspectRatio = false;
1968  double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
1969 
1970  QPointF outputOffset;
1971  double angle = 0.0;
1972  calculateOffsetAndRotation( context, scaledSize, outputOffset, angle );
1973 
1974  p->translate( point + outputOffset );
1975 
1976  bool rotated = !qgsDoubleNear( angle, 0 );
1977  if ( rotated )
1978  p->rotate( angle );
1979 
1980  QString path = mPath;
1982  {
1983  context.setOriginalValueVariable( mPath );
1985  context.renderContext().pathResolver() );
1986  }
1987 
1988  double strokeWidth = mStrokeWidth;
1990  {
1991  context.setOriginalValueVariable( mStrokeWidth );
1993  }
1994  strokeWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
1995 
1996  QColor fillColor = mColor;
1998  {
2001  }
2002 
2003  QColor strokeColor = mStrokeColor;
2005  {
2008  }
2009 
2010  bool fitsInCache = true;
2011  bool usePict = true;
2012  double hwRatio = 1.0;
2013  if ( !context.renderContext().forceVectorOutput() && !rotated )
2014  {
2015  usePict = false;
2016  QImage img = QgsApplication::svgCache()->svgAsImage( path, size, fillColor, strokeColor, strokeWidth,
2017  context.renderContext().scaleFactor(), fitsInCache, aspectRatio );
2018  if ( fitsInCache && img.width() > 1 )
2019  {
2020  //consider transparency
2021  if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2022  {
2023  QImage transparentImage = img.copy();
2024  QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2025  p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
2026  hwRatio = static_cast< double >( transparentImage.height() ) / static_cast< double >( transparentImage.width() );
2027  }
2028  else
2029  {
2030  p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2031  hwRatio = static_cast< double >( img.height() ) / static_cast< double >( img.width() );
2032  }
2033  }
2034  }
2035 
2036  if ( usePict || !fitsInCache )
2037  {
2038  p->setOpacity( context.opacity() );
2039  QPicture pct = QgsApplication::svgCache()->svgAsPicture( path, size, fillColor, strokeColor, strokeWidth,
2040  context.renderContext().scaleFactor(), context.renderContext().forceVectorOutput(), aspectRatio );
2041  if ( pct.width() > 1 )
2042  {
2043  p->save();
2044  _fixQPictureDPI( p );
2045  p->drawPicture( 0, 0, pct );
2046  p->restore();
2047  hwRatio = static_cast< double >( pct.height() ) / static_cast< double >( pct.width() );
2048  }
2049  }
2050 
2051  if ( context.selected() )
2052  {
2053  QPen pen( context.renderContext().selectionColor() );
2054  double penWidth = context.renderContext().convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters );
2055  if ( penWidth > size / 20 )
2056  {
2057  // keep the pen width from covering symbol
2058  penWidth = size / 20;
2059  }
2060  double penOffset = penWidth / 2;
2061  pen.setWidth( penWidth );
2062  p->setPen( pen );
2063  p->setBrush( Qt::NoBrush );
2064  double wSize = size + penOffset;
2065  double hSize = size * hwRatio + penOffset;
2066  p->drawRect( QRectF( -wSize / 2.0, -hSize / 2.0, wSize, hSize ) );
2067  }
2068 
2069  p->restore();
2070 
2072  {
2073  // workaround issue with nested QPictures forgetting antialiasing flag - see https://issues.qgis.org/issues/14960
2074  p->setRenderHint( QPainter::Antialiasing );
2075  }
2076 
2077 }
2078 
2079 double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2080 {
2081  double scaledSize = mSize;
2083 
2084  bool ok = true;
2085  if ( hasDataDefinedSize )
2086  {
2087  context.setOriginalValueVariable( mSize );
2089  }
2090  else
2091  {
2093  if ( hasDataDefinedSize )
2094  {
2095  context.setOriginalValueVariable( mSize );
2097  }
2098  }
2099 
2100  if ( hasDataDefinedSize && ok )
2101  {
2102  switch ( mScaleMethod )
2103  {
2104  case QgsSymbol::ScaleArea:
2105  scaledSize = std::sqrt( scaledSize );
2106  break;
2108  break;
2109  }
2110  }
2111 
2112  return scaledSize;
2113 }
2114 
2115 double QgsSvgMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2116 {
2118  if ( !hasDataDefinedAspectRatio )
2119  return mFixedAspectRatio;
2120 
2121  if ( !mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyHeight ) && mFixedAspectRatio <= 0.0 )
2122  return 0.0;
2123 
2124  double scaledAspectRatio = mDefaultAspectRatio;
2125  if ( mFixedAspectRatio > 0.0 )
2126  scaledAspectRatio = mFixedAspectRatio;
2127 
2128  double defaultHeight = mSize * scaledAspectRatio;
2129  scaledAspectRatio = defaultHeight / scaledSize;
2130 
2131  bool ok = true;
2132  double scaledHeight = scaledSize * scaledAspectRatio;
2134  {
2135  context.setOriginalValueVariable( defaultHeight );
2136  scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyHeight, context.renderContext().expressionContext(), defaultHeight, &ok );
2137  }
2138 
2139  if ( hasDataDefinedAspectRatio && ok )
2140  {
2141  switch ( mScaleMethod )
2142  {
2143  case QgsSymbol::ScaleArea:
2144  scaledHeight = sqrt( scaledHeight );
2145  break;
2147  break;
2148  }
2149  }
2150 
2151  scaledAspectRatio = scaledHeight / scaledSize;
2152 
2153  return scaledAspectRatio;
2154 }
2155 
2156 void QgsSvgMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledSize, QPointF &offset, double &angle ) const
2157 {
2158  //offset
2159  double offsetX = 0;
2160  double offsetY = 0;
2161  markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
2162  offset = QPointF( offsetX, offsetY );
2163 
2164  angle = mAngle + mLineAngle;
2166  {
2167  context.setOriginalValueVariable( mAngle );
2169  }
2170 
2172  if ( hasDataDefinedRotation )
2173  {
2174  // For non-point markers, "dataDefinedRotation" means following the
2175  // shape (shape-data defined). For them, "field-data defined" does
2176  // not work at all. TODO: if "field-data defined" ever gets implemented
2177  // we'll need a way to distinguish here between the two, possibly
2178  // using another flag in renderHints()
2179  const QgsFeature *f = context.feature();
2180  if ( f )
2181  {
2182  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
2183  {
2184  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2185  angle += m2p.mapRotation();
2186  }
2187  }
2188  }
2189 
2190  if ( angle )
2191  offset = _rotatedOffset( offset, angle );
2192 }
2193 
2194 
2196 {
2197  QgsStringMap map;
2198  map[QStringLiteral( "name" )] = mPath;
2199  map[QStringLiteral( "size" )] = QString::number( mSize );
2200  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2201  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2202  map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2203  map[QStringLiteral( "angle" )] = QString::number( mAngle );
2204  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2205  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2206  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2207  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2208  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
2209  map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
2210  map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
2211  map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2212  map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2213  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
2214  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
2215  return map;
2216 }
2217 
2219 {
2221  m->setFixedAspectRatio( mFixedAspectRatio );
2222  m->setColor( mColor );
2223  m->setStrokeColor( mStrokeColor );
2224  m->setStrokeWidth( mStrokeWidth );
2225  m->setStrokeWidthUnit( mStrokeWidthUnit );
2226  m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
2227  m->setOffset( mOffset );
2228  m->setOffsetUnit( mOffsetUnit );
2230  m->setSizeUnit( mSizeUnit );
2235  copyPaintEffect( m );
2236  return m;
2237 }
2238 
2240 {
2242  mStrokeWidthUnit = unit;
2243 }
2244 
2246 {
2248  if ( unit != mStrokeWidthUnit )
2249  {
2251  }
2252  return unit;
2253 }
2254 
2256 {
2258  mStrokeWidthMapUnitScale = scale;
2259 }
2260 
2262 {
2263  if ( QgsMarkerSymbolLayer::mapUnitScale() == mStrokeWidthMapUnitScale )
2264  {
2265  return mStrokeWidthMapUnitScale;
2266  }
2267  return QgsMapUnitScale();
2268 }
2269 
2270 void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
2271 {
2272  // <Graphic>
2273  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2274  element.appendChild( graphicElem );
2275 
2276  // encode a parametric SVG reference
2278  double strokeWidth = QgsSymbolLayerUtils::rescaleUom( mStrokeWidth, mStrokeWidthUnit, props );
2279  QgsSymbolLayerUtils::parametricSvgToSld( doc, graphicElem, mPath, mColor, size, mStrokeColor, strokeWidth );
2280 
2281  // <Rotation>
2282  QString angleFunc;
2283  bool ok;
2284  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2285  if ( !ok )
2286  {
2287  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mAngle );
2288  }
2289  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2290  {
2291  angleFunc = QString::number( angle + mAngle );
2292  }
2293 
2294  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2295 
2296  // <Displacement>
2297  QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2298  QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
2299 }
2300 
2302 {
2303  QgsDebugMsg( "Entered." );
2304 
2305  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
2306  if ( graphicElem.isNull() )
2307  return nullptr;
2308 
2309  QString path, mimeType;
2310  QColor fillColor;
2311  double size;
2312 
2313  if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
2314  return nullptr;
2315 
2316  QString uom = element.attribute( QStringLiteral( "uom" ) );
2317  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
2318 
2319  if ( mimeType != QLatin1String( "image/svg+xml" ) )
2320  return nullptr;
2321 
2322  double angle = 0.0;
2323  QString angleFunc;
2324  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2325  {
2326  bool ok;
2327  double d = angleFunc.toDouble( &ok );
2328  if ( ok )
2329  angle = d;
2330  }
2331 
2332  QPointF offset;
2333  QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, offset );
2334 
2335  QgsSvgMarkerSymbolLayer *m = new QgsSvgMarkerSymbolLayer( path, size );
2336  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
2337  m->setFillColor( fillColor );
2338  //m->setStrokeColor( strokeColor );
2339  //m->setStrokeWidth( strokeWidth );
2340  m->setAngle( angle );
2341  m->setOffset( offset );
2342  return m;
2343 }
2344 
2345 bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
2346 {
2347  Q_UNUSED( layerName );
2348  Q_UNUSED( shift ); //todo...
2349 
2350  //size
2351  double size = mSize;
2352 
2353  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
2354 
2355  bool ok = true;
2356  if ( hasDataDefinedSize )
2357  {
2358  context.setOriginalValueVariable( mSize );
2360  }
2361 
2362  if ( hasDataDefinedSize && ok )
2363  {
2364  switch ( mScaleMethod )
2365  {
2366  case QgsSymbol::ScaleArea:
2367  size = std::sqrt( size );
2368  break;
2370  break;
2371  }
2372  }
2373 
2375  {
2376  size *= mmMapUnitScaleFactor;
2377  }
2378 
2379  double halfSize = size / 2.0;
2380 
2381  //offset, angle
2382  QPointF offset = mOffset;
2383 
2385  {
2387  QString offsetString = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext(), QString(), &ok );
2388  if ( ok )
2389  offset = QgsSymbolLayerUtils::decodePoint( offsetString );
2390  }
2391  double offsetX = offset.x();
2392  double offsetY = offset.y();
2393 
2394  QPointF outputOffset( offsetX, offsetY );
2395 
2396  double angle = mAngle + mLineAngle;
2398  {
2399  context.setOriginalValueVariable( mAngle );
2401  }
2402  //angle = -angle; //rotation in Qt is counterclockwise
2403  if ( angle )
2404  outputOffset = _rotatedOffset( outputOffset, angle );
2405 
2406  outputOffset *= e.mapUnitScaleFactor( e.symbologyScale(), mOffsetUnit, e.mapUnits(), context.renderContext().mapToPixel().mapUnitsPerPixel() );
2407 
2408  QString path = mPath;
2410  {
2411  context.setOriginalValueVariable( mPath );
2413  context.renderContext().pathResolver() );
2414  }
2415 
2416  double strokeWidth = mStrokeWidth;
2418  {
2419  context.setOriginalValueVariable( mStrokeWidth );
2421  }
2422  strokeWidth *= e.mapUnitScaleFactor( e.symbologyScale(), mStrokeWidthUnit, e.mapUnits(), context.renderContext().mapToPixel().mapUnitsPerPixel() );
2423 
2424  QColor fillColor = mColor;
2426  {
2429  }
2430 
2431  QColor strokeColor = mStrokeColor;
2433  {
2436  }
2437 
2438  const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, size, fillColor, strokeColor, strokeWidth,
2439  context.renderContext().scaleFactor(), mFixedAspectRatio );
2440 
2441  //if current entry image is 0: cache image for entry
2442  // checks to see if image will fit into cache
2443  //update stats for memory usage
2444  QSvgRenderer r( svgContent );
2445  if ( !r.isValid() )
2446  {
2447  return false;
2448  }
2449 
2450  QgsDxfPaintDevice pd( &e );
2451  pd.setDrawingSize( QSizeF( r.defaultSize() ) );
2452 
2453  QPainter p;
2454  p.begin( &pd );
2455  if ( !qgsDoubleNear( angle, 0.0 ) )
2456  {
2457  p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
2458  p.rotate( angle );
2459  p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
2460  }
2461  pd.setShift( shift + QPointF( outputOffset.x(), -outputOffset.y() ) );
2462  pd.setOutputSize( QRectF( -halfSize, -halfSize, size, size ) );
2463  pd.setLayer( layerName );
2464  r.render( &p );
2465  p.end();
2466  return true;
2467 }
2468 
2470 {
2471  bool hasDataDefinedSize = false;
2472  double scaledSize = calculateSize( context, hasDataDefinedSize );
2473  scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
2474 
2475  //don't render symbols with size below one or above 10,000 pixels
2476  if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
2477  {
2478  return QRectF();
2479  }
2480 
2481  QPointF outputOffset;
2482  double angle = 0.0;
2483  calculateOffsetAndRotation( context, scaledSize, outputOffset, angle );
2484 
2485  QString path = mPath;
2487  {
2488  context.setOriginalValueVariable( mPath );
2490  context.renderContext().pathResolver() );
2491  }
2492 
2493  double strokeWidth = mStrokeWidth;
2495  {
2496  context.setOriginalValueVariable( mStrokeWidth );
2498  }
2499  strokeWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
2500 
2501  //need to get colors to take advantage of cached SVGs
2502  QColor fillColor = mColor;
2504  {
2507  }
2508 
2509  QColor strokeColor = mStrokeColor;
2511  {
2514  }
2515 
2516  QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledSize, fillColor, strokeColor, strokeWidth,
2517  context.renderContext().scaleFactor(), mFixedAspectRatio );
2518 
2519  double scaledHeight = svgViewbox.isValid() ? scaledSize * svgViewbox.height() / svgViewbox.width() : scaledSize;
2520 
2521  QMatrix transform;
2522 
2523  // move to the desired position
2524  transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
2525 
2526  if ( !qgsDoubleNear( angle, 0.0 ) )
2527  transform.rotate( angle );
2528 
2529  //antialiasing
2530  strokeWidth += 1.0 / 2.0;
2531 
2532  QRectF symbolBounds = transform.mapRect( QRectF( -scaledSize / 2.0,
2533  -scaledHeight / 2.0,
2534  scaledSize,
2535  scaledHeight ) );
2536 
2537  //extend bounds by pen width / 2.0
2538  symbolBounds.adjust( -strokeWidth / 2.0, -strokeWidth / 2.0,
2539  strokeWidth / 2.0, strokeWidth / 2.0 );
2540 
2541  return symbolBounds;
2542 
2543 }
2544 
2546 
2547 QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QChar chr, double pointSize, const QColor &color, double angle )
2548 {
2549  mFontFamily = fontFamily;
2550  mChr = chr;
2551  mColor = color;
2552  mAngle = angle;
2553  mSize = pointSize;
2554  mOrigSize = pointSize;
2556  mOffset = QPointF( 0, 0 );
2558  mStrokeColor = DEFAULT_FONTMARKER_BORDERCOLOR;
2559  mStrokeWidth = 0.0;
2560  mStrokeWidthUnit = QgsUnitTypes::RenderMillimeters;
2561  mPenJoinStyle = DEFAULT_FONTMARKER_JOINSTYLE;
2562 }
2563 
2565 {
2566  delete mFontMetrics;
2567 }
2568 
2570 {
2571  QString fontFamily = DEFAULT_FONTMARKER_FONT;
2572  QChar chr = DEFAULT_FONTMARKER_CHR;
2573  double pointSize = DEFAULT_FONTMARKER_SIZE;
2575  double angle = DEFAULT_FONTMARKER_ANGLE;
2576 
2577  if ( props.contains( QStringLiteral( "font" ) ) )
2578  fontFamily = props[QStringLiteral( "font" )];
2579  if ( props.contains( QStringLiteral( "chr" ) ) && props[QStringLiteral( "chr" )].length() > 0 )
2580  chr = props[QStringLiteral( "chr" )].at( 0 );
2581  if ( props.contains( QStringLiteral( "size" ) ) )
2582  pointSize = props[QStringLiteral( "size" )].toDouble();
2583  if ( props.contains( QStringLiteral( "color" ) ) )
2584  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] );
2585  if ( props.contains( QStringLiteral( "angle" ) ) )
2586  angle = props[QStringLiteral( "angle" )].toDouble();
2587 
2588  QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( fontFamily, chr, pointSize, color, angle );
2589 
2590  if ( props.contains( QStringLiteral( "outline_color" ) ) )
2591  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )] ) );
2592  if ( props.contains( QStringLiteral( "outline_width" ) ) )
2593  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
2594  if ( props.contains( QStringLiteral( "offset" ) ) )
2595  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
2596  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
2597  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
2598  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2599  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
2600  if ( props.contains( QStringLiteral( "size_unit" ) ) )
2601  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
2602  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
2603  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
2604  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
2605  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )] ) );
2606  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
2607  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )] ) );
2608  if ( props.contains( QStringLiteral( "joinstyle" ) ) )
2609  m->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )] ) );
2610  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2611  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2612  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2613  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2614 
2615  m->restoreOldDataDefinedProperties( props );
2616 
2617  return m;
2618 }
2619 
2621 {
2622  return QStringLiteral( "FontMarker" );
2623 }
2624 
2626 {
2627  QColor brushColor = mColor;
2628  QColor penColor = mStrokeColor;
2629 
2630  brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
2631  penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
2632 
2633  mBrush = QBrush( brushColor );
2634  mPen = QPen( penColor );
2635  mPen.setJoinStyle( mPenJoinStyle );
2636  mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
2637 
2638  mFont = QFont( mFontFamily );
2639  mFont.setPixelSize( context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale ) );
2640  delete mFontMetrics;
2641  mFontMetrics = new QFontMetrics( mFont );
2642  mChrWidth = mFontMetrics->width( mChr );
2643  mChrOffset = QPointF( mChrWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
2644  mOrigSize = mSize; // save in case the size would be data defined
2645 }
2646 
2648 {
2649  Q_UNUSED( context );
2650 }
2651 
2652 QString QgsFontMarkerSymbolLayer::characterToRender( QgsSymbolRenderContext &context, QPointF &charOffset, double &charWidth )
2653 {
2654  charOffset = mChrOffset;
2655  QString charToRender = mChr;
2657  {
2658  context.setOriginalValueVariable( mChr );
2660  if ( charToRender != mChr )
2661  {
2662  charWidth = mFontMetrics->width( charToRender );
2663  charOffset = QPointF( charWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
2664  }
2665  }
2666  return charToRender;
2667 }
2668 
2669 void QgsFontMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
2670  double scaledSize,
2671  bool &hasDataDefinedRotation,
2672  QPointF &offset,
2673  double &angle ) const
2674 {
2675  //offset
2676  double offsetX = 0;
2677  double offsetY = 0;
2678  markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
2679  offset = QPointF( offsetX, offsetY );
2680 
2681  //angle
2682  bool ok = true;
2683  angle = mAngle + mLineAngle;
2684  bool usingDataDefinedRotation = false;
2686  {
2687  context.setOriginalValueVariable( angle );
2689  usingDataDefinedRotation = ok;
2690  }
2691 
2692  hasDataDefinedRotation = context.renderHints() & QgsSymbol::DynamicRotation || usingDataDefinedRotation;
2693  if ( hasDataDefinedRotation )
2694  {
2695  // For non-point markers, "dataDefinedRotation" means following the
2696  // shape (shape-data defined). For them, "field-data defined" does
2697  // not work at all. TODO: if "field-data defined" ever gets implemented
2698  // we'll need a way to distinguish here between the two, possibly
2699  // using another flag in renderHints()
2700  const QgsFeature *f = context.feature();
2701  if ( f )
2702  {
2703  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
2704  {
2705  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2706  angle += m2p.mapRotation();
2707  }
2708  }
2709  }
2710 
2711  if ( angle )
2712  offset = _rotatedOffset( offset, angle );
2713 }
2714 
2715 double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context )
2716 {
2717  double scaledSize = mSize;
2718  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
2719 
2720  bool ok = true;
2721  if ( hasDataDefinedSize )
2722  {
2723  context.setOriginalValueVariable( mSize );
2725  }
2726 
2727  if ( hasDataDefinedSize && ok )
2728  {
2729  switch ( mScaleMethod )
2730  {
2731  case QgsSymbol::ScaleArea:
2732  scaledSize = std::sqrt( scaledSize );
2733  break;
2735  break;
2736  }
2737  }
2738  return scaledSize;
2739 }
2740 
2742 {
2743  QPainter *p = context.renderContext().painter();
2744  if ( !p )
2745  return;
2746 
2747  QTransform transform;
2748 
2749  bool ok;
2750  QColor brushColor = mColor;
2752  {
2755  }
2756  brushColor = context.selected() ? context.renderContext().selectionColor() : brushColor;
2757  brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
2758  mBrush.setColor( brushColor );
2759 
2760  QColor penColor = mStrokeColor;
2762  {
2765  }
2766  penColor.setAlphaF( penColor.alphaF() * context.opacity() );
2767 
2768  double penWidth = context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
2770  {
2771  context.setOriginalValueVariable( mStrokeWidth );
2772  double strokeWidth = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyStrokeWidth, context.renderContext().expressionContext(), mStrokeWidth, &ok );
2773  if ( ok )
2774  {
2775  penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
2776  }
2777  }
2778 
2780  {
2783  if ( ok )
2784  {
2785  mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
2786  }
2787  }
2788 
2789  p->setBrush( mBrush );
2790  if ( !qgsDoubleNear( penWidth, 0.0 ) )
2791  {
2792  mPen.setColor( penColor );
2793  mPen.setWidthF( penWidth );
2794  p->setPen( mPen );
2795  }
2796  else
2797  {
2798  p->setPen( Qt::NoPen );
2799  }
2800  p->save();
2801 
2802  QPointF chrOffset = mChrOffset;
2803  double chrWidth;
2804  QString charToRender = characterToRender( context, chrOffset, chrWidth );
2805 
2806  double sizeToRender = calculateSize( context );
2807 
2808  bool hasDataDefinedRotation = false;
2809  QPointF offset;
2810  double angle = 0;
2811  calculateOffsetAndRotation( context, sizeToRender, hasDataDefinedRotation, offset, angle );
2812 
2813  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
2814 
2815  if ( !qgsDoubleNear( angle, 0.0 ) )
2816  transform.rotate( angle );
2817 
2818  if ( !qgsDoubleNear( sizeToRender, mOrigSize ) )
2819  {
2820  double s = sizeToRender / mOrigSize;
2821  transform.scale( s, s );
2822  }
2823 
2824  QPainterPath path;
2825  path.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
2826  p->drawPath( transform.map( path ) );
2827  p->restore();
2828 }
2829 
2831 {
2832  QgsStringMap props;
2833  props[QStringLiteral( "font" )] = mFontFamily;
2834  props[QStringLiteral( "chr" )] = mChr;
2835  props[QStringLiteral( "size" )] = QString::number( mSize );
2836  props[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2837  props[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2838  props[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
2839  props[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
2840  props[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
2841  props[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2842  props[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2843  props[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
2844  props[QStringLiteral( "angle" )] = QString::number( mAngle );
2845  props[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2846  props[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2847  props[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2848  props[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
2849  props[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
2850  return props;
2851 }
2852 
2854 {
2855  QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( mFontFamily, mChr, mSize, mColor, mAngle );
2856  m->setStrokeColor( mStrokeColor );
2857  m->setStrokeWidth( mStrokeWidth );
2858  m->setStrokeWidthUnit( mStrokeWidthUnit );
2859  m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
2860  m->setPenJoinStyle( mPenJoinStyle );
2861  m->setOffset( mOffset );
2862  m->setOffsetUnit( mOffsetUnit );
2864  m->setSizeUnit( mSizeUnit );
2869  copyPaintEffect( m );
2870  return m;
2871 }
2872 
2873 void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
2874 {
2875  // <Graphic>
2876  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2877  element.appendChild( graphicElem );
2878 
2879  QString fontPath = QStringLiteral( "ttf://%1" ).arg( mFontFamily );
2880  int markIndex = mChr.unicode();
2882  QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, QStringLiteral( "ttf" ), &markIndex, mColor, size );
2883 
2884  // <Rotation>
2885  QString angleFunc;
2886  bool ok;
2887  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2888  if ( !ok )
2889  {
2890  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mAngle );
2891  }
2892  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2893  {
2894  angleFunc = QString::number( angle + mAngle );
2895  }
2896  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2897 
2898  // <Displacement>
2899  QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2900  QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
2901 }
2902 
2904 {
2905  QPointF chrOffset = mChrOffset;
2906  double chrWidth = mChrWidth;
2907  //calculate width of rendered character
2908  ( void )characterToRender( context, chrOffset, chrWidth );
2909 
2910  if ( !mFontMetrics )
2911  mFontMetrics = new QFontMetrics( mFont );
2912 
2913  double scaledSize = calculateSize( context );
2914  if ( !qgsDoubleNear( scaledSize, mOrigSize ) )
2915  {
2916  chrWidth *= scaledSize / mOrigSize;
2917  }
2918 
2919  bool hasDataDefinedRotation = false;
2920  QPointF offset;
2921  double angle = 0;
2922  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
2923  scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
2924 
2925  QMatrix transform;
2926 
2927  // move to the desired position
2928  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
2929 
2930  if ( !qgsDoubleNear( angle, 0.0 ) )
2931  transform.rotate( angle );
2932 
2933  QRectF symbolBounds = transform.mapRect( QRectF( -chrWidth / 2.0,
2934  -scaledSize / 2.0,
2935  chrWidth,
2936  scaledSize ) );
2937  return symbolBounds;
2938 }
2939 
2941 {
2942  QgsDebugMsg( "Entered." );
2943 
2944  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
2945  if ( graphicElem.isNull() )
2946  return nullptr;
2947 
2948  QString name, format;
2949  QColor color;
2950  double size;
2951  int chr;
2952 
2953  if ( !QgsSymbolLayerUtils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
2954  return nullptr;
2955 
2956  if ( !name.startsWith( QLatin1String( "ttf://" ) ) || format != QLatin1String( "ttf" ) )
2957  return nullptr;
2958 
2959  QString fontFamily = name.mid( 6 );
2960 
2961  double angle = 0.0;
2962  QString angleFunc;
2963  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2964  {
2965  bool ok;
2966  double d = angleFunc.toDouble( &ok );
2967  if ( ok )
2968  angle = d;
2969  }
2970 
2971  QPointF offset;
2972  QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, offset );
2973 
2974  QString uom = element.attribute( QStringLiteral( "uom" ) );
2975  offset.setX( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.x() ) );
2976  offset.setY( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.y() ) );
2977  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
2978 
2979  QgsMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( fontFamily, chr, size, color );
2980  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
2981  m->setAngle( angle );
2982  m->setOffset( offset );
2983  return m;
2984 }
2985 
bool prepareCache(QgsSymbolRenderContext &context)
Prepares cache image.
void setOffset(QPointF offset)
Sets the marker&#39;s offset, which is the horizontal and vertical displacement which the rendered marker...
static void wellKnownMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &name, const QColor &color, const QColor &strokeColor, Qt::PenStyle strokeStyle, double strokeWidth=-1, double size=-1)
QPicture svgAsPicture(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool forceVectorOutput=false, double fixedAspectRatio=0)
Gets SVG as QPicture&.
#define DEFAULT_SIMPLEMARKER_SIZE
QgsMapUnitScale mapUnitScale() const override
static QgsSvgCache * svgCache()
Returns the application&#39;s SVG cache, used for caching SVG images and handling parameter replacement w...
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
bool writeDxf(QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift=QPointF(0.0, 0.0)) const override
write as DXF
QColor valueAsColor(int key, const QgsExpressionContext &context, const QColor &defaultColor=QColor(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a color...
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
QString ogrFeatureStyle(double mmScaleFactor, double mapUnitScaleFactor) const override
void setOffsetUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol&#39;s offset.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsSimpleMarkerSymbolLayer from an SLD XML element.
const QgsPathResolver & pathResolver() const
Returns the path resolver for conversion between relative and absolute paths during rendering operati...
static QgsSymbol::ScaleMethod decodeScaleMethod(const QString &str)
double symbologyScale() const
Returns the reference scale for output.
Definition: qgsdxfexport.h:150
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
Set stroke width map unit scale.
void writeLine(const QgsPoint &pt1, const QgsPoint &pt2, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Write line (as a polyline)
void clipValueToMapUnitScale(double &value, const QgsMapUnitScale &scale, double pixelToMMFactor) const
Clips value to scale minimum/maximum.
void setColor(const QColor &c) override
The fill color.
QColor mStrokeColor
Stroke color.
QgsFields fields() const
Fields of the layer.
Definition: qgssymbol.h:534
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
Qt::PenStyle mStrokeStyle
Stroke style.
Calculate scale by the diameter.
Definition: qgssymbol.h:97
A paint device for drawing into dxf files.
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double...
#define DEFAULT_SIMPLEMARKER_NAME
Use antialiasing while drawing.
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit u)
Sets the unit for the width of the marker&#39;s stroke.
void setMapUnitScale(const QgsMapUnitScale &scale) override
void setStrokeStyle(Qt::PenStyle strokeStyle)
Sets the marker&#39;s stroke style (e.g., solid, dashed, etc)
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
Simple marker symbol layer, consisting of a rendered shape with solid fill color and an stroke...
void writePolyline(const QgsPointSequence &line, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Draw dxf primitives (LWPOLYLINE)
Abstract base class for simple marker symbol layers.
QImage svgAsImage(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool &fitsInCache, double fixedAspectRatio=0)
Gets SVG as QImage.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsFontMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
Right facing arrow head (unfilled, lines only)
static QgsFillSymbol * createSimple(const QgsStringMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties. ...
Definition: qgssymbol.cpp:1109
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:251
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
double mLineAngle
Line rotation angle (see setLineAngle() for details)
void setDrawingSize(QSizeF size)
QgsUnitTypes::RenderUnit mSizeUnit
Marker size unit.
virtual QColor strokeColor() const
Gets stroke color.
QgsMapUnitScale mapUnitScale() const override
QgsMapUnitScale mStrokeWidthMapUnitScale
Stroke width map unit scale.
static bool displacementFromSldElement(QDomElement &element, QPointF &offset)
static QPointF decodePoint(const QString &string)
Decodes a QSizeF from a string.
#define DEG2RAD(x)
void setStrokeColor(const QColor &color) override
Sets the marker&#39;s stroke color.
void writeSldMarker(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
Writes the symbol layer definition as a SLD XML element.
static void externalMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &path, const QString &format, int *markIndex=nullptr, const QColor &color=QColor(), double size=-1)
void copyPaintEffect(QgsSymbolLayer *destLayer) const
Copies paint effect of this layer to another symbol layer.
void setStrokeColor(const QColor &c) override
Set stroke color.
void setStrokeWidth(double w)
Sets the width of the marker&#39;s stroke.
static const int MAXIMUM_CACHE_WIDTH
Maximum width/height of cache image.
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
#define DEFAULT_FONTMARKER_COLOR
static void createDisplacementElement(QDomDocument &doc, QDomElement &element, QPointF offset)
QBrush mSelBrush
QBrush to use as fill of selected symbols.
static QgsSimpleMarkerSymbolLayerBase::Shape decodeShape(const QString &name, bool *ok=nullptr)
Attempts to decode a string representation of a shape name to the corresponding shape.
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol&#39;s offset.
void restoreOldDataDefinedProperties(const QgsStringMap &stringMap)
Restores older data defined properties from string map.
void setStrokeWidth(double width)
Set stroke width.
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
static double sizeInPixelsFromSldUom(const QString &uom, double size)
Returns the size scaled in pixels according to the uom attribute.
#define DEFAULT_FONTMARKER_BORDERCOLOR
Flags flags() const
Returns combination of flags used for rendering.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
QgsUnitTypes::DistanceUnit mapUnits() const
Retrieve map units.
QPointF mOffset
Marker offset.
QColor strokeColor() const override
Returns the marker&#39;s stroke color.
void startRender(QgsSymbolRenderContext &context) override
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:62
static bool rotationFromSldElement(QDomElement &element, QString &rotationFunc)
QgsUnitTypes::RenderUnit mStrokeWidthUnit
Stroke width units.
#define DEFAULT_SIMPLEMARKER_JOINSTYLE
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:190
static QString encodeShape(QgsSimpleMarkerSymbolLayerBase::Shape shape)
Encodes a shape to its string representation.
QgsMapUnitScale mapUnitScale() const override
QMap< QString, QString > QgsStringMap
Definition: qgis.h:501
Rotation of symbol may be changed during rendering and symbol should not be cached.
Definition: qgssymbol.h:104
QgsFontMarkerSymbolLayer(const QString &fontFamily=DEFAULT_FONTMARKER_FONT, QChar chr=DEFAULT_FONTMARKER_CHR, double pointSize=DEFAULT_FONTMARKER_SIZE, const QColor &color=DEFAULT_FONTMARKER_COLOR, double angle=DEFAULT_FONTMARKER_ANGLE)
static QString encodePoint(QPointF point)
Encodes a QPointF to a string.
Name, eg shape name for simple markers.
static Qt::PenJoinStyle decodePenJoinStyle(const QString &str)
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
static QList< QgsSimpleMarkerSymbolLayerBase::Shape > availableShapes()
Returns a list of all available shape types.
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
void writePolygon(const QgsRingSequence &polygon, const QString &layer, const QString &hatchPattern, const QColor &color)
Draw dxf filled polygon (HATCH)
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
double mapRotation() const
Returns current map rotation in degrees.
#define DEFAULT_FONTMARKER_SIZE
QColor strokeColor() const override
Gets stroke color.
void setPenJoinStyle(Qt::PenJoinStyle style)
Set stroke join style.
double strokeWidth() const
Returns the width of the marker&#39;s stroke.
static QgsSymbolLayer * createFromSld(QDomElement &element)
void setPath(const QString &path)
Set the marker SVG path.
QPainterPath mPath
Painter path representing shape. If mPolygon is empty then the shape is stored in mPath...
static QString encodeColor(const QColor &color)
Diagonal half square (bottom left half)
virtual void setColor(const QColor &color)
The fill color.
void startRender(QgsSymbolRenderContext &context) override
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const override
Returns the calculated value of the property with the specified key from within the collection...
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for data defined symbology.
Definition: qgssymbol.cpp:1050
static QString encodePenStyle(Qt::PenStyle style)
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:36
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsFilledMarkerSymbolLayer.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsSimpleMarkerSymbolLayer.
static QString svgSymbolPathToName(const QString &path, const QgsPathResolver &pathResolver)
Determines an SVG symbol&#39;s name from its path.
static double rescaleUom(double size, QgsUnitTypes::RenderUnit unit, const QgsStringMap &props)
Rescales the given size based on the uomScale found in the props, if any is found, otherwise returns the value un-modified.
One third circle (top left third)
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the stroke width.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
bool shapeToPolygon(Shape shape, QPolygonF &polygon) const
Creates a polygon representing the specified shape.
void setStrokeColor(const QColor &color) override
Set stroke color.
double angle() const
Returns the rotation angle for the marker, in degrees clockwise from north.
QgsUnitTypes::RenderUnit mOffsetUnit
Offset units.
double updateDefaultAspectRatio()
Calculates the default marker aspect ratio between width and height.
void setColor(const QColor &color) override
The fill color.
void stopRender(QgsSymbolRenderContext &context) override
bool mUsingCache
True if using cached images of markers for drawing.
QPen mPen
QPen corresponding to marker&#39;s stroke style.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
Sets the map scale for the width of the marker&#39;s stroke.
#define DEFAULT_SCALE_METHOD
Q_GUI_EXPORT int qt_defaultDpiY()
double calculateSize(QgsSymbolRenderContext &context, bool &hasDataDefinedSize) const
Calculates the desired size of the marker, considering data defined size overrides.
Calculate scale by the area.
Definition: qgssymbol.h:96
qreal opacity() const
Returns the opacity for the symbol.
Definition: qgssymbol.h:483
double size() const
Returns the symbol size.
double mStrokeWidth
Stroke width.
QBrush mBrush
QBrush corresponding to marker&#39;s fill style.
QgsSimpleMarkerSymbolLayerBase(QgsSimpleMarkerSymbolLayerBase::Shape shape=Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, QgsSymbol::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructor for QgsSimpleMarkerSymbolLayerBase.
void setHorizontalAnchorPoint(HorizontalAnchorPoint h)
Sets the horizontal anchor point for positioning the symbol.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
static void resolvePaths(QgsStringMap &properties, const QgsPathResolver &pathResolver, bool saving)
Turns relative paths in properties map to absolute when reading and vice versa when writing...
void setSizeMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol&#39;s size.
#define DEFAULT_FONTMARKER_JOINSTYLE
void setLayer(const QString &layer)
static double mapUnitScaleFactor(double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits, double mapUnitsPerPixel=1.0)
Returns scale factor for conversion to map units.
QColor color() const override
The fill color.
void calculateOffsetAndRotation(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle) const
Calculates the marker offset and rotation.
virtual QColor color() const
The fill color.
static void createRotationElement(QDomDocument &doc, QDomElement &element, const QString &rotationFunc)
void setFillColor(const QColor &color) override
Set fill color.
QColor fillColor() const override
Gets fill color.
QColor selectionColor() const
#define DEFAULT_FONTMARKER_ANGLE
double mAngle
Marker rotation angle, in degrees clockwise from north.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void writeFilledCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius)
Write filled circle (as hatch)
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit unit)
Set stroke width unit.
double calculateAspectRatio(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio) const
Calculates the marker aspect ratio between width and height.
void markerOffset(QgsSymbolRenderContext &context, double &offsetX, double &offsetY) const
Calculates the required marker offset, including both the symbol offset and any displacement required...
static double estimateMaxSymbolBleed(QgsSymbol *symbol, const QgsRenderContext &context)
Returns the maximum estimated bleed for the symbol.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
QgsMapUnitScale mSizeMapUnitScale
Marker size map unit scale.
Character, eg for font marker symbol layers.
static void parametricSvgToSld(QDomDocument &doc, QDomElement &graphicElem, const QString &path, const QColor &fillColor, double size, const QColor &strokeColor, double strokeWidth)
Encodes a reference to a parametric SVG into SLD, as a succession of parametric SVG using URL paramet...
Qt::PenJoinStyle penJoinStyle() const
Returns the marker&#39;s stroke join style (e.g., miter, bevel, etc).
ScaleMethod
Scale method.
Definition: qgssymbol.h:94
static Q_INVOKABLE QgsUnitTypes::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
QgsSimpleMarkerSymbolLayerBase::Shape shape() const
Returns the shape for the rendered marker symbol.
QgsGeometry geometry() const
Returns the geometry associated with this feature.
Definition: qgsfeature.cpp:101
double mapUnitsPerPixel() const
Returns current map units per pixel.
QPen mSelPen
QPen to use as stroke of selected symbols.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
QgsRenderContext & renderContext()
Returns a reference to the context&#39;s render context.
Definition: qgssymbol.h:453
QgsSymbol * subSymbol() override
Returns the symbol&#39;s sub symbol, if present.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
static Qt::PenStyle decodePenStyle(const QString &str)
static bool externalGraphicFromSld(QDomElement &element, QString &path, QString &mime, QColor &color, double &size)
QgsWkbTypes::GeometryType type() const
Returns type of the geometry as a QgsWkbTypes::GeometryType.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void startRender(QgsSymbolRenderContext &context) override
bool selected() const
Definition: qgssymbol.h:492
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
Qt::PenStyle strokeStyle() const
Returns the marker&#39;s stroke style (e.g., solid, dashed, etc)
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void drawMarker(QPainter *p, QgsSymbolRenderContext &context)
Draws the marker shape in the specified painter.
HorizontalAnchorPoint
Symbol horizontal anchor points.
virtual void setStrokeColor(const QColor &color)
Set stroke color.
VerticalAnchorPoint
Symbol vertical anchor points.
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
bool setPreservedAspectRatio(bool par)
Set preserved the marker aspect ratio between width and height.
QString layerType() const override
Returns a string that represents this layer type.
Q_GUI_EXPORT int qt_defaultDpiX()
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
QgsSymbol::ScaleMethod scaleMethod() const
Returns the method to use for scaling the marker&#39;s size.
QgsExpressionContext & expressionContext()
Gets the expression context.
void containsParams(const QString &path, bool &hasFillParam, QColor &defaultFillColor, bool &hasStrokeParam, QColor &defaultStrokeColor, bool &hasStrokeWidthParam, double &defaultStrokeWidth) const
Tests if an svg file contains parameters for fill, stroke color, stroke width.
QString layerType() const override
Returns a string that represents this layer type.
static QString encodeScaleMethod(QgsSymbol::ScaleMethod scaleMethod)
static bool externalMarkerFromSld(QDomElement &element, QString &path, QString &format, int &markIndex, QColor &color, double &size)
void startRender(QgsSymbolRenderContext &context) override
QVector< QgsPoint > QgsPointSequence
void stopRender(QgsSymbolRenderContext &context) override
bool forceVectorOutput() const
void setFixedAspectRatio(double ratio)
Set the marker aspect ratio between width and height to be used in rendering, if the value set is low...
QVector< QgsPointSequence > QgsRingSequence
static QgsSymbolLayer * createFromSld(QDomElement &element)
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol&#39;s size.
QString valueAsString(int key, const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a string...
Stroke style (eg solid, dashed)
#define DEFAULT_SIMPLEMARKER_BORDERCOLOR
Fill symbol.
Definition: qgssymbol.h:87
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns the set of attributes referenced by the layer.
Contains information about the context of a rendering operation.
Abstract base class for marker symbol layers.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
void writeCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius, const QString &lineStyleName, double width)
Write circle (as polyline)
void setShift(QPointF shift)
QPainter * painter()
Returns the destination QPainter for the render operation.
virtual void setFillColor(const QColor &color)
Set fill color.
const QgsMapToPixel & mapToPixel() const
QColor color() const override
The fill color.
SymbolType type() const
Definition: qgssymbol.h:113
QImage mCache
Cached image of marker, if using cached version.
QgsSvgMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
QgsSimpleMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
Struct for storing maximum and minimum scales for measurements in map units.
void setMapUnitScale(const QgsMapUnitScale &scale) override
#define DEFAULT_SIMPLEMARKER_ANGLE
#define DEFAULT_SIMPLEMARKER_COLOR
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void startRender(QgsSymbolRenderContext &context) override
QgsSimpleMarkerSymbolLayer(QgsSimpleMarkerSymbolLayerBase::Shape shape=Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, QgsSymbol::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD, const QColor &color=DEFAULT_SIMPLEMARKER_COLOR, const QColor &strokeColor=DEFAULT_SIMPLEMARKER_BORDERCOLOR, Qt::PenJoinStyle penJoinStyle=DEFAULT_SIMPLEMARKER_JOINSTYLE)
Constructor for QgsSimpleMarkerSymbolLayer.
QgsSymbol::ScaleMethod mScaleMethod
Marker size scaling method.
Qt::PenJoinStyle mPenJoinStyle
Stroke pen join style.
const QgsFeature * feature() const
Current feature being rendered - may be null.
Definition: qgssymbol.h:509
QgsSvgMarkerSymbolLayer(const QString &path, double size=DEFAULT_SVGMARKER_SIZE, double angle=DEFAULT_SVGMARKER_ANGLE, QgsSymbol::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructs SVG marker symbol layer with picture from given absolute path to a SVG file...
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
QByteArray svgContent(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0)
Gets SVG content.
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer&#39;s subsymbol. takes ownership of the passed symbol.
QColor fillColor() const override
Gets fill color.
HorizontalAnchorPoint mHorizontalAnchorPoint
Horizontal anchor point.
QImage mSelCache
Cached image of selected marker, if using cached version.
#define DEFAULT_FONTMARKER_CHR
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
void writeSldMarker(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
Writes the symbol layer definition as a SLD XML element.
void writeSldMarker(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
Writes the symbol layer definition as a SLD XML element.
static bool wellKnownMarkerFromSld(QDomElement &element, QString &name, QColor &color, QColor &strokeColor, Qt::PenStyle &strokeStyle, double &strokeWidth, double &size)
double mSize
Marker size.
void setAngle(double angle)
Sets the rotation angle for the marker.
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
QString layerType() const override
Returns a string that represents this layer type.
void stopRender(QgsSymbolRenderContext &context) override
bool writeDxf(QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift=QPointF(0.0, 0.0)) const override
write as DXF
QgsMapUnitScale mOffsetMapUnitScale
Offset map unit scale.
VerticalAnchorPoint mVerticalAnchorPoint
Vertical anchor point.
void stopRender(QgsSymbolRenderContext &context) override
void setFillColor(const QColor &color) override
Set fill color.
static bool shapeIsFilled(QgsSimpleMarkerSymbolLayerBase::Shape shape)
Returns true if a symbol shape has a fill.
static QString svgSymbolNameToPath(const QString &name, const QgsPathResolver &pathResolver)
Determines an SVG symbol&#39;s path from its name.
static QPointF _rotatedOffset(QPointF offset, double angle)
Adjusts a marker offset to account for rotation.
bool prepareMarkerShape(Shape shape)
Prepares the layer for drawing the specified shape (QPolygonF version)
Resolves relative paths into absolute paths and vice versa.
void setMapUnitScale(const QgsMapUnitScale &scale) override
QString layerType() const override
Returns a string that represents this layer type.
#define DEFAULT_SVGMARKER_SIZE
QPointF offset() const
Returns the marker&#39;s offset, which is the horizontal and vertical displacement which the rendered mar...
QgsFilledMarkerSymbolLayer(QgsSimpleMarkerSymbolLayerBase::Shape shape=Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, QgsSymbol::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructor for QgsFilledMarkerSymbolLayer.
Filled marker symbol layer, consisting of a shape which is rendered using a QgsFillSymbol.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
virtual QColor fillColor() const
Gets fill color.
#define DEFAULT_SVGMARKER_ANGLE
static void multiplyImageOpacity(QImage *image, qreal opacity)
Multiplies opacity of image pixel values with a (global) transparency value.
Rotated cross (lines only), "x" shape.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QPolygonF mPolygon
Polygon of points in shape. If polygon is empty then shape is using mPath.
QgsFilledMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
#define DEFAULT_FONTMARKER_FONT
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
QgsPropertyCollection mDataDefinedProperties
bool prepareMarkerPath(Shape symbol)
Prepares the layer for drawing the specified shape (QPainterPath version)
QSizeF svgViewboxSize(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0)
Calculates the viewbox size of a (possibly cached) SVG file.
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:100
static QColor decodeColor(const QString &str)
void copyDataDefinedProperties(QgsSymbolLayer *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
void setOutputSize(const QRectF &r)
void setVerticalAnchorPoint(VerticalAnchorPoint v)
Sets the vertical anchor point for positioning the symbol.
QgsSymbol::RenderHints renderHints() const
Returns the rendering hint flags for the symbol.
Definition: qgssymbol.h:499
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...