QGIS API Documentation  3.0.2-Girona (307d082)
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  //data defined size?
1225  double size = mSize;
1226 
1227  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
1228 
1229  //data defined size
1230  bool ok = true;
1231  if ( hasDataDefinedSize )
1232  {
1233  context.setOriginalValueVariable( mSize );
1235 
1236  if ( ok )
1237  {
1238  switch ( mScaleMethod )
1239  {
1240  case QgsSymbol::ScaleArea:
1241  size = std::sqrt( size );
1242  break;
1244  break;
1245  }
1246  }
1247 
1249  }
1251  {
1252  size *= mmMapUnitScaleFactor;
1253  }
1254  double halfSize = size / 2.0;
1255 
1256  //strokeWidth
1257  double strokeWidth = mStrokeWidth;
1258 
1260  {
1263  }
1265  {
1266  strokeWidth *= mmMapUnitScaleFactor;
1267  }
1268 
1269  //color
1270  QColor pc = mPen.color();
1271  QColor bc = mBrush.color();
1273  {
1276  }
1278  {
1281  }
1282 
1283  //offset
1284  double offsetX = 0;
1285  double offsetY = 0;
1286  markerOffset( context, offsetX, offsetY );
1287 
1288  QPointF off( offsetX, offsetY );
1289 
1290  //angle
1291  double angle = mAngle + mLineAngle;
1293  {
1294  context.setOriginalValueVariable( mAngle );
1296  }
1297 
1298  Shape shape = mShape;
1300  {
1301  context.setOriginalValueVariable( encodeShape( shape ) );
1302  QString shapeName = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyName, context.renderContext().expressionContext(), QString(), &ok );
1303  if ( ok )
1304  {
1305  shape = decodeShape( shapeName, &ok );
1306  if ( !ok )
1307  shape = mShape;
1308  }
1309  }
1310 
1311  angle = -angle; //rotation in Qt is counterclockwise
1312  if ( angle )
1313  off = _rotatedOffset( off, angle );
1314 
1316  {
1317  off *= mmMapUnitScaleFactor;
1318  }
1319 
1320  QTransform t;
1321  t.translate( shift.x() + offsetX, shift.y() + offsetY );
1322 
1323  if ( !qgsDoubleNear( angle, 0.0 ) )
1324  t.rotate( angle );
1325 
1326  QPolygonF polygon;
1327  if ( shapeToPolygon( shape, polygon ) )
1328  {
1329  t.scale( halfSize, -halfSize );
1330 
1331  polygon = t.map( polygon );
1332 
1333  QgsPointSequence p;
1334  p.reserve( polygon.size() );
1335  for ( int i = 0; i < polygon.size(); i++ )
1336  p << QgsPoint( polygon[i] );
1337  p << p[0];
1338 
1339  if ( mBrush.style() != Qt::NoBrush )
1340  e.writePolygon( QgsRingSequence() << p, layerName, QStringLiteral( "SOLID" ), bc );
1341  if ( mPen.style() != Qt::NoPen )
1342  e.writePolyline( p, layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1343  }
1344  else if ( shape == Circle )
1345  {
1346  if ( mBrush.style() != Qt::NoBrush )
1347  e.writeFilledCircle( layerName, bc, QgsPoint( shift ), halfSize );
1348  if ( mPen.style() != Qt::NoPen )
1349  e.writeCircle( layerName, pc, QgsPoint( shift ), halfSize, QStringLiteral( "CONTINUOUS" ), strokeWidth );
1350  }
1351  else if ( shape == Line )
1352  {
1353  QPointF pt1 = t.map( QPointF( 0, -halfSize ) );
1354  QPointF pt2 = t.map( QPointF( 0, halfSize ) );
1355 
1356  if ( mPen.style() != Qt::NoPen )
1357  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1358  }
1359  else if ( shape == Cross )
1360  {
1361  if ( mPen.style() != Qt::NoPen )
1362  {
1363  QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
1364  QPointF pt2 = t.map( QPointF( halfSize, 0 ) );
1365  QPointF pt3 = t.map( QPointF( 0, -halfSize ) );
1366  QPointF pt4 = t.map( QPointF( 0, halfSize ) );
1367 
1368  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1369  e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1370  }
1371  }
1372  else if ( shape == Cross2 )
1373  {
1374  if ( mPen.style() != Qt::NoPen )
1375  {
1376  QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
1377  QPointF pt2 = t.map( QPointF( halfSize, halfSize ) );
1378  QPointF pt3 = t.map( QPointF( halfSize, -halfSize ) );
1379  QPointF pt4 = t.map( QPointF( -halfSize, halfSize ) );
1380 
1381  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1382  e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1383  }
1384  }
1385  else if ( shape == ArrowHead )
1386  {
1387  if ( mPen.style() != Qt::NoPen )
1388  {
1389  QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
1390  QPointF pt2 = t.map( QPointF( 0, 0 ) );
1391  QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
1392 
1393  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1394  e.writeLine( QgsPoint( pt3 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1395  }
1396  }
1397  else
1398  {
1399  QgsDebugMsg( QString( "Unsupported dxf marker name %1" ).arg( encodeShape( shape ) ) );
1400  return false;
1401  }
1402 
1403  return true;
1404 }
1405 
1406 
1408 {
1410  mStrokeWidthUnit = unit;
1411 }
1412 
1414 {
1416  {
1417  return mStrokeWidthUnit;
1418  }
1420 }
1421 
1423 {
1425  mStrokeWidthMapUnitScale = scale;
1426 }
1427 
1429 {
1431  {
1432  return mStrokeWidthMapUnitScale;
1433  }
1434  return QgsMapUnitScale();
1435 }
1436 
1438 {
1439  QRectF symbolBounds = QgsSimpleMarkerSymbolLayerBase::bounds( point, context );
1440 
1441  // need to account for stroke width
1442  double penWidth = 0.0;
1443  bool ok = true;
1445  {
1448  if ( ok )
1449  {
1450  penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
1451  }
1452  }
1454  {
1457  if ( ok && strokeStyle == QLatin1String( "no" ) )
1458  {
1459  penWidth = 0.0;
1460  }
1461  }
1462  //antialiasing, add 1 pixel
1463  penWidth += 1;
1464 
1465  //extend bounds by pen width / 2.0
1466  symbolBounds.adjust( -penWidth / 2.0, -penWidth / 2.0,
1467  penWidth / 2.0, penWidth / 2.0 );
1468 
1469  return symbolBounds;
1470 }
1471 
1473 {
1474  if ( shapeIsFilled( mShape ) )
1475  {
1476  setFillColor( color );
1477  }
1478  else
1479  {
1480  setStrokeColor( color );
1481  }
1482 }
1483 
1485 {
1486  if ( shapeIsFilled( mShape ) )
1487  {
1488  return fillColor();
1489  }
1490  else
1491  {
1492  return strokeColor();
1493  }
1494 }
1495 
1496 
1497 
1498 
1499 //
1500 // QgsFilledMarkerSymbolLayer
1501 //
1502 
1504  : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
1505 {
1506  mFill.reset( static_cast<QgsFillSymbol *>( QgsFillSymbol::createSimple( QgsStringMap() ) ) );
1507 }
1508 
1510 {
1511  QString name = DEFAULT_SIMPLEMARKER_NAME;
1515 
1516  if ( props.contains( QStringLiteral( "name" ) ) )
1517  name = props[QStringLiteral( "name" )];
1518  if ( props.contains( QStringLiteral( "size" ) ) )
1519  size = props[QStringLiteral( "size" )].toDouble();
1520  if ( props.contains( QStringLiteral( "angle" ) ) )
1521  angle = props[QStringLiteral( "angle" )].toDouble();
1522  if ( props.contains( QStringLiteral( "scale_method" ) ) )
1523  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )] );
1524 
1525  QgsFilledMarkerSymbolLayer *m = new QgsFilledMarkerSymbolLayer( decodeShape( name ), size, angle, scaleMethod );
1526  if ( props.contains( QStringLiteral( "offset" ) ) )
1527  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
1528  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1529  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
1530  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1531  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
1532  if ( props.contains( QStringLiteral( "size_unit" ) ) )
1533  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
1534  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1535  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
1536  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1537  {
1538  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1539  }
1540  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1541  {
1542  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1543  }
1544 
1546 
1547  m->restoreOldDataDefinedProperties( props );
1548 
1549  return m;
1550 }
1551 
1553 {
1554  return QStringLiteral( "FilledMarker" );
1555 }
1556 
1558 {
1559  if ( mFill )
1560  {
1561  mFill->startRender( context.renderContext(), context.fields() );
1562  }
1563 
1565 }
1566 
1568 {
1569  if ( mFill )
1570  {
1571  mFill->stopRender( context.renderContext() );
1572  }
1573 }
1574 
1576 {
1577  QgsStringMap map;
1578  map[QStringLiteral( "name" )] = encodeShape( mShape );
1579  map[QStringLiteral( "size" )] = QString::number( mSize );
1580  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1581  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1582  map[QStringLiteral( "angle" )] = QString::number( mAngle );
1583  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1584  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1585  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1586  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1587  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1588  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1589 
1590  if ( mFill )
1591  {
1592  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mFill->color() );
1593  }
1594  return map;
1595 }
1596 
1598 {
1600  copyPaintEffect( m );
1602  m->setSubSymbol( mFill->clone() );
1603  return m;
1604 }
1605 
1607 {
1608  return mFill.get();
1609 }
1610 
1612 {
1613  if ( symbol && symbol->type() == QgsSymbol::Fill )
1614  {
1615  mFill.reset( static_cast<QgsFillSymbol *>( symbol ) );
1616  return true;
1617  }
1618  else
1619  {
1620  delete symbol;
1621  return false;
1622  }
1623 }
1624 
1626 {
1627  if ( mFill )
1628  {
1629  return QgsSymbolLayerUtils::estimateMaxSymbolBleed( mFill.get(), context );
1630  }
1631  return 0;
1632 }
1633 
1635 {
1636  QSet<QString> attr = QgsSimpleMarkerSymbolLayerBase::usedAttributes( context );
1637  if ( mFill )
1638  attr.unite( mFill->usedAttributes( context ) );
1639  return attr;
1640 }
1641 
1643 {
1644  mColor = c;
1645  if ( mFill )
1646  mFill->setColor( c );
1647 }
1648 
1650 {
1651  return mFill ? mFill->color() : mColor;
1652 }
1653 
1654 void QgsFilledMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, QgsSimpleMarkerSymbolLayerBase::Shape shape, const QPolygonF &polygon, const QPainterPath &path )
1655 {
1656  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1657  //of the rendered point!
1658 
1659  QPainter *p = context.renderContext().painter();
1660  if ( !p )
1661  {
1662  return;
1663  }
1664 
1665  if ( shapeIsFilled( shape ) )
1666  {
1667  p->setBrush( Qt::red );
1668  }
1669  else
1670  {
1671  p->setBrush( Qt::NoBrush );
1672  }
1673  p->setPen( Qt::black );
1674 
1675  if ( !polygon.isEmpty() )
1676  {
1677  mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
1678  }
1679  else
1680  {
1681  QPolygonF poly = path.toFillPolygon();
1682  mFill->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
1683  }
1684 
1685 
1686 }
1687 
1688 
1690 
1691 
1693 {
1694  mPath = path;
1695  mSize = size;
1696  mAngle = angle;
1697  mOffset = QPointF( 0, 0 );
1699  mStrokeWidth = 0.2;
1700  mStrokeWidthUnit = QgsUnitTypes::RenderMillimeters;
1701  mColor = QColor( 35, 35, 35 );
1702  mStrokeColor = QColor( 35, 35, 35 );
1703  updateDefaultAspectRatio();
1704 }
1705 
1706 
1708 {
1709  QString name;
1710  double size = DEFAULT_SVGMARKER_SIZE;
1711  double angle = DEFAULT_SVGMARKER_ANGLE;
1713 
1714  if ( props.contains( QStringLiteral( "name" ) ) )
1715  name = props[QStringLiteral( "name" )];
1716  if ( props.contains( QStringLiteral( "size" ) ) )
1717  size = props[QStringLiteral( "size" )].toDouble();
1718  if ( props.contains( QStringLiteral( "angle" ) ) )
1719  angle = props[QStringLiteral( "angle" )].toDouble();
1720  if ( props.contains( QStringLiteral( "scale_method" ) ) )
1721  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )] );
1722 
1723  QgsSvgMarkerSymbolLayer *m = new QgsSvgMarkerSymbolLayer( name, size, angle, scaleMethod );
1724 
1725  //we only check the svg default parameters if necessary, since it could be expensive
1726  if ( !props.contains( QStringLiteral( "fill" ) ) && !props.contains( QStringLiteral( "color" ) ) && !props.contains( QStringLiteral( "outline" ) ) &&
1727  !props.contains( QStringLiteral( "outline_color" ) ) && !props.contains( QStringLiteral( "outline-width" ) ) && !props.contains( QStringLiteral( "outline_width" ) ) )
1728  {
1729  QColor fillColor, strokeColor;
1730  double fillOpacity = 1.0;
1731  double strokeOpacity = 1.0;
1732  double strokeWidth;
1733  bool hasFillParam = false, hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
1734  bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
1735  QgsApplication::svgCache()->containsParams( name, hasFillParam, hasDefaultFillColor, fillColor,
1736  hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
1737  hasStrokeParam, hasDefaultStrokeColor, strokeColor,
1738  hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
1739  hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
1740  if ( hasDefaultFillColor )
1741  {
1742  m->setFillColor( fillColor );
1743  }
1744  if ( hasDefaultFillOpacity )
1745  {
1746  QColor c = m->fillColor();
1747  c.setAlphaF( fillOpacity );
1748  m->setFillColor( c );
1749  }
1750  if ( hasDefaultStrokeColor )
1751  {
1752  m->setStrokeColor( strokeColor );
1753  }
1754  if ( hasDefaultStrokeWidth )
1755  {
1756  m->setStrokeWidth( strokeWidth );
1757  }
1758  if ( hasDefaultStrokeOpacity )
1759  {
1760  QColor c = m->strokeColor();
1761  c.setAlphaF( strokeOpacity );
1762  m->setStrokeColor( c );
1763  }
1764  }
1765 
1766  if ( props.contains( QStringLiteral( "size_unit" ) ) )
1767  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
1768  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1769  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
1770  if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
1771  m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
1772  if ( props.contains( QStringLiteral( "offset" ) ) )
1773  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
1774  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1775  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
1776  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1777  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
1778  if ( props.contains( QStringLiteral( "fill" ) ) )
1779  {
1780  //pre 2.5 projects used "fill"
1781  m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "fill" )] ) );
1782  }
1783  else if ( props.contains( QStringLiteral( "color" ) ) )
1784  {
1785  m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] ) );
1786  }
1787  if ( props.contains( QStringLiteral( "outline" ) ) )
1788  {
1789  //pre 2.5 projects used "outline"
1790  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline" )] ) );
1791  }
1792  else if ( props.contains( QStringLiteral( "outline_color" ) ) )
1793  {
1794  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )] ) );
1795  }
1796  else if ( props.contains( QStringLiteral( "line_color" ) ) )
1797  {
1798  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )] ) );
1799  }
1800 
1801  if ( props.contains( QStringLiteral( "outline-width" ) ) )
1802  {
1803  //pre 2.5 projects used "outline-width"
1804  m->setStrokeWidth( props[QStringLiteral( "outline-width" )].toDouble() );
1805  }
1806  else if ( props.contains( QStringLiteral( "outline_width" ) ) )
1807  {
1808  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
1809  }
1810  else if ( props.contains( QStringLiteral( "line_width" ) ) )
1811  {
1812  m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
1813  }
1814 
1815  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
1816  {
1817  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )] ) );
1818  }
1819  else if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
1820  {
1821  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )] ) );
1822  }
1823  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
1824  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )] ) );
1825 
1826  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1827  {
1828  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1829  }
1830  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1831  {
1832  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1833  }
1834 
1835  m->restoreOldDataDefinedProperties( props );
1836 
1838 
1839  return m;
1840 }
1841 
1843 {
1844  QgsStringMap::iterator it = properties.find( QStringLiteral( "name" ) );
1845  if ( it != properties.end() )
1846  {
1847  if ( saving )
1848  it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value(), pathResolver );
1849  else
1850  it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value(), pathResolver );
1851  }
1852 }
1853 
1854 void QgsSvgMarkerSymbolLayer::setPath( const QString &path )
1855 {
1856  mPath = path;
1857  QColor defaultFillColor, defaultStrokeColor;
1858  double strokeWidth, fillOpacity, strokeOpacity;
1859  bool hasFillParam = false, hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
1860  bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
1861  QgsApplication::svgCache()->containsParams( path, hasFillParam, hasDefaultFillColor, defaultFillColor,
1862  hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
1863  hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
1864  hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
1865  hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
1866 
1867  double newFillOpacity = hasFillOpacityParam ? fillColor().alphaF() : 1.0;
1868  double newStrokeOpacity = hasStrokeOpacityParam ? strokeColor().alphaF() : 1.0;
1869 
1870  if ( hasDefaultFillColor )
1871  {
1872  defaultFillColor.setAlphaF( newFillOpacity );
1873  setFillColor( defaultFillColor );
1874  }
1875  if ( hasDefaultFillOpacity )
1876  {
1877  QColor c = fillColor();
1878  c.setAlphaF( fillOpacity );
1879  setFillColor( c );
1880  }
1881  if ( hasDefaultStrokeColor )
1882  {
1883  defaultStrokeColor.setAlphaF( newStrokeOpacity );
1884  setStrokeColor( defaultStrokeColor );
1885  }
1886  if ( hasDefaultStrokeWidth )
1887  {
1888  setStrokeWidth( strokeWidth );
1889  }
1890  if ( hasDefaultStrokeOpacity )
1891  {
1892  QColor c = strokeColor();
1893  c.setAlphaF( strokeOpacity );
1894  setStrokeColor( c );
1895  }
1896 
1897  updateDefaultAspectRatio();
1898 }
1899 
1901 {
1902  if ( mDefaultAspectRatio == 0.0 )
1903  {
1904  //size
1905  double size = mSize;
1906  //assume 88 dpi as standard value
1907  double widthScaleFactor = 3.465;
1908  QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( mPath, size, mColor, mStrokeColor, mStrokeWidth, widthScaleFactor );
1909  // set default aspect ratio
1910  mDefaultAspectRatio = svgViewbox.isValid() ? svgViewbox.height() / svgViewbox.width() : 0.0;
1911  }
1912  return mDefaultAspectRatio;
1913 }
1914 
1916 {
1917  bool aPreservedAspectRatio = preservedAspectRatio();
1918  if ( aPreservedAspectRatio && !par )
1919  {
1920  mFixedAspectRatio = mDefaultAspectRatio;
1921  }
1922  else if ( !aPreservedAspectRatio && par )
1923  {
1924  mFixedAspectRatio = 0.0;
1925  }
1926  return preservedAspectRatio();
1927 }
1928 
1929 
1931 {
1932  return QStringLiteral( "SvgMarker" );
1933 }
1934 
1936 {
1937  QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
1938  Q_UNUSED( context );
1939 }
1940 
1942 {
1943  Q_UNUSED( context );
1944 }
1945 
1947 {
1948  QPainter *p = context.renderContext().painter();
1949  if ( !p )
1950  return;
1951 
1952  bool hasDataDefinedSize = false;
1953  double scaledSize = calculateSize( context, hasDataDefinedSize );
1954  double size = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
1955 
1956  //don't render symbols with size below one or above 10,000 pixels
1957  if ( static_cast< int >( size ) < 1 || 10000.0 < size )
1958  {
1959  return;
1960  }
1961 
1962  p->save();
1963 
1964  bool hasDataDefinedAspectRatio = false;
1965  double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
1966 
1967  QPointF outputOffset;
1968  double angle = 0.0;
1969  calculateOffsetAndRotation( context, scaledSize, outputOffset, angle );
1970 
1971  p->translate( point + outputOffset );
1972 
1973  bool rotated = !qgsDoubleNear( angle, 0 );
1974  if ( rotated )
1975  p->rotate( angle );
1976 
1977  QString path = mPath;
1979  {
1980  context.setOriginalValueVariable( mPath );
1982  context.renderContext().pathResolver() );
1983  }
1984 
1985  double strokeWidth = mStrokeWidth;
1987  {
1988  context.setOriginalValueVariable( mStrokeWidth );
1990  }
1991  strokeWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
1992 
1993  QColor fillColor = mColor;
1995  {
1998  }
1999 
2000  QColor strokeColor = mStrokeColor;
2002  {
2005  }
2006 
2007  bool fitsInCache = true;
2008  bool usePict = true;
2009  double hwRatio = 1.0;
2010  if ( !context.renderContext().forceVectorOutput() && !rotated )
2011  {
2012  usePict = false;
2013  QImage img = QgsApplication::svgCache()->svgAsImage( path, size, fillColor, strokeColor, strokeWidth,
2014  context.renderContext().scaleFactor(), fitsInCache, aspectRatio );
2015  if ( fitsInCache && img.width() > 1 )
2016  {
2017  //consider transparency
2018  if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2019  {
2020  QImage transparentImage = img.copy();
2021  QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2022  p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
2023  hwRatio = static_cast< double >( transparentImage.height() ) / static_cast< double >( transparentImage.width() );
2024  }
2025  else
2026  {
2027  p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2028  hwRatio = static_cast< double >( img.height() ) / static_cast< double >( img.width() );
2029  }
2030  }
2031  }
2032 
2033  if ( usePict || !fitsInCache )
2034  {
2035  p->setOpacity( context.opacity() );
2036  QPicture pct = QgsApplication::svgCache()->svgAsPicture( path, size, fillColor, strokeColor, strokeWidth,
2037  context.renderContext().scaleFactor(), context.renderContext().forceVectorOutput(), aspectRatio );
2038  if ( pct.width() > 1 )
2039  {
2040  p->save();
2041  _fixQPictureDPI( p );
2042  p->drawPicture( 0, 0, pct );
2043  p->restore();
2044  hwRatio = static_cast< double >( pct.height() ) / static_cast< double >( pct.width() );
2045  }
2046  }
2047 
2048  if ( context.selected() )
2049  {
2050  QPen pen( context.renderContext().selectionColor() );
2051  double penWidth = context.renderContext().convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters );
2052  if ( penWidth > size / 20 )
2053  {
2054  // keep the pen width from covering symbol
2055  penWidth = size / 20;
2056  }
2057  double penOffset = penWidth / 2;
2058  pen.setWidth( penWidth );
2059  p->setPen( pen );
2060  p->setBrush( Qt::NoBrush );
2061  double wSize = size + penOffset;
2062  double hSize = size * hwRatio + penOffset;
2063  p->drawRect( QRectF( -wSize / 2.0, -hSize / 2.0, wSize, hSize ) );
2064  }
2065 
2066  p->restore();
2067 
2069  {
2070  // workaround issue with nested QPictures forgetting antialiasing flag - see https://issues.qgis.org/issues/14960
2071  p->setRenderHint( QPainter::Antialiasing );
2072  }
2073 
2074 }
2075 
2076 double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2077 {
2078  double scaledSize = mSize;
2080 
2081  bool ok = true;
2082  if ( hasDataDefinedSize )
2083  {
2084  context.setOriginalValueVariable( mSize );
2086  }
2087  else
2088  {
2090  if ( hasDataDefinedSize )
2091  {
2092  context.setOriginalValueVariable( mSize );
2094  }
2095  }
2096 
2097  if ( hasDataDefinedSize && ok )
2098  {
2099  switch ( mScaleMethod )
2100  {
2101  case QgsSymbol::ScaleArea:
2102  scaledSize = std::sqrt( scaledSize );
2103  break;
2105  break;
2106  }
2107  }
2108 
2109  return scaledSize;
2110 }
2111 
2112 double QgsSvgMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2113 {
2115  if ( !hasDataDefinedAspectRatio )
2116  return mFixedAspectRatio;
2117 
2118  if ( !mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyHeight ) && mFixedAspectRatio <= 0.0 )
2119  return 0.0;
2120 
2121  double scaledAspectRatio = mDefaultAspectRatio;
2122  if ( mFixedAspectRatio > 0.0 )
2123  scaledAspectRatio = mFixedAspectRatio;
2124 
2125  double defaultHeight = mSize * scaledAspectRatio;
2126  scaledAspectRatio = defaultHeight / scaledSize;
2127 
2128  bool ok = true;
2129  double scaledHeight = scaledSize * scaledAspectRatio;
2131  {
2132  context.setOriginalValueVariable( defaultHeight );
2133  scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyHeight, context.renderContext().expressionContext(), defaultHeight, &ok );
2134  }
2135 
2136  if ( hasDataDefinedAspectRatio && ok )
2137  {
2138  switch ( mScaleMethod )
2139  {
2140  case QgsSymbol::ScaleArea:
2141  scaledHeight = sqrt( scaledHeight );
2142  break;
2144  break;
2145  }
2146  }
2147 
2148  scaledAspectRatio = scaledHeight / scaledSize;
2149 
2150  return scaledAspectRatio;
2151 }
2152 
2153 void QgsSvgMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledSize, QPointF &offset, double &angle ) const
2154 {
2155  //offset
2156  double offsetX = 0;
2157  double offsetY = 0;
2158  markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
2159  offset = QPointF( offsetX, offsetY );
2160 
2161  angle = mAngle + mLineAngle;
2163  {
2164  context.setOriginalValueVariable( mAngle );
2166  }
2167 
2169  if ( hasDataDefinedRotation )
2170  {
2171  // For non-point markers, "dataDefinedRotation" means following the
2172  // shape (shape-data defined). For them, "field-data defined" does
2173  // not work at all. TODO: if "field-data defined" ever gets implemented
2174  // we'll need a way to distinguish here between the two, possibly
2175  // using another flag in renderHints()
2176  const QgsFeature *f = context.feature();
2177  if ( f )
2178  {
2179  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
2180  {
2181  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2182  angle += m2p.mapRotation();
2183  }
2184  }
2185  }
2186 
2187  if ( angle )
2188  offset = _rotatedOffset( offset, angle );
2189 }
2190 
2191 
2193 {
2194  QgsStringMap map;
2195  map[QStringLiteral( "name" )] = mPath;
2196  map[QStringLiteral( "size" )] = QString::number( mSize );
2197  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2198  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2199  map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2200  map[QStringLiteral( "angle" )] = QString::number( mAngle );
2201  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2202  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2203  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2204  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2205  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
2206  map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
2207  map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
2208  map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2209  map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2210  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
2211  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
2212  return map;
2213 }
2214 
2216 {
2218  m->setFixedAspectRatio( mFixedAspectRatio );
2219  m->setColor( mColor );
2220  m->setStrokeColor( mStrokeColor );
2221  m->setStrokeWidth( mStrokeWidth );
2222  m->setStrokeWidthUnit( mStrokeWidthUnit );
2223  m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
2224  m->setOffset( mOffset );
2225  m->setOffsetUnit( mOffsetUnit );
2227  m->setSizeUnit( mSizeUnit );
2232  copyPaintEffect( m );
2233  return m;
2234 }
2235 
2237 {
2239  mStrokeWidthUnit = unit;
2240 }
2241 
2243 {
2245  if ( unit != mStrokeWidthUnit )
2246  {
2248  }
2249  return unit;
2250 }
2251 
2253 {
2255  mStrokeWidthMapUnitScale = scale;
2256 }
2257 
2259 {
2260  if ( QgsMarkerSymbolLayer::mapUnitScale() == mStrokeWidthMapUnitScale )
2261  {
2262  return mStrokeWidthMapUnitScale;
2263  }
2264  return QgsMapUnitScale();
2265 }
2266 
2267 void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
2268 {
2269  // <Graphic>
2270  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2271  element.appendChild( graphicElem );
2272 
2273  // encode a parametric SVG reference
2275  double strokeWidth = QgsSymbolLayerUtils::rescaleUom( mStrokeWidth, mStrokeWidthUnit, props );
2276  QgsSymbolLayerUtils::parametricSvgToSld( doc, graphicElem, mPath, mColor, size, mStrokeColor, strokeWidth );
2277 
2278  // <Rotation>
2279  QString angleFunc;
2280  bool ok;
2281  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2282  if ( !ok )
2283  {
2284  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mAngle );
2285  }
2286  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2287  {
2288  angleFunc = QString::number( angle + mAngle );
2289  }
2290 
2291  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2292 
2293  // <Displacement>
2294  QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2295  QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
2296 }
2297 
2299 {
2300  QgsDebugMsg( "Entered." );
2301 
2302  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
2303  if ( graphicElem.isNull() )
2304  return nullptr;
2305 
2306  QString path, mimeType;
2307  QColor fillColor;
2308  double size;
2309 
2310  if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
2311  return nullptr;
2312 
2313  QString uom = element.attribute( QStringLiteral( "uom" ) );
2314  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
2315 
2316  if ( mimeType != QLatin1String( "image/svg+xml" ) )
2317  return nullptr;
2318 
2319  double angle = 0.0;
2320  QString angleFunc;
2321  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2322  {
2323  bool ok;
2324  double d = angleFunc.toDouble( &ok );
2325  if ( ok )
2326  angle = d;
2327  }
2328 
2329  QPointF offset;
2330  QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, offset );
2331 
2332  QgsSvgMarkerSymbolLayer *m = new QgsSvgMarkerSymbolLayer( path, size );
2333  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
2334  m->setFillColor( fillColor );
2335  //m->setStrokeColor( strokeColor );
2336  //m->setStrokeWidth( strokeWidth );
2337  m->setAngle( angle );
2338  m->setOffset( offset );
2339  return m;
2340 }
2341 
2342 bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
2343 {
2344  Q_UNUSED( layerName );
2345  Q_UNUSED( shift ); //todo...
2346 
2347  //size
2348  double size = mSize;
2349 
2350  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
2351 
2352  bool ok = true;
2353  if ( hasDataDefinedSize )
2354  {
2355  context.setOriginalValueVariable( mSize );
2357  }
2358 
2359  if ( hasDataDefinedSize && ok )
2360  {
2361  switch ( mScaleMethod )
2362  {
2363  case QgsSymbol::ScaleArea:
2364  size = std::sqrt( size );
2365  break;
2367  break;
2368  }
2369  }
2370 
2372  {
2373  size *= mmMapUnitScaleFactor;
2374  }
2375 
2376  double halfSize = size / 2.0;
2377 
2378  //offset, angle
2379  QPointF offset = mOffset;
2380 
2382  {
2384  QString offsetString = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext(), QString(), &ok );
2385  if ( ok )
2386  offset = QgsSymbolLayerUtils::decodePoint( offsetString );
2387  }
2388  double offsetX = offset.x();
2389  double offsetY = offset.y();
2391  {
2392  offsetX *= mmMapUnitScaleFactor;
2393  offsetY *= mmMapUnitScaleFactor;
2394  }
2395 
2396  QPointF outputOffset( offsetX, offsetY );
2397 
2398  double angle = mAngle + mLineAngle;
2400  {
2401  context.setOriginalValueVariable( mAngle );
2403  }
2404  //angle = -angle; //rotation in Qt is counterclockwise
2405  if ( angle )
2406  outputOffset = _rotatedOffset( outputOffset, angle );
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 = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
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 );
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)
Get 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)
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 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.
static QString svgSymbolPathToName(QString path, const QgsPathResolver &pathResolver)
Get SVG symbols&#39;s name from its path.
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)
Get 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:1114
void startRender(QgsSymbolRenderContext &context) override
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
Get 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
Return combination of flags used for rendering.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
static QString svgSymbolNameToPath(QString name, const QgsPathResolver &pathResolver)
Get SVG symbol&#39;s path from its name.
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:479
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)
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:251
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
Return current map rotation in degrees.
#define DEFAULT_FONTMARKER_SIZE
QColor strokeColor() const override
Get 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
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:1055
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 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)
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
Get 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
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)
Get SVG content.
bool setSubSymbol(QgsSymbol *symbol) override
set layer&#39;s subsymbol. takes ownership of the passed symbol
QColor fillColor() const override
Get 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 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
Get 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...