QGIS API Documentation  3.6.0-Noosa (5873452)
qgsmarkersymbollayer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmarkersymbollayer.cpp
3  ---------------------
4  begin : November 2009
5  copyright : (C) 2009 by Martin Dobias
6  email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgsmarkersymbollayer.h"
17 #include "qgssymbollayerutils.h"
18 
19 #include "qgsdxfexport.h"
20 #include "qgsdxfpaintdevice.h"
21 #include "qgsexpression.h"
22 #include "qgsimagecache.h"
23 #include "qgsimageoperation.h"
24 #include "qgsrendercontext.h"
25 #include "qgslogger.h"
26 #include "qgssvgcache.h"
27 #include "qgsunittypes.h"
28 
29 #include <QPainter>
30 #include <QSvgRenderer>
31 #include <QFileInfo>
32 #include <QDir>
33 #include <QDomDocument>
34 #include <QDomElement>
35 
36 #include <cmath>
37 
38 Q_GUI_EXPORT extern int qt_defaultDpiX();
39 Q_GUI_EXPORT extern int qt_defaultDpiY();
40 
41 static void _fixQPictureDPI( QPainter *p )
42 {
43  // QPicture makes an assumption that we drawing to it with system DPI.
44  // Then when being drawn, it scales the painter. The following call
45  // negates the effect. There is no way of setting QPicture's DPI.
46  // See QTBUG-20361
47  p->scale( static_cast< double >( qt_defaultDpiX() ) / p->device()->logicalDpiX(),
48  static_cast< double >( qt_defaultDpiY() ) / p->device()->logicalDpiY() );
49 }
50 
51 
53 
54 
55 //
56 // QgsSimpleMarkerSymbolLayerBase
57 //
58 
59 QList<QgsSimpleMarkerSymbolLayerBase::Shape> QgsSimpleMarkerSymbolLayerBase::availableShapes()
60 {
61  QList< Shape > shapes;
62  shapes << Square
63  << Diamond
64  << Pentagon
65  << Hexagon
66  << Triangle
68  << Star
69  << Arrow
70  << Circle
71  << Cross
72  << CrossFill
73  << Cross2
74  << Line
75  << ArrowHead
77  << SemiCircle
78  << ThirdCircle
79  << QuarterCircle
80  << QuarterSquare
81  << HalfSquare
85  return shapes;
86 }
87 
89  : mShape( shape )
90 {
91  mSize = size;
92  mAngle = angle;
93  mOffset = QPointF( 0, 0 );
97 }
98 
100 {
101  switch ( shape )
102  {
103  case Square:
104  case Diamond:
105  case Pentagon:
106  case Hexagon:
107  case Triangle:
108  case EquilateralTriangle:
109  case Star:
110  case Arrow:
111  case Circle:
112  case CrossFill:
113  case ArrowHeadFilled:
114  case SemiCircle:
115  case ThirdCircle:
116  case QuarterCircle:
117  case QuarterSquare:
118  case HalfSquare:
119  case DiagonalHalfSquare:
120  case RightHalfTriangle:
121  case LeftHalfTriangle:
122  return true;
123 
124  case Cross:
125  case Cross2:
126  case Line:
127  case ArrowHead:
128  return false;
129  }
130  return true;
131 }
132 
134 {
135  bool hasDataDefinedRotation = context.renderHints() & QgsSymbol::DynamicRotation
137  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
138 
139  // use either QPolygonF or QPainterPath for drawing
140  if ( !prepareMarkerShape( mShape ) ) // drawing as a polygon
141  {
142  prepareMarkerPath( mShape ); // drawing as a painter path
143  }
144 
145  QTransform transform;
146 
147  // scale the shape (if the size is not going to be modified)
148  if ( !hasDataDefinedSize )
149  {
150  double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
151  double half = scaledSize / 2.0;
152  transform.scale( half, half );
153  }
154 
155  // rotate if the rotation is not going to be changed during the rendering
156  if ( !hasDataDefinedRotation && !qgsDoubleNear( mAngle, 0.0 ) )
157  {
158  transform.rotate( mAngle );
159  }
160 
161  if ( !mPolygon.isEmpty() )
162  mPolygon = transform.map( mPolygon );
163  else
164  mPath = transform.map( mPath );
165 
167 }
168 
170 {
171  Q_UNUSED( context );
172 }
173 
175 {
176  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
177  //of the rendered point!
178 
179  QPainter *p = context.renderContext().painter();
180  if ( !p )
181  {
182  return;
183  }
184 
185  bool hasDataDefinedSize = false;
186  double scaledSize = calculateSize( context, hasDataDefinedSize );
187 
188  bool hasDataDefinedRotation = false;
189  QPointF offset;
190  double angle = 0;
191  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
192 
193  //data defined shape?
194  bool createdNewPath = false;
195  bool ok = true;
196  Shape symbol = mShape;
198  {
199  context.setOriginalValueVariable( encodeShape( symbol ) );
201  if ( exprVal.isValid() )
202  {
203  Shape decoded = decodeShape( exprVal.toString(), &ok );
204  if ( ok )
205  {
206  symbol = decoded;
207 
208  if ( !prepareMarkerShape( symbol ) ) // drawing as a polygon
209  {
210  prepareMarkerPath( symbol ); // drawing as a painter path
211  }
212  createdNewPath = true;
213  }
214  }
215  else
216  {
217  symbol = mShape;
218  }
219  }
220 
221  QTransform transform;
222 
223  // move to the desired position
224  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
225 
226  // resize if necessary
227  if ( hasDataDefinedSize || createdNewPath )
228  {
229  double s = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
230  double half = s / 2.0;
231  transform.scale( half, half );
232  }
233 
234  if ( !qgsDoubleNear( angle, 0.0 ) && ( hasDataDefinedRotation || createdNewPath ) )
235  transform.rotate( angle );
236 
237  //need to pass: symbol, polygon, path
238 
239  QPolygonF polygon;
240  QPainterPath path;
241  if ( !mPolygon.isEmpty() )
242  {
243  polygon = transform.map( mPolygon );
244  }
245  else
246  {
247  path = transform.map( mPath );
248  }
249  draw( context, symbol, polygon, path );
250 }
251 
253 {
254  bool hasDataDefinedSize = false;
255  double scaledSize = calculateSize( context, hasDataDefinedSize );
256 
257  bool hasDataDefinedRotation = false;
258  QPointF offset;
259  double angle = 0;
260  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
261 
262  scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
263 
264  QTransform transform;
265 
266  // move to the desired position
267  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
268 
269  if ( !qgsDoubleNear( angle, 0.0 ) )
270  transform.rotate( angle );
271 
272  return transform.mapRect( QRectF( -scaledSize / 2.0,
273  -scaledSize / 2.0,
274  scaledSize,
275  scaledSize ) );
276 }
277 
279 {
280  if ( ok )
281  *ok = true;
282  QString cleaned = name.toLower().trimmed();
283 
284  if ( cleaned == QLatin1String( "square" ) || cleaned == QLatin1String( "rectangle" ) )
285  return Square;
286  else if ( cleaned == QLatin1String( "diamond" ) )
287  return Diamond;
288  else if ( cleaned == QLatin1String( "pentagon" ) )
289  return Pentagon;
290  else if ( cleaned == QLatin1String( "hexagon" ) )
291  return Hexagon;
292  else if ( cleaned == QLatin1String( "triangle" ) )
293  return Triangle;
294  else if ( cleaned == QLatin1String( "equilateral_triangle" ) )
295  return EquilateralTriangle;
296  else if ( cleaned == QLatin1String( "star" ) || cleaned == QLatin1String( "regular_star" ) )
297  return Star;
298  else if ( cleaned == QLatin1String( "arrow" ) )
299  return Arrow;
300  else if ( cleaned == QLatin1String( "circle" ) )
301  return Circle;
302  else if ( cleaned == QLatin1String( "cross" ) )
303  return Cross;
304  else if ( cleaned == QLatin1String( "cross_fill" ) )
305  return CrossFill;
306  else if ( cleaned == QLatin1String( "cross2" ) || cleaned == QLatin1String( "x" ) )
307  return Cross2;
308  else if ( cleaned == QLatin1String( "line" ) )
309  return Line;
310  else if ( cleaned == QLatin1String( "arrowhead" ) )
311  return ArrowHead;
312  else if ( cleaned == QLatin1String( "filled_arrowhead" ) )
313  return ArrowHeadFilled;
314  else if ( cleaned == QLatin1String( "semi_circle" ) )
315  return SemiCircle;
316  else if ( cleaned == QLatin1String( "third_circle" ) )
317  return ThirdCircle;
318  else if ( cleaned == QLatin1String( "quarter_circle" ) )
319  return QuarterCircle;
320  else if ( cleaned == QLatin1String( "quarter_square" ) )
321  return QuarterSquare;
322  else if ( cleaned == QLatin1String( "half_square" ) )
323  return HalfSquare;
324  else if ( cleaned == QLatin1String( "diagonal_half_square" ) )
325  return DiagonalHalfSquare;
326  else if ( cleaned == QLatin1String( "right_half_triangle" ) )
327  return RightHalfTriangle;
328  else if ( cleaned == QLatin1String( "left_half_triangle" ) )
329  return LeftHalfTriangle;
330 
331  if ( ok )
332  *ok = false;
333  return Circle;
334 }
335 
337 {
338  switch ( shape )
339  {
340  case Square:
341  return QStringLiteral( "square" );
342  case QuarterSquare:
343  return QStringLiteral( "quarter_square" );
344  case HalfSquare:
345  return QStringLiteral( "half_square" );
346  case DiagonalHalfSquare:
347  return QStringLiteral( "diagonal_half_square" );
348  case Diamond:
349  return QStringLiteral( "diamond" );
350  case Pentagon:
351  return QStringLiteral( "pentagon" );
352  case Hexagon:
353  return QStringLiteral( "hexagon" );
354  case Triangle:
355  return QStringLiteral( "triangle" );
356  case EquilateralTriangle:
357  return QStringLiteral( "equilateral_triangle" );
358  case LeftHalfTriangle:
359  return QStringLiteral( "left_half_triangle" );
360  case RightHalfTriangle:
361  return QStringLiteral( "right_half_triangle" );
362  case Star:
363  return QStringLiteral( "star" );
364  case Arrow:
365  return QStringLiteral( "arrow" );
366  case ArrowHeadFilled:
367  return QStringLiteral( "filled_arrowhead" );
368  case CrossFill:
369  return QStringLiteral( "cross_fill" );
370  case Circle:
371  return QStringLiteral( "circle" );
372  case Cross:
373  return QStringLiteral( "cross" );
374  case Cross2:
375  return QStringLiteral( "cross2" );
376  case Line:
377  return QStringLiteral( "line" );
378  case ArrowHead:
379  return QStringLiteral( "arrowhead" );
380  case SemiCircle:
381  return QStringLiteral( "semi_circle" );
382  case ThirdCircle:
383  return QStringLiteral( "third_circle" );
384  case QuarterCircle:
385  return QStringLiteral( "quarter_circle" );
386  }
387  return QString();
388 }
389 
391 {
392  return shapeToPolygon( shape, mPolygon );
393 }
394 
396 {
397  polygon.clear();
398 
399  switch ( shape )
400  {
401  case Square:
402  polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 1, 1 ) ) );
403  return true;
404 
405  case QuarterSquare:
406  polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 0 ) ) );
407  return true;
408 
409  case HalfSquare:
410  polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 1 ) ) );
411  return true;
412 
413  case DiagonalHalfSquare:
414  polygon << QPointF( -1, -1 ) << QPointF( 1, 1 ) << QPointF( -1, 1 ) << QPointF( -1, -1 );
415  return true;
416 
417  case Diamond:
418  polygon << QPointF( -1, 0 ) << QPointF( 0, 1 )
419  << QPointF( 1, 0 ) << QPointF( 0, -1 ) << QPointF( -1, 0 );
420  return true;
421 
422  case Pentagon:
423  /* angular-representation of hardcoded values used
424  polygon << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288.0 ) ) )
425  << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) )
426  << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) )
427  << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) )
428  << QPointF( 0, -1 ); */
429  polygon << QPointF( -0.9511, -0.3090 )
430  << QPointF( -0.5878, 0.8090 )
431  << QPointF( 0.5878, 0.8090 )
432  << QPointF( 0.9511, -0.3090 )
433  << QPointF( 0, -1 )
434  << QPointF( -0.9511, -0.3090 );
435  return true;
436 
437  case Hexagon:
438  /* angular-representation of hardcoded values used
439  polygon << QPointF( std::sin( DEG2RAD( 300.0 ) ), - std::cos( DEG2RAD( 300.0 ) ) )
440  << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
441  << QPointF( std::sin( DEG2RAD( 180.0 ) ), - std::cos( DEG2RAD( 180.0 ) ) )
442  << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
443  << QPointF( std::sin( DEG2RAD( 60.0 ) ), - std::cos( DEG2RAD( 60.0 ) ) )
444  << QPointF( 0, -1 ); */
445  polygon << QPointF( -0.8660, -0.5 )
446  << QPointF( -0.8660, 0.5 )
447  << QPointF( 0, 1 )
448  << QPointF( 0.8660, 0.5 )
449  << QPointF( 0.8660, -0.5 )
450  << QPointF( 0, -1 )
451  << QPointF( -0.8660, -0.5 );
452  return true;
453 
454  case Triangle:
455  polygon << QPointF( -1, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
456  return true;
457 
458  case EquilateralTriangle:
459  /* angular-representation of hardcoded values used
460  polygon << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
461  << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
462  << QPointF( 0, -1 ); */
463  polygon << QPointF( -0.8660, 0.5 )
464  << QPointF( 0.8660, 0.5 )
465  << QPointF( 0, -1 )
466  << QPointF( -0.8660, 0.5 );
467  return true;
468 
469  case LeftHalfTriangle:
470  polygon << QPointF( 0, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( 0, 1 );
471  return true;
472 
473  case RightHalfTriangle:
474  polygon << QPointF( -1, 1 ) << QPointF( 0, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
475  return true;
476 
477  case Star:
478  {
479  double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
480 
481  polygon << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ) // 324
482  << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288 ) ) ) // 288
483  << QPointF( inner_r * std::sin( DEG2RAD( 252.0 ) ), - inner_r * std::cos( DEG2RAD( 252.0 ) ) ) // 252
484  << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) ) // 216
485  << QPointF( 0, inner_r ) // 180
486  << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) ) // 144
487  << QPointF( inner_r * std::sin( DEG2RAD( 108.0 ) ), - inner_r * std::cos( DEG2RAD( 108.0 ) ) ) // 108
488  << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) ) // 72
489  << QPointF( inner_r * std::sin( DEG2RAD( 36.0 ) ), - inner_r * std::cos( DEG2RAD( 36.0 ) ) ) // 36
490  << QPointF( 0, -1 )
491  << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ); // 324; // 0
492  return true;
493  }
494 
495  case Arrow:
496  polygon << QPointF( 0, -1 )
497  << QPointF( 0.5, -0.5 )
498  << QPointF( 0.25, -0.5 )
499  << QPointF( 0.25, 1 )
500  << QPointF( -0.25, 1 )
501  << QPointF( -0.25, -0.5 )
502  << QPointF( -0.5, -0.5 )
503  << QPointF( 0, -1 );
504  return true;
505 
506  case ArrowHeadFilled:
507  polygon << QPointF( 0, 0 ) << QPointF( -1, 1 ) << QPointF( -1, -1 ) << QPointF( 0, 0 );
508  return true;
509 
510  case CrossFill:
511  polygon << QPointF( -1, -0.2 )
512  << QPointF( -1, -0.2 )
513  << QPointF( -1, 0.2 )
514  << QPointF( -0.2, 0.2 )
515  << QPointF( -0.2, 1 )
516  << QPointF( 0.2, 1 )
517  << QPointF( 0.2, 0.2 )
518  << QPointF( 1, 0.2 )
519  << QPointF( 1, -0.2 )
520  << QPointF( 0.2, -0.2 )
521  << QPointF( 0.2, -1 )
522  << QPointF( -0.2, -1 )
523  << QPointF( -0.2, -0.2 )
524  << QPointF( -1, -0.2 );
525  return true;
526 
527  case Circle:
528  case Cross:
529  case Cross2:
530  case Line:
531  case ArrowHead:
532  case SemiCircle:
533  case ThirdCircle:
534  case QuarterCircle:
535  return false;
536  }
537 
538  return false;
539 }
540 
542 {
543  mPath = QPainterPath();
544 
545  switch ( symbol )
546  {
547  case Circle:
548 
549  mPath.addEllipse( QRectF( -1, -1, 2, 2 ) ); // x,y,w,h
550  return true;
551 
552  case SemiCircle:
553  mPath.arcTo( -1, -1, 2, 2, 0, 180 );
554  mPath.lineTo( 0, 0 );
555  return true;
556 
557  case ThirdCircle:
558  mPath.arcTo( -1, -1, 2, 2, 90, 120 );
559  mPath.lineTo( 0, 0 );
560  return true;
561 
562  case QuarterCircle:
563  mPath.arcTo( -1, -1, 2, 2, 90, 90 );
564  mPath.lineTo( 0, 0 );
565  return true;
566 
567  case Cross:
568  mPath.moveTo( -1, 0 );
569  mPath.lineTo( 1, 0 ); // horizontal
570  mPath.moveTo( 0, -1 );
571  mPath.lineTo( 0, 1 ); // vertical
572  return true;
573 
574  case Cross2:
575  mPath.moveTo( -1, -1 );
576  mPath.lineTo( 1, 1 );
577  mPath.moveTo( 1, -1 );
578  mPath.lineTo( -1, 1 );
579  return true;
580 
581  case Line:
582  mPath.moveTo( 0, -1 );
583  mPath.lineTo( 0, 1 ); // vertical line
584  return true;
585 
586  case ArrowHead:
587  mPath.moveTo( -1, -1 );
588  mPath.lineTo( 0, 0 );
589  mPath.lineTo( -1, 1 );
590  return true;
591 
592  case Square:
593  case QuarterSquare:
594  case HalfSquare:
595  case DiagonalHalfSquare:
596  case Diamond:
597  case Pentagon:
598  case Hexagon:
599  case Triangle:
600  case EquilateralTriangle:
601  case LeftHalfTriangle:
602  case RightHalfTriangle:
603  case Star:
604  case Arrow:
605  case ArrowHeadFilled:
606  case CrossFill:
607  return false;
608  }
609  return false;
610 }
611 
612 double QgsSimpleMarkerSymbolLayerBase::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
613 {
614  double scaledSize = mSize;
615 
617  bool ok = true;
618  if ( hasDataDefinedSize )
619  {
620  context.setOriginalValueVariable( mSize );
622  mSize, &ok );
623  }
624 
625  if ( hasDataDefinedSize && ok )
626  {
627  switch ( mScaleMethod )
628  {
630  scaledSize = std::sqrt( scaledSize );
631  break;
633  break;
634  }
635  }
636 
637  return scaledSize;
638 }
639 
640 void QgsSimpleMarkerSymbolLayerBase::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle ) const
641 {
642  //offset
643  double offsetX = 0;
644  double offsetY = 0;
645  markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
646  offset = QPointF( offsetX, offsetY );
647 
648  //angle
649  bool ok = true;
650  angle = mAngle + mLineAngle;
651  bool usingDataDefinedRotation = false;
653  {
654  context.setOriginalValueVariable( angle );
656  usingDataDefinedRotation = ok;
657  }
658 
659  hasDataDefinedRotation = context.renderHints() & QgsSymbol::DynamicRotation || usingDataDefinedRotation;
660  if ( hasDataDefinedRotation )
661  {
662  // For non-point markers, "dataDefinedRotation" means following the
663  // shape (shape-data defined). For them, "field-data defined" does
664  // not work at all. TODO: if "field-data defined" ever gets implemented
665  // we'll need a way to distinguish here between the two, possibly
666  // using another flag in renderHints()
667  const QgsFeature *f = context.feature();
668  if ( f )
669  {
670  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
671  {
672  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
673  angle += m2p.mapRotation();
674  }
675  }
676  }
677 
678  if ( angle )
679  offset = _rotatedOffset( offset, angle );
680 }
681 
682 
683 //
684 // QgsSimpleMarkerSymbolLayer
685 //
686 
688  : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
689  , mStrokeColor( strokeColor )
690  , mPenJoinStyle( penJoinStyle )
691 {
692  mColor = color;
693 }
694 
696 {
697  Shape shape = Circle;
700  Qt::PenJoinStyle penJoinStyle = DEFAULT_SIMPLEMARKER_JOINSTYLE;
704 
705  if ( props.contains( QStringLiteral( "name" ) ) )
706  {
707  shape = decodeShape( props[QStringLiteral( "name" )] );
708  }
709  if ( props.contains( QStringLiteral( "color" ) ) )
710  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] );
711  if ( props.contains( QStringLiteral( "color_border" ) ) )
712  {
713  //pre 2.5 projects use "color_border"
714  strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color_border" )] );
715  }
716  else if ( props.contains( QStringLiteral( "outline_color" ) ) )
717  {
718  strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )] );
719  }
720  else if ( props.contains( QStringLiteral( "line_color" ) ) )
721  {
722  strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )] );
723  }
724  if ( props.contains( QStringLiteral( "joinstyle" ) ) )
725  {
726  penJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )] );
727  }
728  if ( props.contains( QStringLiteral( "size" ) ) )
729  size = props[QStringLiteral( "size" )].toDouble();
730  if ( props.contains( QStringLiteral( "angle" ) ) )
731  angle = props[QStringLiteral( "angle" )].toDouble();
732  if ( props.contains( QStringLiteral( "scale_method" ) ) )
733  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )] );
734 
735  QgsSimpleMarkerSymbolLayer *m = new QgsSimpleMarkerSymbolLayer( shape, size, angle, scaleMethod, color, strokeColor, penJoinStyle );
736  if ( props.contains( QStringLiteral( "offset" ) ) )
737  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
738  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
739  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
740  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
741  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
742  if ( props.contains( QStringLiteral( "size_unit" ) ) )
743  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
744  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
745  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
746 
747  if ( props.contains( QStringLiteral( "outline_style" ) ) )
748  {
749  m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "outline_style" )] ) );
750  }
751  else if ( props.contains( QStringLiteral( "line_style" ) ) )
752  {
753  m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "line_style" )] ) );
754  }
755  if ( props.contains( QStringLiteral( "outline_width" ) ) )
756  {
757  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
758  }
759  else if ( props.contains( QStringLiteral( "line_width" ) ) )
760  {
761  m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
762  }
763  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
764  {
765  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )] ) );
766  }
767  if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
768  {
769  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )] ) );
770  }
771  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
772  {
773  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )] ) );
774  }
775 
776  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
777  {
778  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
779  }
780  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
781  {
782  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
783  }
784 
786 
787  return m;
788 }
789 
790 
792 {
793  return QStringLiteral( "SimpleMarker" );
794 }
795 
797 {
799 
800  QColor brushColor = mColor;
801  QColor penColor = mStrokeColor;
802 
803  brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
804  penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
805 
806  mBrush = QBrush( brushColor );
807  mPen = QPen( penColor );
808  mPen.setStyle( mStrokeStyle );
809  mPen.setJoinStyle( mPenJoinStyle );
811 
812  QColor selBrushColor = context.renderContext().selectionColor();
813  QColor selPenColor = selBrushColor == mColor ? selBrushColor : mStrokeColor;
814  if ( context.opacity() < 1 )
815  {
816  selBrushColor.setAlphaF( context.opacity() );
817  selPenColor.setAlphaF( context.opacity() );
818  }
819  mSelBrush = QBrush( selBrushColor );
820  mSelPen = QPen( selPenColor );
821  mSelPen.setStyle( mStrokeStyle );
823 
825  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
826 
827  // use caching only when:
828  // - size, rotation, shape, color, stroke color is not data-defined
829  // - drawing to screen (not printer)
830  mUsingCache = !hasDataDefinedRotation && !hasDataDefinedSize && !context.renderContext().forceVectorOutput()
834 
835  if ( !shapeIsFilled( mShape ) )
836  {
837  // some markers can't be drawn as a polygon (circle, cross)
838  // For these set the selected stroke color to the selected color
839  mSelPen.setColor( selBrushColor );
840  }
841 
842 
843  if ( mUsingCache )
844  {
845  if ( !prepareCache( context ) )
846  {
847  mUsingCache = false;
848  }
849  }
850  else
851  {
852  mCache = QImage();
853  mSelCache = QImage();
854  }
855 }
856 
857 
859 {
860  double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
861 
862  // calculate necessary image size for the cache
863  double pw = static_cast< int >( std::round( ( ( qgsDoubleNear( mPen.widthF(), 0.0 ) ? 1 : mPen.widthF() * 4 ) + 1 ) ) ) / 2 * 2; // make even (round up); handle cosmetic pen
864  int imageSize = ( static_cast< int >( scaledSize ) + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width
865  double center = imageSize / 2.0;
866 
867  if ( imageSize > MAXIMUM_CACHE_WIDTH )
868  {
869  return false;
870  }
871 
872  mCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
873  mCache.fill( 0 );
874 
875  bool needsBrush = shapeIsFilled( mShape );
876 
877  QPainter p;
878  p.begin( &mCache );
879  p.setRenderHint( QPainter::Antialiasing );
880  p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
881  p.setPen( mPen );
882  p.translate( QPointF( center, center ) );
883  drawMarker( &p, context );
884  p.end();
885 
886  // Construct the selected version of the Cache
887 
888  QColor selColor = context.renderContext().selectionColor();
889 
890  mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
891  mSelCache.fill( 0 );
892 
893  p.begin( &mSelCache );
894  p.setRenderHint( QPainter::Antialiasing );
895  p.setBrush( needsBrush ? mSelBrush : Qt::NoBrush );
896  p.setPen( mSelPen );
897  p.translate( QPointF( center, center ) );
898  drawMarker( &p, context );
899  p.end();
900 
901  // Check that the selected version is different. If not, then re-render,
902  // filling the background with the selection color and using the normal
903  // colors for the symbol .. could be ugly!
904 
905  if ( mSelCache == mCache )
906  {
907  p.begin( &mSelCache );
908  p.setRenderHint( QPainter::Antialiasing );
909  p.fillRect( 0, 0, imageSize, imageSize, selColor );
910  p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
911  p.setPen( mPen );
912  p.translate( QPointF( center, center ) );
913  drawMarker( &p, context );
914  p.end();
915  }
916 
917  return true;
918 }
919 
920 void QgsSimpleMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, QgsSimpleMarkerSymbolLayerBase::Shape shape, const QPolygonF &polygon, const QPainterPath &path )
921 {
922  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
923  //of the rendered point!
924 
925  QPainter *p = context.renderContext().painter();
926  if ( !p )
927  {
928  return;
929  }
930 
931  bool ok = true;
933  {
936  if ( ok )
937  mBrush.setColor( c );
938  }
940  {
943  if ( ok )
944  {
945  mPen.setColor( c );
946  mSelPen.setColor( c );
947  }
948  }
950  {
953  if ( ok )
954  {
955  mPen.setWidthF( context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
957  }
958  }
960  {
963  if ( ok )
964  {
965  mPen.setStyle( QgsSymbolLayerUtils::decodePenStyle( strokeStyle ) );
966  mSelPen.setStyle( QgsSymbolLayerUtils::decodePenStyle( strokeStyle ) );
967  }
968  }
970  {
973  if ( ok )
974  {
975  mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
976  mSelPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
977  }
978  }
979 
980  if ( shapeIsFilled( shape ) )
981  {
982  p->setBrush( context.selected() ? mSelBrush : mBrush );
983  }
984  else
985  {
986  p->setBrush( Qt::NoBrush );
987  }
988  p->setPen( context.selected() ? mSelPen : mPen );
989 
990  if ( !polygon.isEmpty() )
991  p->drawPolygon( polygon );
992  else
993  p->drawPath( path );
994 }
995 
997 {
998  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
999  //of the rendered point!
1000 
1001  QPainter *p = context.renderContext().painter();
1002  if ( !p )
1003  {
1004  return;
1005  }
1006 
1007  if ( mUsingCache )
1008  {
1009  QImage &img = context.selected() ? mSelCache : mCache;
1010  double s = img.width();
1011 
1012  bool hasDataDefinedSize = false;
1013  double scaledSize = calculateSize( context, hasDataDefinedSize );
1014 
1015  bool hasDataDefinedRotation = false;
1016  QPointF offset;
1017  double angle = 0;
1018  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
1019 
1020  p->drawImage( QRectF( point.x() - s / 2.0 + offset.x(),
1021  point.y() - s / 2.0 + offset.y(),
1022  s, s ), img );
1023  }
1024  else
1025  {
1027  }
1028 }
1029 
1031 {
1032  QgsStringMap map;
1033  map[QStringLiteral( "name" )] = encodeShape( mShape );
1034  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
1035  map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
1036  map[QStringLiteral( "size" )] = QString::number( mSize );
1037  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1038  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1039  map[QStringLiteral( "angle" )] = QString::number( mAngle );
1040  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1041  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1042  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1043  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1044  map[QStringLiteral( "outline_style" )] = QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle );
1045  map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
1046  map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
1047  map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
1048  map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
1049  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1050  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1051  return map;
1052 }
1053 
1055 {
1057  m->setOffset( mOffset );
1058  m->setSizeUnit( mSizeUnit );
1060  m->setOffsetUnit( mOffsetUnit );
1069  copyPaintEffect( m );
1070  return m;
1071 }
1072 
1073 void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
1074 {
1075  // <Graphic>
1076  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
1077  element.appendChild( graphicElem );
1078 
1081  QgsSymbolLayerUtils::wellKnownMarkerToSld( doc, graphicElem, encodeShape( mShape ), mColor, mStrokeColor, mStrokeStyle, strokeWidth, size );
1082 
1083  // <Rotation>
1084  QString angleFunc;
1085  bool ok;
1086  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
1087  if ( !ok )
1088  {
1089  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mAngle );
1090  }
1091  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
1092  {
1093  angleFunc = QString::number( angle + mAngle );
1094  }
1095  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
1096 
1097  // <Displacement>
1099  QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
1100 }
1101 
1102 QString QgsSimpleMarkerSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
1103 {
1104  Q_UNUSED( mmScaleFactor );
1105  Q_UNUSED( mapUnitScaleFactor );
1106 #if 0
1107  QString ogrType = "3"; //default is circle
1108  if ( mName == "square" )
1109  {
1110  ogrType = "5";
1111  }
1112  else if ( mName == "triangle" )
1113  {
1114  ogrType = "7";
1115  }
1116  else if ( mName == "star" )
1117  {
1118  ogrType = "9";
1119  }
1120  else if ( mName == "circle" )
1121  {
1122  ogrType = "3";
1123  }
1124  else if ( mName == "cross" )
1125  {
1126  ogrType = "0";
1127  }
1128  else if ( mName == "x" || mName == "cross2" )
1129  {
1130  ogrType = "1";
1131  }
1132  else if ( mName == "line" )
1133  {
1134  ogrType = "10";
1135  }
1136 
1137  QString ogrString;
1138  ogrString.append( "SYMBOL(" );
1139  ogrString.append( "id:" );
1140  ogrString.append( '\"' );
1141  ogrString.append( "ogr-sym-" );
1142  ogrString.append( ogrType );
1143  ogrString.append( '\"' );
1144  ogrString.append( ",c:" );
1145  ogrString.append( mColor.name() );
1146  ogrString.append( ",o:" );
1147  ogrString.append( mStrokeColor.name() );
1148  ogrString.append( QString( ",s:%1mm" ).arg( mSize ) );
1149  ogrString.append( ')' );
1150  return ogrString;
1151 #endif //0
1152 
1153  QString ogrString;
1154  ogrString.append( "PEN(" );
1155  ogrString.append( "c:" );
1156  ogrString.append( mColor.name() );
1157  ogrString.append( ",w:" );
1158  ogrString.append( QString::number( mSize ) );
1159  ogrString.append( "mm" );
1160  ogrString.append( ")" );
1161  return ogrString;
1162 }
1163 
1165 {
1166  QgsDebugMsg( QStringLiteral( "Entered." ) );
1167 
1168  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
1169  if ( graphicElem.isNull() )
1170  return nullptr;
1171 
1172  QString name = QStringLiteral( "square" );
1173  QColor color, strokeColor;
1174  double strokeWidth, size;
1175  Qt::PenStyle strokeStyle;
1176 
1177  if ( !QgsSymbolLayerUtils::wellKnownMarkerFromSld( graphicElem, name, color, strokeColor, strokeStyle, strokeWidth, size ) )
1178  return nullptr;
1179 
1180  double angle = 0.0;
1181  QString angleFunc;
1182  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
1183  {
1184  bool ok;
1185  double d = angleFunc.toDouble( &ok );
1186  if ( ok )
1187  angle = d;
1188  }
1189 
1190  QPointF offset;
1191  QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, offset );
1192 
1193  Shape shape = decodeShape( name );
1194 
1195  QString uom = element.attribute( QStringLiteral( "uom" ) );
1196  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
1197  offset.setX( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.x() ) );
1198  offset.setY( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.y() ) );
1199 
1201  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
1202  m->setColor( color );
1203  m->setStrokeColor( strokeColor );
1204  m->setAngle( angle );
1205  m->setOffset( offset );
1206  m->setStrokeStyle( strokeStyle );
1207  m->setStrokeWidth( strokeWidth );
1208  return m;
1209 }
1210 
1212 {
1213  Q_UNUSED( context );
1214 
1215  if ( mPolygon.count() != 0 )
1216  {
1217  p->drawPolygon( mPolygon );
1218  }
1219  else
1220  {
1221  p->drawPath( mPath );
1222  }
1223 }
1224 
1225 bool QgsSimpleMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
1226 {
1227  Q_UNUSED( mmMapUnitScaleFactor );
1228 
1229  //data defined size?
1230  double size = mSize;
1231 
1232  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
1233 
1234  //data defined size
1235  bool ok = true;
1236  if ( hasDataDefinedSize )
1237  {
1239 
1240  if ( ok )
1241  {
1242  switch ( mScaleMethod )
1243  {
1244  case QgsSymbol::ScaleArea:
1245  size = std::sqrt( size );
1246  break;
1248  break;
1249  }
1250  }
1251 
1253  }
1255  {
1257  }
1258  double halfSize = size / 2.0;
1259 
1260  //strokeWidth
1261  double strokeWidth = mStrokeWidth;
1262 
1264  {
1267  }
1270  {
1272  }
1273 
1274  //color
1275  QColor pc = mPen.color();
1276  QColor bc = mBrush.color();
1278  {
1281  }
1283  {
1286  }
1287 
1288  //offset
1289  double offsetX = 0;
1290  double offsetY = 0;
1291  markerOffset( context, offsetX, offsetY );
1292  offsetX *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1293  offsetY *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1294 
1295 
1296  QPointF off( offsetX, offsetY );
1297 
1298  //angle
1299  double angle = mAngle + mLineAngle;
1301  {
1302  context.setOriginalValueVariable( mAngle );
1304  }
1305 
1306  Shape shape = mShape;
1308  {
1309  context.setOriginalValueVariable( encodeShape( shape ) );
1310  QString shapeName = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyName, context.renderContext().expressionContext(), QString(), &ok );
1311  if ( ok )
1312  {
1313  shape = decodeShape( shapeName, &ok );
1314  if ( !ok )
1315  shape = mShape;
1316  }
1317  }
1318 
1319  if ( angle )
1320  off = _rotatedOffset( off, angle );
1321 
1323 
1324  QTransform t;
1325  t.translate( shift.x() + off.x(), shift.y() - off.y() );
1326 
1327  if ( !qgsDoubleNear( angle, 0.0 ) )
1328  t.rotate( angle );
1329 
1330  QPolygonF polygon;
1331  if ( shapeToPolygon( shape, polygon ) )
1332  {
1333  t.scale( halfSize, -halfSize );
1334 
1335  polygon = t.map( polygon );
1336 
1337  QgsPointSequence p;
1338  p.reserve( polygon.size() );
1339  for ( int i = 0; i < polygon.size(); i++ )
1340  {
1341  p << QgsPoint( polygon[i] );
1342  }
1343 
1344  if ( mBrush.style() != Qt::NoBrush )
1345  e.writePolygon( QgsRingSequence() << p, layerName, QStringLiteral( "SOLID" ), bc );
1346  if ( mPen.style() != Qt::NoPen )
1347  e.writePolyline( p, layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1348  }
1349  else if ( shape == Circle )
1350  {
1351  shift += QPointF( off.x(), -off.y() );
1352  if ( mBrush.style() != Qt::NoBrush )
1353  e.writeFilledCircle( layerName, bc, QgsPoint( shift ), halfSize );
1354  if ( mPen.style() != Qt::NoPen )
1355  e.writeCircle( layerName, pc, QgsPoint( shift ), halfSize, QStringLiteral( "CONTINUOUS" ), strokeWidth );
1356  }
1357  else if ( shape == Line )
1358  {
1359  QPointF pt1 = t.map( QPointF( 0, -halfSize ) );
1360  QPointF pt2 = t.map( QPointF( 0, halfSize ) );
1361 
1362  if ( mPen.style() != Qt::NoPen )
1363  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1364  }
1365  else if ( shape == Cross )
1366  {
1367  if ( mPen.style() != Qt::NoPen )
1368  {
1369  QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
1370  QPointF pt2 = t.map( QPointF( halfSize, 0 ) );
1371  QPointF pt3 = t.map( QPointF( 0, -halfSize ) );
1372  QPointF pt4 = t.map( QPointF( 0, halfSize ) );
1373 
1374  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1375  e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1376  }
1377  }
1378  else if ( shape == Cross2 )
1379  {
1380  if ( mPen.style() != Qt::NoPen )
1381  {
1382  QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
1383  QPointF pt2 = t.map( QPointF( halfSize, halfSize ) );
1384  QPointF pt3 = t.map( QPointF( halfSize, -halfSize ) );
1385  QPointF pt4 = t.map( QPointF( -halfSize, halfSize ) );
1386 
1387  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1388  e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1389  }
1390  }
1391  else if ( shape == ArrowHead )
1392  {
1393  if ( mPen.style() != Qt::NoPen )
1394  {
1395  QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
1396  QPointF pt2 = t.map( QPointF( 0, 0 ) );
1397  QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
1398 
1399  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1400  e.writeLine( QgsPoint( pt3 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1401  }
1402  }
1403  else
1404  {
1405  QgsDebugMsg( QStringLiteral( "Unsupported dxf marker name %1" ).arg( encodeShape( shape ) ) );
1406  return false;
1407  }
1408 
1409  return true;
1410 }
1411 
1412 
1414 {
1416  mStrokeWidthUnit = unit;
1417 }
1418 
1420 {
1422  {
1423  return mStrokeWidthUnit;
1424  }
1426 }
1427 
1429 {
1431  mStrokeWidthMapUnitScale = scale;
1432 }
1433 
1435 {
1437  {
1438  return mStrokeWidthMapUnitScale;
1439  }
1440  return QgsMapUnitScale();
1441 }
1442 
1444 {
1445  QRectF symbolBounds = QgsSimpleMarkerSymbolLayerBase::bounds( point, context );
1446 
1447  // need to account for stroke width
1448  double penWidth = 0.0;
1449  bool ok = true;
1451  {
1454  if ( ok )
1455  {
1456  penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
1457  }
1458  }
1460  {
1463  if ( ok && strokeStyle == QLatin1String( "no" ) )
1464  {
1465  penWidth = 0.0;
1466  }
1467  }
1468  //antialiasing, add 1 pixel
1469  penWidth += 1;
1470 
1471  //extend bounds by pen width / 2.0
1472  symbolBounds.adjust( -penWidth / 2.0, -penWidth / 2.0,
1473  penWidth / 2.0, penWidth / 2.0 );
1474 
1475  return symbolBounds;
1476 }
1477 
1479 {
1480  if ( shapeIsFilled( mShape ) )
1481  {
1482  setFillColor( color );
1483  }
1484  else
1485  {
1486  setStrokeColor( color );
1487  }
1488 }
1489 
1491 {
1492  if ( shapeIsFilled( mShape ) )
1493  {
1494  return fillColor();
1495  }
1496  else
1497  {
1498  return strokeColor();
1499  }
1500 }
1501 
1502 
1503 
1504 
1505 //
1506 // QgsFilledMarkerSymbolLayer
1507 //
1508 
1510  : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
1511 {
1512  mFill.reset( static_cast<QgsFillSymbol *>( QgsFillSymbol::createSimple( QgsStringMap() ) ) );
1513 }
1514 
1516 {
1517  QString name = DEFAULT_SIMPLEMARKER_NAME;
1521 
1522  if ( props.contains( QStringLiteral( "name" ) ) )
1523  name = props[QStringLiteral( "name" )];
1524  if ( props.contains( QStringLiteral( "size" ) ) )
1525  size = props[QStringLiteral( "size" )].toDouble();
1526  if ( props.contains( QStringLiteral( "angle" ) ) )
1527  angle = props[QStringLiteral( "angle" )].toDouble();
1528  if ( props.contains( QStringLiteral( "scale_method" ) ) )
1529  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )] );
1530 
1531  QgsFilledMarkerSymbolLayer *m = new QgsFilledMarkerSymbolLayer( decodeShape( name ), size, angle, scaleMethod );
1532  if ( props.contains( QStringLiteral( "offset" ) ) )
1533  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
1534  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1535  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
1536  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1537  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
1538  if ( props.contains( QStringLiteral( "size_unit" ) ) )
1539  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
1540  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1541  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
1542  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1543  {
1544  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1545  }
1546  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1547  {
1548  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1549  }
1550 
1552 
1553  m->restoreOldDataDefinedProperties( props );
1554 
1555  return m;
1556 }
1557 
1559 {
1560  return QStringLiteral( "FilledMarker" );
1561 }
1562 
1564 {
1565  if ( mFill )
1566  {
1567  mFill->startRender( context.renderContext(), context.fields() );
1568  }
1569 
1571 }
1572 
1574 {
1575  if ( mFill )
1576  {
1577  mFill->stopRender( context.renderContext() );
1578  }
1579 }
1580 
1582 {
1583  QgsStringMap map;
1584  map[QStringLiteral( "name" )] = encodeShape( mShape );
1585  map[QStringLiteral( "size" )] = QString::number( mSize );
1586  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1587  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1588  map[QStringLiteral( "angle" )] = QString::number( mAngle );
1589  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1590  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1591  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1592  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1593  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1594  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1595 
1596  if ( mFill )
1597  {
1598  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mFill->color() );
1599  }
1600  return map;
1601 }
1602 
1604 {
1606  copyPaintEffect( m );
1608  m->setSubSymbol( mFill->clone() );
1609  return m;
1610 }
1611 
1613 {
1614  return mFill.get();
1615 }
1616 
1618 {
1619  if ( symbol && symbol->type() == QgsSymbol::Fill )
1620  {
1621  mFill.reset( static_cast<QgsFillSymbol *>( symbol ) );
1622  return true;
1623  }
1624  else
1625  {
1626  delete symbol;
1627  return false;
1628  }
1629 }
1630 
1632 {
1633  if ( mFill )
1634  {
1635  return QgsSymbolLayerUtils::estimateMaxSymbolBleed( mFill.get(), context );
1636  }
1637  return 0;
1638 }
1639 
1641 {
1642  QSet<QString> attr = QgsSimpleMarkerSymbolLayerBase::usedAttributes( context );
1643  if ( mFill )
1644  attr.unite( mFill->usedAttributes( context ) );
1645  return attr;
1646 }
1647 
1649 {
1651  return true;
1652  if ( mFill && mFill->hasDataDefinedProperties() )
1653  return true;
1654  return false;
1655 }
1656 
1658 {
1659  mColor = c;
1660  if ( mFill )
1661  mFill->setColor( c );
1662 }
1663 
1665 {
1666  return mFill ? mFill->color() : mColor;
1667 }
1668 
1669 void QgsFilledMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, QgsSimpleMarkerSymbolLayerBase::Shape shape, const QPolygonF &polygon, const QPainterPath &path )
1670 {
1671  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1672  //of the rendered point!
1673 
1674  QPainter *p = context.renderContext().painter();
1675  if ( !p )
1676  {
1677  return;
1678  }
1679 
1680  if ( shapeIsFilled( shape ) )
1681  {
1682  p->setBrush( Qt::red );
1683  }
1684  else
1685  {
1686  p->setBrush( Qt::NoBrush );
1687  }
1688  p->setPen( Qt::black );
1689 
1690  if ( !polygon.isEmpty() )
1691  {
1692  mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
1693  }
1694  else
1695  {
1696  QPolygonF poly = path.toFillPolygon();
1697  mFill->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
1698  }
1699 
1700 
1701 }
1702 
1703 
1705 
1706 
1708 {
1709  mPath = path;
1710  mSize = size;
1711  mAngle = angle;
1712  mOffset = QPointF( 0, 0 );
1714  mStrokeWidth = 0.2;
1715  mStrokeWidthUnit = QgsUnitTypes::RenderMillimeters;
1716  mColor = QColor( 35, 35, 35 );
1717  mStrokeColor = QColor( 35, 35, 35 );
1718  updateDefaultAspectRatio();
1719 }
1720 
1721 
1723 {
1724  QString name;
1725  double size = DEFAULT_SVGMARKER_SIZE;
1726  double angle = DEFAULT_SVGMARKER_ANGLE;
1728 
1729  if ( props.contains( QStringLiteral( "name" ) ) )
1730  name = props[QStringLiteral( "name" )];
1731  if ( props.contains( QStringLiteral( "size" ) ) )
1732  size = props[QStringLiteral( "size" )].toDouble();
1733  if ( props.contains( QStringLiteral( "angle" ) ) )
1734  angle = props[QStringLiteral( "angle" )].toDouble();
1735  if ( props.contains( QStringLiteral( "scale_method" ) ) )
1736  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )] );
1737 
1738  QgsSvgMarkerSymbolLayer *m = new QgsSvgMarkerSymbolLayer( name, size, angle, scaleMethod );
1739 
1740  //we only check the svg default parameters if necessary, since it could be expensive
1741  if ( !props.contains( QStringLiteral( "fill" ) ) && !props.contains( QStringLiteral( "color" ) ) && !props.contains( QStringLiteral( "outline" ) ) &&
1742  !props.contains( QStringLiteral( "outline_color" ) ) && !props.contains( QStringLiteral( "outline-width" ) ) && !props.contains( QStringLiteral( "outline_width" ) ) )
1743  {
1744  QColor fillColor, strokeColor;
1745  double fillOpacity = 1.0;
1746  double strokeOpacity = 1.0;
1747  double strokeWidth;
1748  bool hasFillParam = false, hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
1749  bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
1750  QgsApplication::svgCache()->containsParams( name, hasFillParam, hasDefaultFillColor, fillColor,
1751  hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
1752  hasStrokeParam, hasDefaultStrokeColor, strokeColor,
1753  hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
1754  hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
1755  if ( hasDefaultFillColor )
1756  {
1757  m->setFillColor( fillColor );
1758  }
1759  if ( hasDefaultFillOpacity )
1760  {
1761  QColor c = m->fillColor();
1762  c.setAlphaF( fillOpacity );
1763  m->setFillColor( c );
1764  }
1765  if ( hasDefaultStrokeColor )
1766  {
1767  m->setStrokeColor( strokeColor );
1768  }
1769  if ( hasDefaultStrokeWidth )
1770  {
1771  m->setStrokeWidth( strokeWidth );
1772  }
1773  if ( hasDefaultStrokeOpacity )
1774  {
1775  QColor c = m->strokeColor();
1776  c.setAlphaF( strokeOpacity );
1777  m->setStrokeColor( c );
1778  }
1779  }
1780 
1781  if ( props.contains( QStringLiteral( "size_unit" ) ) )
1782  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
1783  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1784  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
1785  if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
1786  m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
1787  if ( props.contains( QStringLiteral( "offset" ) ) )
1788  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
1789  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1790  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
1791  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1792  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
1793  if ( props.contains( QStringLiteral( "fill" ) ) )
1794  {
1795  //pre 2.5 projects used "fill"
1796  m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "fill" )] ) );
1797  }
1798  else if ( props.contains( QStringLiteral( "color" ) ) )
1799  {
1800  m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] ) );
1801  }
1802  if ( props.contains( QStringLiteral( "outline" ) ) )
1803  {
1804  //pre 2.5 projects used "outline"
1805  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline" )] ) );
1806  }
1807  else if ( props.contains( QStringLiteral( "outline_color" ) ) )
1808  {
1809  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )] ) );
1810  }
1811  else if ( props.contains( QStringLiteral( "line_color" ) ) )
1812  {
1813  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )] ) );
1814  }
1815 
1816  if ( props.contains( QStringLiteral( "outline-width" ) ) )
1817  {
1818  //pre 2.5 projects used "outline-width"
1819  m->setStrokeWidth( props[QStringLiteral( "outline-width" )].toDouble() );
1820  }
1821  else if ( props.contains( QStringLiteral( "outline_width" ) ) )
1822  {
1823  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
1824  }
1825  else if ( props.contains( QStringLiteral( "line_width" ) ) )
1826  {
1827  m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
1828  }
1829 
1830  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
1831  {
1832  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )] ) );
1833  }
1834  else if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
1835  {
1836  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )] ) );
1837  }
1838  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
1839  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )] ) );
1840 
1841  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1842  {
1843  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1844  }
1845  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1846  {
1847  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1848  }
1849 
1850  m->restoreOldDataDefinedProperties( props );
1851 
1853 
1854  return m;
1855 }
1856 
1858 {
1859  QgsStringMap::iterator it = properties.find( QStringLiteral( "name" ) );
1860  if ( it != properties.end() )
1861  {
1862  if ( saving )
1863  it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value(), pathResolver );
1864  else
1865  it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value(), pathResolver );
1866  }
1867 }
1868 
1869 void QgsSvgMarkerSymbolLayer::setPath( const QString &path )
1870 {
1871  mPath = path;
1872  QColor defaultFillColor, defaultStrokeColor;
1873  double strokeWidth, fillOpacity, strokeOpacity;
1874  bool hasFillParam = false, hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
1875  bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
1876  QgsApplication::svgCache()->containsParams( path, hasFillParam, hasDefaultFillColor, defaultFillColor,
1877  hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
1878  hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
1879  hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
1880  hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
1881 
1882  double newFillOpacity = hasFillOpacityParam ? fillColor().alphaF() : 1.0;
1883  double newStrokeOpacity = hasStrokeOpacityParam ? strokeColor().alphaF() : 1.0;
1884 
1885  if ( hasDefaultFillColor )
1886  {
1887  defaultFillColor.setAlphaF( newFillOpacity );
1888  setFillColor( defaultFillColor );
1889  }
1890  if ( hasDefaultFillOpacity )
1891  {
1892  QColor c = fillColor();
1893  c.setAlphaF( fillOpacity );
1894  setFillColor( c );
1895  }
1896  if ( hasDefaultStrokeColor )
1897  {
1898  defaultStrokeColor.setAlphaF( newStrokeOpacity );
1899  setStrokeColor( defaultStrokeColor );
1900  }
1901  if ( hasDefaultStrokeWidth )
1902  {
1903  setStrokeWidth( strokeWidth );
1904  }
1905  if ( hasDefaultStrokeOpacity )
1906  {
1907  QColor c = strokeColor();
1908  c.setAlphaF( strokeOpacity );
1909  setStrokeColor( c );
1910  }
1911 
1912  updateDefaultAspectRatio();
1913 }
1914 
1916 {
1917  if ( mDefaultAspectRatio == 0.0 )
1918  {
1919  //size
1920  double size = mSize;
1921  //assume 88 dpi as standard value
1922  double widthScaleFactor = 3.465;
1923  QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( mPath, size, mColor, mStrokeColor, mStrokeWidth, widthScaleFactor );
1924  // set default aspect ratio
1925  mDefaultAspectRatio = svgViewbox.isValid() ? svgViewbox.height() / svgViewbox.width() : 0.0;
1926  }
1927  return mDefaultAspectRatio;
1928 }
1929 
1931 {
1932  bool aPreservedAspectRatio = preservedAspectRatio();
1933  if ( aPreservedAspectRatio && !par )
1934  {
1935  mFixedAspectRatio = mDefaultAspectRatio;
1936  }
1937  else if ( !aPreservedAspectRatio && par )
1938  {
1939  mFixedAspectRatio = 0.0;
1940  }
1941  return preservedAspectRatio();
1942 }
1943 
1944 
1946 {
1947  return QStringLiteral( "SvgMarker" );
1948 }
1949 
1951 {
1952  QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
1953  Q_UNUSED( context );
1954 }
1955 
1957 {
1958  Q_UNUSED( context );
1959 }
1960 
1962 {
1963  QPainter *p = context.renderContext().painter();
1964  if ( !p )
1965  return;
1966 
1967  bool hasDataDefinedSize = false;
1968  double scaledSize = calculateSize( context, hasDataDefinedSize );
1969  double size = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
1970 
1971  //don't render symbols with size below one or above 10,000 pixels
1972  if ( static_cast< int >( size ) < 1 || 10000.0 < size )
1973  {
1974  return;
1975  }
1976 
1977  p->save();
1978 
1979  bool hasDataDefinedAspectRatio = false;
1980  double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
1981 
1982  QPointF outputOffset;
1983  double angle = 0.0;
1984  calculateOffsetAndRotation( context, scaledSize, outputOffset, angle );
1985 
1986  p->translate( point + outputOffset );
1987 
1988  bool rotated = !qgsDoubleNear( angle, 0 );
1989  if ( rotated )
1990  p->rotate( angle );
1991 
1992  QString path = mPath;
1994  {
1995  context.setOriginalValueVariable( mPath );
1997  context.renderContext().pathResolver() );
1998  }
1999 
2000  double strokeWidth = mStrokeWidth;
2002  {
2003  context.setOriginalValueVariable( mStrokeWidth );
2005  }
2006  strokeWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
2007 
2008  QColor fillColor = mColor;
2010  {
2013  }
2014 
2015  QColor strokeColor = mStrokeColor;
2017  {
2020  }
2021 
2022  bool fitsInCache = true;
2023  bool usePict = true;
2024  double hwRatio = 1.0;
2025  if ( !context.renderContext().forceVectorOutput() && !rotated )
2026  {
2027  QImage img = QgsApplication::svgCache()->svgAsImage( path, size, fillColor, strokeColor, strokeWidth,
2028  context.renderContext().scaleFactor(), fitsInCache, aspectRatio );
2029  if ( fitsInCache && img.width() > 1 )
2030  {
2031  usePict = false;
2032  //consider transparency
2033  if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2034  {
2035  QImage transparentImage = img.copy();
2036  QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2037  p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
2038  hwRatio = static_cast< double >( transparentImage.height() ) / static_cast< double >( transparentImage.width() );
2039  }
2040  else
2041  {
2042  p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2043  hwRatio = static_cast< double >( img.height() ) / static_cast< double >( img.width() );
2044  }
2045  }
2046  }
2047 
2048  if ( usePict || !fitsInCache )
2049  {
2050  p->setOpacity( context.opacity() );
2051  QPicture pct = QgsApplication::svgCache()->svgAsPicture( path, size, fillColor, strokeColor, strokeWidth,
2052  context.renderContext().scaleFactor(), context.renderContext().forceVectorOutput(), aspectRatio );
2053  if ( pct.width() > 1 )
2054  {
2055  p->save();
2056  _fixQPictureDPI( p );
2057  p->drawPicture( 0, 0, pct );
2058  p->restore();
2059  hwRatio = static_cast< double >( pct.height() ) / static_cast< double >( pct.width() );
2060  }
2061  }
2062 
2063  if ( context.selected() )
2064  {
2065  QPen pen( context.renderContext().selectionColor() );
2066  double penWidth = context.renderContext().convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters );
2067  if ( penWidth > size / 20 )
2068  {
2069  // keep the pen width from covering symbol
2070  penWidth = size / 20;
2071  }
2072  double penOffset = penWidth / 2;
2073  pen.setWidth( penWidth );
2074  p->setPen( pen );
2075  p->setBrush( Qt::NoBrush );
2076  double wSize = size + penOffset;
2077  double hSize = size * hwRatio + penOffset;
2078  p->drawRect( QRectF( -wSize / 2.0, -hSize / 2.0, wSize, hSize ) );
2079  }
2080 
2081  p->restore();
2082 
2084  {
2085  // workaround issue with nested QPictures forgetting antialiasing flag - see https://issues.qgis.org/issues/14960
2086  p->setRenderHint( QPainter::Antialiasing );
2087  }
2088 
2089 }
2090 
2091 double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2092 {
2093  double scaledSize = mSize;
2095 
2096  bool ok = true;
2097  if ( hasDataDefinedSize )
2098  {
2099  context.setOriginalValueVariable( mSize );
2101  }
2102  else
2103  {
2105  if ( hasDataDefinedSize )
2106  {
2107  context.setOriginalValueVariable( mSize );
2109  }
2110  }
2111 
2112  if ( hasDataDefinedSize && ok )
2113  {
2114  switch ( mScaleMethod )
2115  {
2116  case QgsSymbol::ScaleArea:
2117  scaledSize = std::sqrt( scaledSize );
2118  break;
2120  break;
2121  }
2122  }
2123 
2124  return scaledSize;
2125 }
2126 
2127 double QgsSvgMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2128 {
2130  if ( !hasDataDefinedAspectRatio )
2131  return mFixedAspectRatio;
2132 
2133  if ( !mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyHeight ) && mFixedAspectRatio <= 0.0 )
2134  return 0.0;
2135 
2136  double scaledAspectRatio = mDefaultAspectRatio;
2137  if ( mFixedAspectRatio > 0.0 )
2138  scaledAspectRatio = mFixedAspectRatio;
2139 
2140  double defaultHeight = mSize * scaledAspectRatio;
2141  scaledAspectRatio = defaultHeight / scaledSize;
2142 
2143  bool ok = true;
2144  double scaledHeight = scaledSize * scaledAspectRatio;
2146  {
2147  context.setOriginalValueVariable( defaultHeight );
2148  scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyHeight, context.renderContext().expressionContext(), defaultHeight, &ok );
2149  }
2150 
2151  if ( hasDataDefinedAspectRatio && ok )
2152  {
2153  switch ( mScaleMethod )
2154  {
2155  case QgsSymbol::ScaleArea:
2156  scaledHeight = sqrt( scaledHeight );
2157  break;
2159  break;
2160  }
2161  }
2162 
2163  scaledAspectRatio = scaledHeight / scaledSize;
2164 
2165  return scaledAspectRatio;
2166 }
2167 
2168 void QgsSvgMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledSize, QPointF &offset, double &angle ) const
2169 {
2170  //offset
2171  double offsetX = 0;
2172  double offsetY = 0;
2173  markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
2174  offset = QPointF( offsetX, offsetY );
2175 
2176  angle = mAngle + mLineAngle;
2178  {
2179  context.setOriginalValueVariable( mAngle );
2181  }
2182 
2184  if ( hasDataDefinedRotation )
2185  {
2186  // For non-point markers, "dataDefinedRotation" means following the
2187  // shape (shape-data defined). For them, "field-data defined" does
2188  // not work at all. TODO: if "field-data defined" ever gets implemented
2189  // we'll need a way to distinguish here between the two, possibly
2190  // using another flag in renderHints()
2191  const QgsFeature *f = context.feature();
2192  if ( f )
2193  {
2194  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
2195  {
2196  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2197  angle += m2p.mapRotation();
2198  }
2199  }
2200  }
2201 
2202  if ( angle )
2203  offset = _rotatedOffset( offset, angle );
2204 }
2205 
2206 
2208 {
2209  QgsStringMap map;
2210  map[QStringLiteral( "name" )] = mPath;
2211  map[QStringLiteral( "size" )] = QString::number( mSize );
2212  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2213  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2214  map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2215  map[QStringLiteral( "angle" )] = QString::number( mAngle );
2216  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2217  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2218  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2219  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2220  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
2221  map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
2222  map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
2223  map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2224  map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2225  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
2226  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
2227  return map;
2228 }
2229 
2231 {
2233  m->setFixedAspectRatio( mFixedAspectRatio );
2234  m->setColor( mColor );
2235  m->setStrokeColor( mStrokeColor );
2236  m->setStrokeWidth( mStrokeWidth );
2237  m->setStrokeWidthUnit( mStrokeWidthUnit );
2238  m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
2239  m->setOffset( mOffset );
2240  m->setOffsetUnit( mOffsetUnit );
2242  m->setSizeUnit( mSizeUnit );
2247  copyPaintEffect( m );
2248  return m;
2249 }
2250 
2252 {
2254  mStrokeWidthUnit = unit;
2255 }
2256 
2258 {
2260  if ( unit != mStrokeWidthUnit )
2261  {
2263  }
2264  return unit;
2265 }
2266 
2268 {
2270  mStrokeWidthMapUnitScale = scale;
2271 }
2272 
2274 {
2275  if ( QgsMarkerSymbolLayer::mapUnitScale() == mStrokeWidthMapUnitScale )
2276  {
2277  return mStrokeWidthMapUnitScale;
2278  }
2279  return QgsMapUnitScale();
2280 }
2281 
2282 void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
2283 {
2284  // <Graphic>
2285  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2286  element.appendChild( graphicElem );
2287 
2288  // encode a parametric SVG reference
2290  double strokeWidth = QgsSymbolLayerUtils::rescaleUom( mStrokeWidth, mStrokeWidthUnit, props );
2291  QgsSymbolLayerUtils::parametricSvgToSld( doc, graphicElem, mPath, mColor, size, mStrokeColor, strokeWidth );
2292 
2293  // <Rotation>
2294  QString angleFunc;
2295  bool ok;
2296  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2297  if ( !ok )
2298  {
2299  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mAngle );
2300  }
2301  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2302  {
2303  angleFunc = QString::number( angle + mAngle );
2304  }
2305 
2306  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2307 
2308  // <Displacement>
2309  QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2310  QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
2311 }
2312 
2314 {
2315  QgsDebugMsg( QStringLiteral( "Entered." ) );
2316 
2317  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
2318  if ( graphicElem.isNull() )
2319  return nullptr;
2320 
2321  QString path, mimeType;
2322  QColor fillColor;
2323  double size;
2324 
2325  if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
2326  return nullptr;
2327 
2328  QString uom = element.attribute( QStringLiteral( "uom" ) );
2329  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
2330 
2331  if ( mimeType != QLatin1String( "image/svg+xml" ) )
2332  return nullptr;
2333 
2334  double angle = 0.0;
2335  QString angleFunc;
2336  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2337  {
2338  bool ok;
2339  double d = angleFunc.toDouble( &ok );
2340  if ( ok )
2341  angle = d;
2342  }
2343 
2344  QPointF offset;
2345  QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, offset );
2346 
2347  QgsSvgMarkerSymbolLayer *m = new QgsSvgMarkerSymbolLayer( path, size );
2348  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
2349  m->setFillColor( fillColor );
2350  //m->setStrokeColor( strokeColor );
2351  //m->setStrokeWidth( strokeWidth );
2352  m->setAngle( angle );
2353  m->setOffset( offset );
2354  return m;
2355 }
2356 
2357 bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
2358 {
2359  //size
2360  double size = mSize;
2361 
2362  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
2363 
2364  bool ok = true;
2365  if ( hasDataDefinedSize )
2366  {
2367  context.setOriginalValueVariable( mSize );
2369  }
2370 
2371  if ( hasDataDefinedSize && ok )
2372  {
2373  switch ( mScaleMethod )
2374  {
2375  case QgsSymbol::ScaleArea:
2376  size = std::sqrt( size );
2377  break;
2379  break;
2380  }
2381  }
2382 
2384  {
2385  size *= mmMapUnitScaleFactor;
2386  }
2387 
2388  //offset, angle
2389  QPointF offset = mOffset;
2390 
2392  {
2394  QString offsetString = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext(), QString(), &ok );
2395  if ( ok )
2396  offset = QgsSymbolLayerUtils::decodePoint( offsetString );
2397  }
2398  double offsetX = offset.x();
2399  double offsetY = offset.y();
2400 
2401  QPointF outputOffset( offsetX, offsetY );
2402 
2403  double angle = mAngle + mLineAngle;
2405  {
2406  context.setOriginalValueVariable( mAngle );
2408  }
2409 
2410  if ( angle )
2411  outputOffset = _rotatedOffset( outputOffset, angle );
2412 
2413  outputOffset *= e.mapUnitScaleFactor( e.symbologyScale(), mOffsetUnit, e.mapUnits(), context.renderContext().mapToPixel().mapUnitsPerPixel() );
2414 
2415  QString path = mPath;
2417  {
2418  context.setOriginalValueVariable( mPath );
2420  context.renderContext().pathResolver() );
2421  }
2422 
2423  double strokeWidth = mStrokeWidth;
2425  {
2426  context.setOriginalValueVariable( mStrokeWidth );
2428  }
2429  strokeWidth *= e.mapUnitScaleFactor( e.symbologyScale(), mStrokeWidthUnit, e.mapUnits(), context.renderContext().mapToPixel().mapUnitsPerPixel() );
2430 
2431  QColor fillColor = mColor;
2433  {
2436  }
2437 
2438  QColor strokeColor = mStrokeColor;
2440  {
2443  }
2444 
2445  const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, size, fillColor, strokeColor, strokeWidth,
2446  context.renderContext().scaleFactor(), mFixedAspectRatio );
2447 
2448  QSvgRenderer r( svgContent );
2449  if ( !r.isValid() )
2450  return false;
2451 
2452  QgsDxfPaintDevice pd( &e );
2453  pd.setDrawingSize( QSizeF( r.defaultSize() ) );
2454 
2455  QSizeF outSize( r.defaultSize() );
2456  outSize.scale( size, size, Qt::KeepAspectRatio );
2457 
2458  QPainter p;
2459  p.begin( &pd );
2460  if ( !qgsDoubleNear( angle, 0.0 ) )
2461  {
2462  p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
2463  p.rotate( angle );
2464  p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
2465  }
2466  pd.setShift( shift + QPointF( outputOffset.x(), -outputOffset.y() ) );
2467  pd.setOutputSize( QRectF( -outSize.width() / 2.0, -outSize.height() / 2.0, outSize.width(), outSize.height() ) );
2468  pd.setLayer( layerName );
2469  r.render( &p );
2470  p.end();
2471  return true;
2472 }
2473 
2475 {
2476  bool hasDataDefinedSize = false;
2477  double scaledSize = calculateSize( context, hasDataDefinedSize );
2478  scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
2479 
2480  //don't render symbols with size below one or above 10,000 pixels
2481  if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
2482  {
2483  return QRectF();
2484  }
2485 
2486  QPointF outputOffset;
2487  double angle = 0.0;
2488  calculateOffsetAndRotation( context, scaledSize, outputOffset, angle );
2489 
2490  QString path = mPath;
2492  {
2493  context.setOriginalValueVariable( mPath );
2495  context.renderContext().pathResolver() );
2496  }
2497 
2498  double strokeWidth = mStrokeWidth;
2500  {
2501  context.setOriginalValueVariable( mStrokeWidth );
2503  }
2504  strokeWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
2505 
2506  //need to get colors to take advantage of cached SVGs
2507  QColor fillColor = mColor;
2509  {
2512  }
2513 
2514  QColor strokeColor = mStrokeColor;
2516  {
2519  }
2520 
2521  QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledSize, fillColor, strokeColor, strokeWidth,
2522  context.renderContext().scaleFactor(), mFixedAspectRatio );
2523 
2524  double scaledHeight = svgViewbox.isValid() ? scaledSize * svgViewbox.height() / svgViewbox.width() : scaledSize;
2525 
2526  QMatrix transform;
2527 
2528  // move to the desired position
2529  transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
2530 
2531  if ( !qgsDoubleNear( angle, 0.0 ) )
2532  transform.rotate( angle );
2533 
2534  //antialiasing
2535  strokeWidth += 1.0 / 2.0;
2536 
2537  QRectF symbolBounds = transform.mapRect( QRectF( -scaledSize / 2.0,
2538  -scaledHeight / 2.0,
2539  scaledSize,
2540  scaledHeight ) );
2541 
2542  //extend bounds by pen width / 2.0
2543  symbolBounds.adjust( -strokeWidth / 2.0, -strokeWidth / 2.0,
2544  strokeWidth / 2.0, strokeWidth / 2.0 );
2545 
2546  return symbolBounds;
2547 
2548 }
2549 
2551 
2553  : mPath( path )
2554 {
2555  mSize = size;
2556  mAngle = angle;
2557  mOffset = QPointF( 0, 0 );
2560 }
2561 
2562 
2564 {
2565  QString path;
2567  double angle = DEFAULT_RASTERMARKER_ANGLE;
2569 
2570  if ( props.contains( QStringLiteral( "imageFile" ) ) )
2571  path = props[QStringLiteral( "imageFile" )];
2572  if ( props.contains( QStringLiteral( "size" ) ) )
2573  size = props[QStringLiteral( "size" )].toDouble();
2574  if ( props.contains( QStringLiteral( "angle" ) ) )
2575  angle = props[QStringLiteral( "angle" )].toDouble();
2576  if ( props.contains( QStringLiteral( "scale_method" ) ) )
2577  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )] );
2578 
2579  QgsRasterMarkerSymbolLayer *m = new QgsRasterMarkerSymbolLayer( path, size, angle, scaleMethod );
2580 
2581  if ( props.contains( QStringLiteral( "alpha" ) ) )
2582  {
2583  m->setOpacity( props[QStringLiteral( "alpha" )].toDouble() );
2584  }
2585 
2586  if ( props.contains( QStringLiteral( "size_unit" ) ) )
2587  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
2588  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
2589  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
2590  if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
2591  m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
2592 
2593  if ( props.contains( QStringLiteral( "offset" ) ) )
2594  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
2595  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
2596  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
2597  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2598  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
2599 
2600  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2601  {
2602  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2603  }
2604  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2605  {
2606  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2607  }
2608 
2609  m->restoreOldDataDefinedProperties( props );
2611 
2612  return m;
2613 }
2614 
2616 {
2617  QgsStringMap::iterator it = properties.find( QStringLiteral( "name" ) );
2618  if ( it != properties.end() )
2619  {
2620  if ( saving )
2621  it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value(), pathResolver );
2622  else
2623  it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value(), pathResolver );
2624  }
2625 }
2626 
2627 void QgsRasterMarkerSymbolLayer::setPath( const QString &path )
2628 {
2629  mPath = path;
2631 }
2632 
2634 {
2635  bool aPreservedAspectRatio = preservedAspectRatio();
2636  if ( aPreservedAspectRatio && !par )
2637  {
2639  }
2640  else if ( !aPreservedAspectRatio && par )
2641  {
2642  mFixedAspectRatio = 0.0;
2643  }
2644  return preservedAspectRatio();
2645 }
2646 
2648 {
2649  if ( mDefaultAspectRatio == 0.0 )
2650  {
2652  mDefaultAspectRatio = ( !size.isNull() && size.isValid() && size.width() > 0 ) ? static_cast< double >( size.height() ) / static_cast< double >( size.width() ) : 0.0;
2653  }
2654  return mDefaultAspectRatio;
2655 }
2656 
2658 {
2659  return QStringLiteral( "RasterMarker" );
2660 }
2661 
2663 {
2664  QPainter *p = context.renderContext().painter();
2665  if ( !p )
2666  return;
2667 
2668  bool hasDataDefinedSize = false;
2669  double scaledSize = calculateSize( context, hasDataDefinedSize );
2670  double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
2671  bool hasDataDefinedAspectRatio = false;
2672  double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
2673  double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
2674 
2675  //don't render symbols with size below one or above 10,000 pixels
2676  if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2677  {
2678  return;
2679  }
2680 
2681  QString path = mPath;
2683  {
2684  context.setOriginalValueVariable( mPath );
2686  if ( preservedAspectRatio() && path != mPath )
2687  {
2688  QSize size = QgsApplication::imageCache()->originalSize( path );
2689  if ( !size.isNull() && size.isValid() && size.width() > 0 )
2690  {
2691  height = width * ( static_cast< double >( size.height() ) / static_cast< double >( size.width() ) );
2692  }
2693  }
2694  }
2695 
2696  if ( path.isEmpty() )
2697  return;
2698 
2699  p->save();
2700 
2701  QPointF outputOffset;
2702  double angle = 0.0;
2703  calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
2704 
2705  p->translate( point + outputOffset );
2706 
2707  bool rotated = !qgsDoubleNear( angle, 0 );
2708  if ( rotated )
2709  p->rotate( angle );
2710 
2711  double opacity = mOpacity;
2713  {
2716  }
2717  opacity *= context.opacity();
2718 
2719  bool cached;
2720  QImage img = QgsApplication::imageCache()->pathAsImage( path, QSize( width, preservedAspectRatio() ? 0 : width * aspectRatio ), preservedAspectRatio(), opacity, cached );
2721  if ( !img.isNull() )
2722  {
2723  if ( context.selected() )
2725 
2726  p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2727  }
2728 
2729  p->restore();
2730 }
2731 
2732 double QgsRasterMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2733 {
2734  double scaledSize = mSize;
2736 
2737  bool ok = true;
2738  if ( hasDataDefinedSize )
2739  {
2740  context.setOriginalValueVariable( mSize );
2742  }
2743  else
2744  {
2746  if ( hasDataDefinedSize )
2747  {
2748  context.setOriginalValueVariable( mSize );
2750  }
2751  }
2752 
2753  if ( hasDataDefinedSize && ok )
2754  {
2755  switch ( mScaleMethod )
2756  {
2757  case QgsSymbol::ScaleArea:
2758  scaledSize = std::sqrt( scaledSize );
2759  break;
2761  break;
2762  }
2763  }
2764 
2765  return scaledSize;
2766 }
2767 
2768 double QgsRasterMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2769 {
2771  if ( !hasDataDefinedAspectRatio )
2772  return mFixedAspectRatio;
2773 
2775  return 0.0;
2776 
2777  double scaledAspectRatio = mDefaultAspectRatio;
2778  if ( mFixedAspectRatio > 0.0 )
2779  scaledAspectRatio = mFixedAspectRatio;
2780 
2781  double defaultHeight = mSize * scaledAspectRatio;
2782  scaledAspectRatio = defaultHeight / scaledSize;
2783 
2784  bool ok = true;
2785  double scaledHeight = scaledSize * scaledAspectRatio;
2787  {
2788  context.setOriginalValueVariable( defaultHeight );
2789  scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyHeight, context.renderContext().expressionContext(), defaultHeight, &ok );
2790  }
2791 
2792  if ( hasDataDefinedAspectRatio && ok )
2793  {
2794  switch ( mScaleMethod )
2795  {
2796  case QgsSymbol::ScaleArea:
2797  scaledHeight = sqrt( scaledHeight );
2798  break;
2800  break;
2801  }
2802  }
2803 
2804  scaledAspectRatio = scaledHeight / scaledSize;
2805 
2806  return scaledAspectRatio;
2807 }
2808 
2809 void QgsRasterMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
2810 {
2811  //offset
2812  double offsetX = 0;
2813  double offsetY = 0;
2814  markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
2815  offset = QPointF( offsetX, offsetY );
2816 
2817  angle = mAngle + mLineAngle;
2819  {
2820  context.setOriginalValueVariable( mAngle );
2822  }
2823 
2825  if ( hasDataDefinedRotation )
2826  {
2827  const QgsFeature *f = context.feature();
2828  if ( f )
2829  {
2830  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
2831  {
2832  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2833  angle += m2p.mapRotation();
2834  }
2835  }
2836  }
2837 
2838  if ( angle )
2839  offset = _rotatedOffset( offset, angle );
2840 }
2841 
2842 
2844 {
2845  QgsStringMap map;
2846  map[QStringLiteral( "imageFile" )] = mPath;
2847  map[QStringLiteral( "size" )] = QString::number( mSize );
2848  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2849  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2850  map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2851  map[QStringLiteral( "angle" )] = QString::number( mAngle );
2852  map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
2853  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2854  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2855  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2856  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2857  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
2858  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
2859  return map;
2860 }
2861 
2863 {
2866  m->setOpacity( mOpacity );
2867  m->setOffset( mOffset );
2868  m->setOffsetUnit( mOffsetUnit );
2870  m->setSizeUnit( mSizeUnit );
2875  copyPaintEffect( m );
2876  return m;
2877 }
2878 
2880 {
2882 }
2883 
2885 {
2887 }
2888 
2890 {
2891  bool hasDataDefinedSize = false;
2892  double scaledSize = calculateSize( context, hasDataDefinedSize );
2893  double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
2894  bool hasDataDefinedAspectRatio = false;
2895  double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
2896  double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
2897 
2898  //don't render symbols with size below one or above 10,000 pixels
2899  if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
2900  {
2901  return QRectF();
2902  }
2903 
2904  QPointF outputOffset;
2905  double angle = 0.0;
2906  calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
2907 
2908  QMatrix transform;
2909 
2910  // move to the desired position
2911  transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
2912 
2913  if ( !qgsDoubleNear( angle, 0.0 ) )
2914  transform.rotate( angle );
2915 
2916  QRectF symbolBounds = transform.mapRect( QRectF( -width / 2.0,
2917  -height / 2.0,
2918  width,
2919  height ) );
2920 
2921  return symbolBounds;
2922 }
2923 
2925 
2926 QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QChar chr, double pointSize, const QColor &color, double angle )
2927 {
2928  mFontFamily = fontFamily;
2929  mChr = chr;
2930  mColor = color;
2931  mAngle = angle;
2932  mSize = pointSize;
2933  mOrigSize = pointSize;
2935  mOffset = QPointF( 0, 0 );
2937  mStrokeColor = DEFAULT_FONTMARKER_BORDERCOLOR;
2938  mStrokeWidth = 0.0;
2939  mStrokeWidthUnit = QgsUnitTypes::RenderMillimeters;
2940  mPenJoinStyle = DEFAULT_FONTMARKER_JOINSTYLE;
2941 }
2942 
2944 {
2945  delete mFontMetrics;
2946 }
2947 
2949 {
2950  QString fontFamily = DEFAULT_FONTMARKER_FONT;
2951  QChar chr = DEFAULT_FONTMARKER_CHR;
2952  double pointSize = DEFAULT_FONTMARKER_SIZE;
2954  double angle = DEFAULT_FONTMARKER_ANGLE;
2955 
2956  if ( props.contains( QStringLiteral( "font" ) ) )
2957  fontFamily = props[QStringLiteral( "font" )];
2958  if ( props.contains( QStringLiteral( "chr" ) ) && props[QStringLiteral( "chr" )].length() > 0 )
2959  chr = props[QStringLiteral( "chr" )].at( 0 );
2960  if ( props.contains( QStringLiteral( "size" ) ) )
2961  pointSize = props[QStringLiteral( "size" )].toDouble();
2962  if ( props.contains( QStringLiteral( "color" ) ) )
2963  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )] );
2964  if ( props.contains( QStringLiteral( "angle" ) ) )
2965  angle = props[QStringLiteral( "angle" )].toDouble();
2966 
2967  QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( fontFamily, chr, pointSize, color, angle );
2968 
2969  if ( props.contains( QStringLiteral( "outline_color" ) ) )
2970  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )] ) );
2971  if ( props.contains( QStringLiteral( "outline_width" ) ) )
2972  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
2973  if ( props.contains( QStringLiteral( "offset" ) ) )
2974  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )] ) );
2975  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
2976  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )] ) );
2977  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2978  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )] ) );
2979  if ( props.contains( QStringLiteral( "size_unit" ) ) )
2980  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )] ) );
2981  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
2982  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )] ) );
2983  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
2984  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )] ) );
2985  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
2986  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )] ) );
2987  if ( props.contains( QStringLiteral( "joinstyle" ) ) )
2988  m->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )] ) );
2989  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2990  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2991  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2992  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2993 
2994  m->restoreOldDataDefinedProperties( props );
2995 
2996  return m;
2997 }
2998 
3000 {
3001  return QStringLiteral( "FontMarker" );
3002 }
3003 
3005 {
3006  QColor brushColor = mColor;
3007  QColor penColor = mStrokeColor;
3008 
3009  brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
3010  penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
3011 
3012  mBrush = QBrush( brushColor );
3013  mPen = QPen( penColor );
3014  mPen.setJoinStyle( mPenJoinStyle );
3015  mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
3016 
3017  mFont = QFont( mFontFamily );
3018  mFont.setPixelSize( context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale ) );
3019  delete mFontMetrics;
3020  mFontMetrics = new QFontMetrics( mFont );
3021  mChrWidth = mFontMetrics->width( mChr );
3022  mChrOffset = QPointF( mChrWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3023  mOrigSize = mSize; // save in case the size would be data defined
3024 }
3025 
3027 {
3028  Q_UNUSED( context );
3029 }
3030 
3031 QString QgsFontMarkerSymbolLayer::characterToRender( QgsSymbolRenderContext &context, QPointF &charOffset, double &charWidth )
3032 {
3033  charOffset = mChrOffset;
3034  QString charToRender = mChr;
3036  {
3037  context.setOriginalValueVariable( mChr );
3039  if ( charToRender != mChr )
3040  {
3041  charWidth = mFontMetrics->width( charToRender );
3042  charOffset = QPointF( charWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3043  }
3044  }
3045  return charToRender;
3046 }
3047 
3048 void QgsFontMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
3049  double scaledSize,
3050  bool &hasDataDefinedRotation,
3051  QPointF &offset,
3052  double &angle ) const
3053 {
3054  //offset
3055  double offsetX = 0;
3056  double offsetY = 0;
3057  markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
3058  offset = QPointF( offsetX, offsetY );
3059 
3060  //angle
3061  bool ok = true;
3062  angle = mAngle + mLineAngle;
3063  bool usingDataDefinedRotation = false;
3065  {
3066  context.setOriginalValueVariable( angle );
3068  usingDataDefinedRotation = ok;
3069  }
3070 
3071  hasDataDefinedRotation = context.renderHints() & QgsSymbol::DynamicRotation || usingDataDefinedRotation;
3072  if ( hasDataDefinedRotation )
3073  {
3074  // For non-point markers, "dataDefinedRotation" means following the
3075  // shape (shape-data defined). For them, "field-data defined" does
3076  // not work at all. TODO: if "field-data defined" ever gets implemented
3077  // we'll need a way to distinguish here between the two, possibly
3078  // using another flag in renderHints()
3079  const QgsFeature *f = context.feature();
3080  if ( f )
3081  {
3082  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
3083  {
3084  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3085  angle += m2p.mapRotation();
3086  }
3087  }
3088  }
3089 
3090  if ( angle )
3091  offset = _rotatedOffset( offset, angle );
3092 }
3093 
3094 double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context )
3095 {
3096  double scaledSize = mSize;
3097  bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
3098 
3099  bool ok = true;
3100  if ( hasDataDefinedSize )
3101  {
3102  context.setOriginalValueVariable( mSize );
3104  }
3105 
3106  if ( hasDataDefinedSize && ok )
3107  {
3108  switch ( mScaleMethod )
3109  {
3110  case QgsSymbol::ScaleArea:
3111  scaledSize = std::sqrt( scaledSize );
3112  break;
3114  break;
3115  }
3116  }
3117  return scaledSize;
3118 }
3119 
3121 {
3122  QPainter *p = context.renderContext().painter();
3123  if ( !p )
3124  return;
3125 
3126  QTransform transform;
3127 
3128  bool ok;
3129  QColor brushColor = mColor;
3131  {
3134  }
3135  brushColor = context.selected() ? context.renderContext().selectionColor() : brushColor;
3136  brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
3137  mBrush.setColor( brushColor );
3138 
3139  QColor penColor = mStrokeColor;
3141  {
3144  }
3145  penColor.setAlphaF( penColor.alphaF() * context.opacity() );
3146 
3147  double penWidth = context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3149  {
3150  context.setOriginalValueVariable( mStrokeWidth );
3151  double strokeWidth = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyStrokeWidth, context.renderContext().expressionContext(), mStrokeWidth, &ok );
3152  if ( ok )
3153  {
3154  penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3155  }
3156  }
3157 
3159  {
3162  if ( ok )
3163  {
3164  mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
3165  }
3166  }
3167 
3168  p->setBrush( mBrush );
3169  if ( !qgsDoubleNear( penWidth, 0.0 ) )
3170  {
3171  mPen.setColor( penColor );
3172  mPen.setWidthF( penWidth );
3173  p->setPen( mPen );
3174  }
3175  else
3176  {
3177  p->setPen( Qt::NoPen );
3178  }
3179  p->save();
3180 
3181  QPointF chrOffset = mChrOffset;
3182  double chrWidth;
3183  QString charToRender = characterToRender( context, chrOffset, chrWidth );
3184 
3185  double sizeToRender = calculateSize( context );
3186 
3187  bool hasDataDefinedRotation = false;
3188  QPointF offset;
3189  double angle = 0;
3190  calculateOffsetAndRotation( context, sizeToRender, hasDataDefinedRotation, offset, angle );
3191 
3192  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
3193 
3194  if ( !qgsDoubleNear( angle, 0.0 ) )
3195  transform.rotate( angle );
3196 
3197  if ( !qgsDoubleNear( sizeToRender, mOrigSize ) )
3198  {
3199  double s = sizeToRender / mOrigSize;
3200  transform.scale( s, s );
3201  }
3202 
3203  QPainterPath path;
3204  path.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3205  p->drawPath( transform.map( path ) );
3206  p->restore();
3207 }
3208 
3210 {
3211  QgsStringMap props;
3212  props[QStringLiteral( "font" )] = mFontFamily;
3213  props[QStringLiteral( "chr" )] = mChr;
3214  props[QStringLiteral( "size" )] = QString::number( mSize );
3215  props[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3216  props[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3217  props[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
3218  props[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
3219  props[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
3220  props[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
3221  props[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
3222  props[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
3223  props[QStringLiteral( "angle" )] = QString::number( mAngle );
3224  props[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3225  props[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3226  props[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3227  props[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3228  props[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3229  return props;
3230 }
3231 
3233 {
3234  QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( mFontFamily, mChr, mSize, mColor, mAngle );
3235  m->setStrokeColor( mStrokeColor );
3236  m->setStrokeWidth( mStrokeWidth );
3237  m->setStrokeWidthUnit( mStrokeWidthUnit );
3238  m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
3239  m->setPenJoinStyle( mPenJoinStyle );
3240  m->setOffset( mOffset );
3241  m->setOffsetUnit( mOffsetUnit );
3243  m->setSizeUnit( mSizeUnit );
3248  copyPaintEffect( m );
3249  return m;
3250 }
3251 
3252 void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
3253 {
3254  // <Graphic>
3255  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
3256  element.appendChild( graphicElem );
3257 
3258  QString fontPath = QStringLiteral( "ttf://%1" ).arg( mFontFamily );
3259  int markIndex = mChr.unicode();
3261  QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, QStringLiteral( "ttf" ), &markIndex, mColor, size );
3262 
3263  // <Rotation>
3264  QString angleFunc;
3265  bool ok;
3266  double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
3267  if ( !ok )
3268  {
3269  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ) ).arg( mAngle );
3270  }
3271  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
3272  {
3273  angleFunc = QString::number( angle + mAngle );
3274  }
3275  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
3276 
3277  // <Displacement>
3278  QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
3279  QgsSymbolLayerUtils::createDisplacementElement( doc, graphicElem, offset );
3280 }
3281 
3283 {
3284  QPointF chrOffset = mChrOffset;
3285  double chrWidth = mChrWidth;
3286  //calculate width of rendered character
3287  ( void )characterToRender( context, chrOffset, chrWidth );
3288 
3289  if ( !mFontMetrics )
3290  mFontMetrics = new QFontMetrics( mFont );
3291 
3292  double scaledSize = calculateSize( context );
3293  if ( !qgsDoubleNear( scaledSize, mOrigSize ) )
3294  {
3295  chrWidth *= scaledSize / mOrigSize;
3296  }
3297 
3298  bool hasDataDefinedRotation = false;
3299  QPointF offset;
3300  double angle = 0;
3301  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
3302  scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3303 
3304  QMatrix transform;
3305 
3306  // move to the desired position
3307  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
3308 
3309  if ( !qgsDoubleNear( angle, 0.0 ) )
3310  transform.rotate( angle );
3311 
3312  QRectF symbolBounds = transform.mapRect( QRectF( -chrWidth / 2.0,
3313  -scaledSize / 2.0,
3314  chrWidth,
3315  scaledSize ) );
3316  return symbolBounds;
3317 }
3318 
3320 {
3321  QgsDebugMsg( QStringLiteral( "Entered." ) );
3322 
3323  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
3324  if ( graphicElem.isNull() )
3325  return nullptr;
3326 
3327  QString name, format;
3328  QColor color;
3329  double size;
3330  int chr;
3331 
3332  if ( !QgsSymbolLayerUtils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
3333  return nullptr;
3334 
3335  if ( !name.startsWith( QLatin1String( "ttf://" ) ) || format != QLatin1String( "ttf" ) )
3336  return nullptr;
3337 
3338  QString fontFamily = name.mid( 6 );
3339 
3340  double angle = 0.0;
3341  QString angleFunc;
3342  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
3343  {
3344  bool ok;
3345  double d = angleFunc.toDouble( &ok );
3346  if ( ok )
3347  angle = d;
3348  }
3349 
3350  QPointF offset;
3351  QgsSymbolLayerUtils::displacementFromSldElement( graphicElem, offset );
3352 
3353  QString uom = element.attribute( QStringLiteral( "uom" ) );
3354  offset.setX( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.x() ) );
3355  offset.setY( QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, offset.y() ) );
3356  size = QgsSymbolLayerUtils::sizeInPixelsFromSldUom( uom, size );
3357 
3358  QgsMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( fontFamily, chr, size, color );
3359  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
3360  m->setAngle( angle );
3361  m->setOffset( offset );
3362  return m;
3363 }
3364 
bool prepareCache(QgsSymbolRenderContext &context)
Prepares cache image.
void setOffset(QPointF offset)
Sets the marker&#39;s offset, which is the horizontal and vertical displacement which the rendered marker...
static void wellKnownMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &name, const QColor &color, const QColor &strokeColor, Qt::PenStyle strokeStyle, double strokeWidth=-1, double size=-1)
QPicture svgAsPicture(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool forceVectorOutput=false, double fixedAspectRatio=0)
Gets SVG as QPicture&.
#define DEFAULT_SIMPLEMARKER_SIZE
QgsMapUnitScale mapUnitScale() const override
static QgsSvgCache * svgCache()
Returns the application&#39;s SVG cache, used for caching SVG images and handling parameter replacement w...
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
bool writeDxf(QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift=QPointF(0.0, 0.0)) const override
write as DXF
QColor valueAsColor(int key, const QgsExpressionContext &context, const QColor &defaultColor=QColor(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a color...
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
QString ogrFeatureStyle(double mmScaleFactor, double mapUnitScaleFactor) const override
void setOffsetUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol&#39;s offset.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsSimpleMarkerSymbolLayer from an SLD XML element.
const QgsPathResolver & pathResolver() const
Returns the path resolver for conversion between relative and absolute paths during rendering operati...
static QgsSymbol::ScaleMethod decodeScaleMethod(const QString &str)
double symbologyScale() const
Returns the reference scale for output.
Definition: qgsdxfexport.h:150
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
Sets the stroke width map unit scale.
#define DEFAULT_RASTERMARKER_SIZE
void writeLine(const QgsPoint &pt1, const QgsPoint &pt2, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Write line (as a polyline)
void clipValueToMapUnitScale(double &value, const QgsMapUnitScale &scale, double pixelToMMFactor) const
Clips value to scale minimum/maximum.
double defaultAspectRatio() const
Returns the default marker aspect ratio between width and height, 0 if not yet calculated.
void setColor(const QColor &c) override
The fill color.
QColor mStrokeColor
Stroke color.
QgsFields fields() const
Fields of the layer.
Definition: qgssymbol.h:653
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
QgsRasterMarkerSymbolLayer(const QString &path=QString(), double size=DEFAULT_SVGMARKER_SIZE, double angle=DEFAULT_SVGMARKER_ANGLE, QgsSymbol::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructs raster marker symbol layer with picture from given absolute path to a raster image file...
Qt::PenStyle mStrokeStyle
Stroke style.
Calculate scale by the diameter.
Definition: qgssymbol.h:97
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:61
void setMapUnitScale(const QgsMapUnitScale &scale) override
A paint device for drawing into dxf files.
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double...
#define DEFAULT_SIMPLEMARKER_NAME
Use antialiasing while drawing.
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit u)
Sets the unit for the width of the marker&#39;s stroke.
void setMapUnitScale(const QgsMapUnitScale &scale) override
void setStrokeStyle(Qt::PenStyle strokeStyle)
Sets the marker&#39;s stroke style (e.g., solid, dashed, etc)
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
Simple marker symbol layer, consisting of a rendered shape with solid fill color and an stroke...
void writePolyline(const QgsPointSequence &line, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Draw dxf primitives (LWPOLYLINE)
Abstract base class for simple marker symbol layers.
QImage svgAsImage(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool &fitsInCache, double fixedAspectRatio=0)
Gets SVG as QImage.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsFontMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
Right facing arrow head (unfilled, lines only)
static QgsFillSymbol * createSimple(const QgsStringMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties. ...
Definition: qgssymbol.cpp:1148
void startRender(QgsSymbolRenderContext &context) override
static QgsImageCache * imageCache()
Returns the application&#39;s image cache, used for caching resampled versions of raster images...
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:265
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
double mLineAngle
Line rotation angle (see setLineAngle() for details)
void setDrawingSize(QSizeF size)
QgsUnitTypes::RenderUnit mSizeUnit
Marker size unit.
virtual QColor strokeColor() const
Gets stroke color.
QgsMapUnitScale mapUnitScale() const override
QgsMapUnitScale mStrokeWidthMapUnitScale
Stroke width map unit scale.
static bool displacementFromSldElement(QDomElement &element, QPointF &offset)
static QPointF decodePoint(const QString &string)
Decodes a QSizeF from a string.
#define DEG2RAD(x)
void setStrokeColor(const QColor &color) override
Sets the marker&#39;s stroke color.
void writeSldMarker(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
Writes the symbol layer definition as a SLD XML element.
static void externalMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &path, const QString &format, int *markIndex=nullptr, const QColor &color=QColor(), double size=-1)
void copyPaintEffect(QgsSymbolLayer *destLayer) const
Copies paint effect of this layer to another symbol layer.
void setStrokeColor(const QColor &c) override
Set stroke color.
void setStrokeWidth(double w)
Sets the width of the marker&#39;s stroke.
static const int MAXIMUM_CACHE_WIDTH
Maximum width/height of cache image.
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
#define DEFAULT_FONTMARKER_COLOR
static void createDisplacementElement(QDomDocument &doc, QDomElement &element, QPointF offset)
QBrush mSelBrush
QBrush to use as fill of selected symbols.
static QgsSimpleMarkerSymbolLayerBase::Shape decodeShape(const QString &name, bool *ok=nullptr)
Attempts to decode a string representation of a shape name to the corresponding shape.
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol&#39;s offset.
void restoreOldDataDefinedProperties(const QgsStringMap &stringMap)
Restores older data defined properties from string map.
void setStrokeWidth(double width)
Set&#39;s the marker&#39;s stroke width.
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
static double sizeInPixelsFromSldUom(const QString &uom, double size)
Returns the size scaled in pixels according to the uom attribute.
#define DEFAULT_FONTMARKER_BORDERCOLOR
double updateDefaultAspectRatio()
Calculates the default marker aspect ratio between width and height.
Flags flags() const
Returns combination of flags used for rendering.
double mFixedAspectRatio
The marker fixed aspect ratio.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
QgsUnitTypes::DistanceUnit mapUnits() const
Retrieve map units.
QPointF mOffset
Marker offset.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QColor strokeColor() const override
Returns the marker&#39;s stroke color.
void startRender(QgsSymbolRenderContext &context) override
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
static bool rotationFromSldElement(QDomElement &element, QString &rotationFunc)
QgsUnitTypes::RenderUnit mStrokeWidthUnit
Stroke width units.
#define DEFAULT_SIMPLEMARKER_JOINSTYLE
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:197
static QString encodeShape(QgsSimpleMarkerSymbolLayerBase::Shape shape)
Encodes a shape to its string representation.
QgsMapUnitScale mapUnitScale() const override
QMap< QString, QString > QgsStringMap
Definition: qgis.h:587
Rotation of symbol may be changed during rendering and symbol should not be cached.
Definition: qgssymbol.h:104
QgsFontMarkerSymbolLayer(const QString &fontFamily=DEFAULT_FONTMARKER_FONT, QChar chr=DEFAULT_FONTMARKER_CHR, double pointSize=DEFAULT_FONTMARKER_SIZE, const QColor &color=DEFAULT_FONTMARKER_COLOR, double angle=DEFAULT_FONTMARKER_ANGLE)
static QString encodePoint(QPointF point)
Encodes a QPointF to a string.
Name, eg shape name for simple markers.
static Qt::PenJoinStyle decodePenJoinStyle(const QString &str)
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
static QList< QgsSimpleMarkerSymbolLayerBase::Shape > availableShapes()
Returns a list of all available shape types.
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
void writePolygon(const QgsRingSequence &polygon, const QString &layer, const QString &hatchPattern, const QColor &color)
Draw dxf filled polygon (HATCH)
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
double mapRotation() const
Returns current map rotation in degrees.
#define DEFAULT_FONTMARKER_SIZE
QColor strokeColor() const override
Gets stroke color.
void setPenJoinStyle(Qt::PenJoinStyle style)
Sets the stroke join style.
double strokeWidth() const
Returns the width of the marker&#39;s stroke.
static QgsSymbolLayer * createFromSld(QDomElement &element)
void setPath(const QString &path)
Set the marker SVG path.
QPainterPath mPath
Painter path representing shape. If mPolygon is empty then the shape is stored in mPath...
static QString encodeColor(const QColor &color)
Diagonal half square (bottom left half)
virtual bool hasDataDefinedProperties() const
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties...
virtual void setColor(const QColor &color)
The fill color.
void startRender(QgsSymbolRenderContext &context) override
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const override
Returns the calculated value of the property with the specified key from within the collection...
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for data defined symbology.
Definition: qgssymbol.cpp:1089
static QString encodePenStyle(Qt::PenStyle style)
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:37
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsFilledMarkerSymbolLayer.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsSimpleMarkerSymbolLayer.
static QString svgSymbolPathToName(const QString &path, const QgsPathResolver &pathResolver)
Determines an SVG symbol&#39;s name from its path.
static double rescaleUom(double size, QgsUnitTypes::RenderUnit unit, const QgsStringMap &props)
Rescales the given size based on the uomScale found in the props, if any is found, otherwise returns the value un-modified.
One third circle (top left third)
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the stroke width.
Raster marker symbol layer class.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
bool shapeToPolygon(Shape shape, QPolygonF &polygon) const
Creates a polygon representing the specified shape.
void setStrokeColor(const QColor &color) override
Set stroke color.
double angle() const
Returns the rotation angle for the marker, in degrees clockwise from north.
QgsUnitTypes::RenderUnit mOffsetUnit
Offset units.
double updateDefaultAspectRatio()
Calculates the default marker aspect ratio between width and height.
void setColor(const QColor &color) override
The fill color.
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties...
void stopRender(QgsSymbolRenderContext &context) override
double opacity() const
Returns the marker opacity.
void setFixedAspectRatio(double ratio)
Set the marker aspect ratio between width and height to be used in rendering, if the value set is low...
bool mUsingCache
True if using cached images of markers for drawing.
static void adjustHueSaturation(QImage &image, double saturation, const QColor &colorizeColor=QColor(), double colorizeStrength=1.0)
Alter the hue or saturation of a QImage.
QPen mPen
QPen corresponding to marker&#39;s stroke style.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
Sets the map scale for the width of the marker&#39;s stroke.
#define DEFAULT_SCALE_METHOD
Q_GUI_EXPORT int qt_defaultDpiY()
double calculateSize(QgsSymbolRenderContext &context, bool &hasDataDefinedSize) const
Calculates the desired size of the marker, considering data defined size overrides.
Calculate scale by the area.
Definition: qgssymbol.h:96
double mOpacity
The marker default opacity.
qreal opacity() const
Returns the opacity for the symbol.
Definition: qgssymbol.h:602
double size() const
Returns the symbol size.
bool preservedAspectRatio() const
Returns the preserved aspect ratio value, true if fixed aspect ratio has been lower or equal to 0...
double mStrokeWidth
Stroke width.
QgsMapUnitScale mapUnitScale() const override
#define DEFAULT_RASTERMARKER_ANGLE
QBrush mBrush
QBrush corresponding to marker&#39;s fill style.
QgsSimpleMarkerSymbolLayerBase(QgsSimpleMarkerSymbolLayerBase::Shape shape=Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, QgsSymbol::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructor for QgsSimpleMarkerSymbolLayerBase.
QSize originalSize(const QString &path) const
Returns the original size (in pixels) of the image at the specified path.
void setHorizontalAnchorPoint(HorizontalAnchorPoint h)
Sets the horizontal anchor point for positioning the symbol.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
static void resolvePaths(QgsStringMap &properties, const QgsPathResolver &pathResolver, bool saving)
Turns relative paths in properties map to absolute when reading and vice versa when writing...
void setSizeMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol&#39;s size.
#define DEFAULT_FONTMARKER_JOINSTYLE
void setLayer(const QString &layer)
static double mapUnitScaleFactor(double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits, double mapUnitsPerPixel=1.0)
Returns scale factor for conversion to map units.
QColor color() const override
The fill color.
void calculateOffsetAndRotation(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle) const
Calculates the marker offset and rotation.
virtual QColor color() const
The fill color.
static void createRotationElement(QDomDocument &doc, QDomElement &element, const QString &rotationFunc)
void setFillColor(const QColor &color) override
Set fill color.
QColor fillColor() const override
Gets fill color.
QColor selectionColor() const
#define DEFAULT_FONTMARKER_ANGLE
double mAngle
Marker rotation angle, in degrees clockwise from north.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void writeFilledCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius)
Write filled circle (as hatch)
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit unit)
Sets the stroke width unit.
double calculateAspectRatio(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio) const
Calculates the marker aspect ratio between width and height.
void markerOffset(QgsSymbolRenderContext &context, double &offsetX, double &offsetY) const
Calculates the required marker offset, including both the symbol offset and any displacement required...
static double estimateMaxSymbolBleed(QgsSymbol *symbol, const QgsRenderContext &context)
Returns the maximum estimated bleed for the symbol.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Creates a new QgsFontMarkerSymbolLayer from a property map (see properties())
QgsMapUnitScale mSizeMapUnitScale
Marker size map unit scale.
Character, eg for font marker symbol layers.
static void parametricSvgToSld(QDomDocument &doc, QDomElement &graphicElem, const QString &path, const QColor &fillColor, double size, const QColor &strokeColor, double strokeWidth)
Encodes a reference to a parametric SVG into SLD, as a succession of parametric SVG using URL paramet...
Qt::PenJoinStyle penJoinStyle() const
Returns the marker&#39;s stroke join style (e.g., miter, bevel, etc).
ScaleMethod
Scale method.
Definition: qgssymbol.h:94
static Q_INVOKABLE QgsUnitTypes::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
QgsSimpleMarkerSymbolLayerBase::Shape shape() const
Returns the shape for the rendered marker symbol.
void setOpacity(double opacity)
Set the marker opacity.
double mapUnitsPerPixel() const
Returns current map units per pixel.
QPen mSelPen
QPen to use as stroke of selected symbols.
QString path() const
Returns the marker raster image path.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
QgsRenderContext & renderContext()
Returns a reference to the context&#39;s render context.
Definition: qgssymbol.h:572
QgsSymbol * subSymbol() override
Returns the symbol&#39;s sub symbol, if present.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
static Qt::PenStyle decodePenStyle(const QString &str)
static bool externalGraphicFromSld(QDomElement &element, QString &path, QString &mime, QColor &color, double &size)
void setPath(const QString &path)
Set the marker raster image path.
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void startRender(QgsSymbolRenderContext &context) override
bool selected() const
Definition: qgssymbol.h:611
static void resolvePaths(QgsStringMap &properties, const QgsPathResolver &pathResolver, bool saving)
Turns relative paths in properties map to absolute when reading and vice versa when writing...
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
Qt::PenStyle strokeStyle() const
Returns the marker&#39;s stroke style (e.g., solid, dashed, etc)
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void drawMarker(QPainter *p, QgsSymbolRenderContext &context)
Draws the marker shape in the specified painter.
HorizontalAnchorPoint
Symbol horizontal anchor points.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
virtual void setStrokeColor(const QColor &color)
Set stroke color.
VerticalAnchorPoint
Symbol vertical anchor points.
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
bool setPreservedAspectRatio(bool par)
Set preserved the marker aspect ratio between width and height.
QString layerType() const override
Returns a string that represents this layer type.
Q_GUI_EXPORT int qt_defaultDpiX()
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
QgsSymbol::ScaleMethod scaleMethod() const
Returns the method to use for scaling the marker&#39;s size.
QgsRasterMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
QgsExpressionContext & expressionContext()
Gets the expression context.
void containsParams(const QString &path, bool &hasFillParam, QColor &defaultFillColor, bool &hasStrokeParam, QColor &defaultStrokeColor, bool &hasStrokeWidthParam, double &defaultStrokeWidth) const
Tests if an svg file contains parameters for fill, stroke color, stroke width.
QString layerType() const override
Returns a string that represents this layer type.
static QString encodeScaleMethod(QgsSymbol::ScaleMethod scaleMethod)
static bool externalMarkerFromSld(QDomElement &element, QString &path, QString &format, int &markIndex, QColor &color, double &size)
void startRender(QgsSymbolRenderContext &context) override
QVector< QgsPoint > QgsPointSequence
void stopRender(QgsSymbolRenderContext &context) override
bool forceVectorOutput() const
QString layerType() const override
Returns a string that represents this layer type.
void setFixedAspectRatio(double ratio)
Set the marker aspect ratio between width and height to be used in rendering, if the value set is low...
QVector< QgsPointSequence > QgsRingSequence
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsFontMarkerSymbolLayer from an SLD XML element.
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol&#39;s size.
QString valueAsString(int key, const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a string...
Stroke style (eg solid, dashed)
#define DEFAULT_SIMPLEMARKER_BORDERCOLOR
Fill symbol.
Definition: qgssymbol.h:87
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns the set of attributes referenced by the layer.
Contains information about the context of a rendering operation.
Abstract base class for marker symbol layers.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
void writeCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius, const QString &lineStyleName, double width)
Write circle (as polyline)
void setShift(QPointF shift)
double calculateAspectRatio(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio) const
Calculates the marker aspect ratio between width and height.
QPainter * painter()
Returns the destination QPainter for the render operation.
virtual void setFillColor(const QColor &color)
Set fill color.
const QgsMapToPixel & mapToPixel() const
QColor color() const override
The fill color.
QImage pathAsImage(const QString &path, const QSize size, const bool keepAspectRatio, const double opacity, bool &fitsInCache)
Returns the specified path rendered as an image.
SymbolType type() const
Returns the symbol&#39;s type.
Definition: qgssymbol.h:120
QImage mCache
Cached image of marker, if using cached version.
QgsSvgMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
QgsSimpleMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
Struct for storing maximum and minimum scales for measurements in map units.
void setMapUnitScale(const QgsMapUnitScale &scale) override
#define DEFAULT_SIMPLEMARKER_ANGLE
#define DEFAULT_SIMPLEMARKER_COLOR
QgsStringMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void startRender(QgsSymbolRenderContext &context) override
QgsSimpleMarkerSymbolLayer(QgsSimpleMarkerSymbolLayerBase::Shape shape=Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, QgsSymbol::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD, const QColor &color=DEFAULT_SIMPLEMARKER_COLOR, const QColor &strokeColor=DEFAULT_SIMPLEMARKER_BORDERCOLOR, Qt::PenJoinStyle penJoinStyle=DEFAULT_SIMPLEMARKER_JOINSTYLE)
Constructor for QgsSimpleMarkerSymbolLayer.
QgsSymbol::ScaleMethod mScaleMethod
Marker size scaling method.
Qt::PenJoinStyle mPenJoinStyle
Stroke pen join style.
const QgsFeature * feature() const
Current feature being rendered - may be null.
Definition: qgssymbol.h:628
QgsSvgMarkerSymbolLayer(const QString &path, double size=DEFAULT_SVGMARKER_SIZE, double angle=DEFAULT_SVGMARKER_ANGLE, QgsSymbol::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructs SVG marker symbol layer with picture from given absolute path to a SVG file...
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
QByteArray svgContent(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0)
Gets SVG content.
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer&#39;s subsymbol. takes ownership of the passed symbol.
QColor fillColor() const override
Gets fill color.
HorizontalAnchorPoint mHorizontalAnchorPoint
Horizontal anchor point.
QImage mSelCache
Cached image of selected marker, if using cached version.
#define DEFAULT_FONTMARKER_CHR
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
void writeSldMarker(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
Writes the symbol layer definition as a SLD XML element.
void writeSldMarker(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const override
Writes the symbol layer definition as a SLD XML element.
static bool wellKnownMarkerFromSld(QDomElement &element, QString &name, QColor &color, QColor &strokeColor, Qt::PenStyle &strokeStyle, double &strokeWidth, double &size)
double mSize
Marker size.
void setAngle(double angle)
Sets the rotation angle for the marker.
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
QString layerType() const override
Returns a string that represents this layer type.
void stopRender(QgsSymbolRenderContext &context) override
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
double mDefaultAspectRatio
The marker default aspect ratio.
bool writeDxf(QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift=QPointF(0.0, 0.0)) const override
write as DXF
QgsMapUnitScale mOffsetMapUnitScale
Offset map unit scale.
QgsWkbTypes::GeometryType type
Definition: qgsgeometry.h:110
QgsGeometry geometry
Definition: qgsfeature.h:67
VerticalAnchorPoint mVerticalAnchorPoint
Vertical anchor point.
void stopRender(QgsSymbolRenderContext &context) override
void setFillColor(const QColor &color) override
Set fill color.
static bool shapeIsFilled(QgsSimpleMarkerSymbolLayerBase::Shape shape)
Returns true if a symbol shape has a fill.
static QString svgSymbolNameToPath(const QString &name, const QgsPathResolver &pathResolver)
Determines an SVG symbol&#39;s path from its name.
static QPointF _rotatedOffset(QPointF offset, double angle)
Adjusts a marker offset to account for rotation.
bool prepareMarkerShape(Shape shape)
Prepares the layer for drawing the specified shape (QPolygonF version)
Resolves relative paths into absolute paths and vice versa.
void setMapUnitScale(const QgsMapUnitScale &scale) override
bool setPreservedAspectRatio(bool par)
Set preserved the marker aspect ratio between width and height.
QString layerType() const override
Returns a string that represents this layer type.
static QgsSymbolLayer * create(const QgsStringMap &properties=QgsStringMap())
Creates a raster marker symbol layer from a string map of properties.
#define DEFAULT_SVGMARKER_SIZE
QPointF offset() const
Returns the marker&#39;s offset, which is the horizontal and vertical displacement which the rendered mar...
QgsFilledMarkerSymbolLayer(QgsSimpleMarkerSymbolLayerBase::Shape shape=Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, QgsSymbol::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructor for QgsFilledMarkerSymbolLayer.
Filled marker symbol layer, consisting of a shape which is rendered using a QgsFillSymbol.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
virtual QColor fillColor() const
Gets fill color.
#define DEFAULT_SVGMARKER_ANGLE
static void multiplyImageOpacity(QImage *image, qreal opacity)
Multiplies opacity of image pixel values with a (global) transparency value.
Rotated cross (lines only), "x" shape.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QPolygonF mPolygon
Polygon of points in shape. If polygon is empty then shape is using mPath.
QgsFilledMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
#define DEFAULT_FONTMARKER_FONT
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
QgsPropertyCollection mDataDefinedProperties
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:111
static QColor decodeColor(const QString &str)
void copyDataDefinedProperties(QgsSymbolLayer *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
void setOutputSize(const QRectF &r)
void setVerticalAnchorPoint(VerticalAnchorPoint v)
Sets the vertical anchor point for positioning the symbol.
QgsSymbol::RenderHints renderHints() const
Returns the rendering hint flags for the symbol.
Definition: qgssymbol.h:618
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...