QGIS API Documentation  3.25.0-Master (10b47c2603)
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 "qgsfontutils.h"
23 #include "qgsimagecache.h"
24 #include "qgsimageoperation.h"
25 #include "qgsrendercontext.h"
26 #include "qgslogger.h"
27 #include "qgssvgcache.h"
28 #include "qgsunittypes.h"
29 #include "qgssymbol.h"
30 #include "qgsfillsymbol.h"
31 
32 #include <QPainter>
33 #include <QSvgRenderer>
34 #include <QFileInfo>
35 #include <QDir>
36 #include <QDomDocument>
37 #include <QDomElement>
38 
39 #include <cmath>
40 
41 Q_GUI_EXPORT extern int qt_defaultDpiX();
42 Q_GUI_EXPORT extern int qt_defaultDpiY();
43 
44 static constexpr int MAX_FONT_CHARACTER_SIZE_IN_PIXELS = 500;
45 
46 static void _fixQPictureDPI( QPainter *p )
47 {
48  // QPicture makes an assumption that we drawing to it with system DPI.
49  // Then when being drawn, it scales the painter. The following call
50  // negates the effect. There is no way of setting QPicture's DPI.
51  // See QTBUG-20361
52  p->scale( static_cast< double >( qt_defaultDpiX() ) / p->device()->logicalDpiX(),
53  static_cast< double >( qt_defaultDpiY() ) / p->device()->logicalDpiY() );
54 }
55 
56 
58 
59 
60 //
61 // QgsSimpleMarkerSymbolLayerBase
62 //
63 
65 {
66  QList< Qgis::MarkerShape > shapes;
96 
97  return shapes;
98 }
99 
101  : mShape( shape )
102 {
103  mSize = size;
104  mAngle = angle;
105  mOffset = QPointF( 0, 0 );
109 }
110 
112 
114 {
115  switch ( shape )
116  {
139  return true;
140 
148  return false;
149  }
150  return true;
151 }
152 
154 {
155  const bool hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation
157  const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
158 
159  // use either QPolygonF or QPainterPath for drawing
160  if ( !prepareMarkerShape( mShape ) ) // drawing as a polygon
161  {
162  prepareMarkerPath( mShape ); // drawing as a painter path
163  }
164 
165  QTransform transform;
166 
167  // scale the shape (if the size is not going to be modified)
168  if ( !hasDataDefinedSize )
169  {
170  double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
172  {
173  // rendering for symbol previews -- an size in meters in map units can't be calculated, so treat the size as millimeters
174  // and clamp it to a reasonable range. It's the best we can do in this situation!
175  scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, QgsUnitTypes::RenderMillimeters ), 3.0 ), 100.0 );
176  }
177 
178  const double half = scaledSize / 2.0;
179  transform.scale( half, half );
180  }
181 
182  // rotate if the rotation is not going to be changed during the rendering
183  if ( !hasDataDefinedRotation && !qgsDoubleNear( mAngle, 0.0 ) )
184  {
185  transform.rotate( mAngle );
186  }
187 
188  if ( !mPolygon.isEmpty() )
189  mPolygon = transform.map( mPolygon );
190  else
191  mPath = transform.map( mPath );
192 
194 }
195 
197 {
198  Q_UNUSED( context )
199 }
200 
202 {
203  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
204  //of the rendered point!
205 
206  QPainter *p = context.renderContext().painter();
207  if ( !p )
208  {
209  return;
210  }
211 
212  bool hasDataDefinedSize = false;
213  const double scaledSize = calculateSize( context, hasDataDefinedSize );
214 
215  bool hasDataDefinedRotation = false;
216  QPointF offset;
217  double angle = 0;
218  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
219 
220  //data defined shape?
221  bool createdNewPath = false;
222  bool ok = true;
223  Qgis::MarkerShape symbol = mShape;
225  {
226  context.setOriginalValueVariable( encodeShape( symbol ) );
228  if ( !exprVal.isNull() )
229  {
230  const Qgis::MarkerShape decoded = decodeShape( exprVal.toString(), &ok );
231  if ( ok )
232  {
233  symbol = decoded;
234 
235  if ( !prepareMarkerShape( symbol ) ) // drawing as a polygon
236  {
237  prepareMarkerPath( symbol ); // drawing as a painter path
238  }
239  createdNewPath = true;
240  }
241  }
242  else
243  {
244  symbol = mShape;
245  }
246  }
247 
248  QTransform transform;
249 
250  // move to the desired position
251  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
252 
253  // resize if necessary
254  if ( hasDataDefinedSize || createdNewPath )
255  {
256  double s = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
258  {
259  // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
260  // and clamp it to a reasonable range. It's the best we can do in this situation!
261  s = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, QgsUnitTypes::RenderMillimeters ), 3.0 ), 100.0 );
262  }
263  const double half = s / 2.0;
264  transform.scale( half, half );
265  }
266 
267  if ( !qgsDoubleNear( angle, 0.0 ) && ( hasDataDefinedRotation || createdNewPath ) )
268  {
269  transform.rotate( angle );
270  }
271 
272  //need to pass: symbol, polygon, path
273 
274  QPolygonF polygon;
275  QPainterPath path;
276  if ( !mPolygon.isEmpty() )
277  {
278  polygon = transform.map( mPolygon );
279  }
280  else
281  {
282  path = transform.map( mPath );
283  }
284  draw( context, symbol, polygon, path );
285 }
286 
288 {
289  bool hasDataDefinedSize = false;
290  double scaledSize = calculateSize( context, hasDataDefinedSize );
291 
292  bool hasDataDefinedRotation = false;
293  QPointF offset;
294  double angle = 0;
295  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
296 
297  scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
298 
299  QTransform transform;
300 
301  // move to the desired position
302  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
303 
304  if ( !qgsDoubleNear( angle, 0.0 ) )
305  transform.rotate( angle );
306 
307  return transform.mapRect( QRectF( -scaledSize / 2.0,
308  -scaledSize / 2.0,
309  scaledSize,
310  scaledSize ) );
311 }
312 
314 {
315  if ( ok )
316  *ok = true;
317  const QString cleaned = name.toLower().trimmed();
318 
319  if ( cleaned == QLatin1String( "square" ) || cleaned == QLatin1String( "rectangle" ) )
321  else if ( cleaned == QLatin1String( "square_with_corners" ) )
323  else if ( cleaned == QLatin1String( "diamond" ) )
325  else if ( cleaned == QLatin1String( "pentagon" ) )
327  else if ( cleaned == QLatin1String( "hexagon" ) )
329  else if ( cleaned == QLatin1String( "octagon" ) )
331  else if ( cleaned == QLatin1String( "triangle" ) )
333  else if ( cleaned == QLatin1String( "equilateral_triangle" ) )
335  else if ( cleaned == QLatin1String( "star" ) || cleaned == QLatin1String( "regular_star" ) )
337  else if ( cleaned == QLatin1String( "arrow" ) )
339  else if ( cleaned == QLatin1String( "circle" ) )
341  else if ( cleaned == QLatin1String( "cross" ) )
343  else if ( cleaned == QLatin1String( "cross_fill" ) )
345  else if ( cleaned == QLatin1String( "cross2" ) || cleaned == QLatin1String( "x" ) )
347  else if ( cleaned == QLatin1String( "line" ) )
349  else if ( cleaned == QLatin1String( "arrowhead" ) )
351  else if ( cleaned == QLatin1String( "filled_arrowhead" ) )
353  else if ( cleaned == QLatin1String( "semi_circle" ) )
355  else if ( cleaned == QLatin1String( "third_circle" ) )
357  else if ( cleaned == QLatin1String( "quarter_circle" ) )
359  else if ( cleaned == QLatin1String( "quarter_square" ) )
361  else if ( cleaned == QLatin1String( "half_square" ) )
363  else if ( cleaned == QLatin1String( "diagonal_half_square" ) )
365  else if ( cleaned == QLatin1String( "right_half_triangle" ) )
367  else if ( cleaned == QLatin1String( "left_half_triangle" ) )
369  else if ( cleaned == QLatin1String( "asterisk_fill" ) )
371  else if ( cleaned == QLatin1String( "half_arc" ) )
373  else if ( cleaned == QLatin1String( "third_arc" ) )
375  else if ( cleaned == QLatin1String( "quarter_arc" ) )
377 
378  if ( ok )
379  *ok = false;
381 }
382 
384 {
385  switch ( shape )
386  {
388  return QStringLiteral( "square" );
390  return QStringLiteral( "quarter_square" );
392  return QStringLiteral( "half_square" );
394  return QStringLiteral( "diagonal_half_square" );
396  return QStringLiteral( "diamond" );
398  return QStringLiteral( "pentagon" );
400  return QStringLiteral( "hexagon" );
402  return QStringLiteral( "octagon" );
404  return QStringLiteral( "square_with_corners" );
406  return QStringLiteral( "triangle" );
408  return QStringLiteral( "equilateral_triangle" );
410  return QStringLiteral( "left_half_triangle" );
412  return QStringLiteral( "right_half_triangle" );
414  return QStringLiteral( "star" );
416  return QStringLiteral( "arrow" );
418  return QStringLiteral( "filled_arrowhead" );
420  return QStringLiteral( "cross_fill" );
422  return QStringLiteral( "circle" );
424  return QStringLiteral( "cross" );
426  return QStringLiteral( "cross2" );
428  return QStringLiteral( "line" );
430  return QStringLiteral( "arrowhead" );
432  return QStringLiteral( "semi_circle" );
434  return QStringLiteral( "third_circle" );
436  return QStringLiteral( "quarter_circle" );
438  return QStringLiteral( "asterisk_fill" );
440  return QStringLiteral( "half_arc" );
442  return QStringLiteral( "third_arc" );
444  return QStringLiteral( "quarter_arc" );
445  }
446  return QString();
447 }
448 
450 {
451  return shapeToPolygon( shape, mPolygon );
452 }
453 
455 {
456  polygon.clear();
457 
458  switch ( shape )
459  {
461  polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 1, 1 ) ) );
462  return true;
463 
465  {
466  static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 0.6072;
467 
468  polygon << QPointF( - VERTEX_OFFSET_FROM_ORIGIN, 1 )
469  << QPointF( VERTEX_OFFSET_FROM_ORIGIN, 1 )
470  << QPointF( 1, VERTEX_OFFSET_FROM_ORIGIN )
471  << QPointF( 1, -VERTEX_OFFSET_FROM_ORIGIN )
472  << QPointF( VERTEX_OFFSET_FROM_ORIGIN, -1 )
473  << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, -1 )
474  << QPointF( -1, -VERTEX_OFFSET_FROM_ORIGIN )
475  << QPointF( -1, VERTEX_OFFSET_FROM_ORIGIN )
476  << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 );
477  return true;
478  }
479 
481  polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 0 ) ) );
482  return true;
483 
485  polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 1 ) ) );
486  return true;
487 
489  polygon << QPointF( -1, -1 ) << QPointF( 1, 1 ) << QPointF( -1, 1 ) << QPointF( -1, -1 );
490  return true;
491 
493  polygon << QPointF( -1, 0 ) << QPointF( 0, 1 )
494  << QPointF( 1, 0 ) << QPointF( 0, -1 ) << QPointF( -1, 0 );
495  return true;
496 
498  /* angular-representation of hardcoded values used
499  polygon << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288.0 ) ) )
500  << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) )
501  << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) )
502  << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) )
503  << QPointF( 0, -1 ); */
504  polygon << QPointF( -0.9511, -0.3090 )
505  << QPointF( -0.5878, 0.8090 )
506  << QPointF( 0.5878, 0.8090 )
507  << QPointF( 0.9511, -0.3090 )
508  << QPointF( 0, -1 )
509  << QPointF( -0.9511, -0.3090 );
510  return true;
511 
513  /* angular-representation of hardcoded values used
514  polygon << QPointF( std::sin( DEG2RAD( 300.0 ) ), - std::cos( DEG2RAD( 300.0 ) ) )
515  << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
516  << QPointF( std::sin( DEG2RAD( 180.0 ) ), - std::cos( DEG2RAD( 180.0 ) ) )
517  << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
518  << QPointF( std::sin( DEG2RAD( 60.0 ) ), - std::cos( DEG2RAD( 60.0 ) ) )
519  << QPointF( 0, -1 ); */
520  polygon << QPointF( -0.8660, -0.5 )
521  << QPointF( -0.8660, 0.5 )
522  << QPointF( 0, 1 )
523  << QPointF( 0.8660, 0.5 )
524  << QPointF( 0.8660, -0.5 )
525  << QPointF( 0, -1 )
526  << QPointF( -0.8660, -0.5 );
527  return true;
528 
530  {
531  static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 1.0 / ( 1 + M_SQRT2 );
532 
533  polygon << QPointF( - VERTEX_OFFSET_FROM_ORIGIN, 1 )
534  << QPointF( VERTEX_OFFSET_FROM_ORIGIN, 1 )
535  << QPointF( 1, VERTEX_OFFSET_FROM_ORIGIN )
536  << QPointF( 1, -VERTEX_OFFSET_FROM_ORIGIN )
537  << QPointF( VERTEX_OFFSET_FROM_ORIGIN, -1 )
538  << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, -1 )
539  << QPointF( -1, -VERTEX_OFFSET_FROM_ORIGIN )
540  << QPointF( -1, VERTEX_OFFSET_FROM_ORIGIN )
541  << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 );
542  return true;
543  }
544 
546  polygon << QPointF( -1, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
547  return true;
548 
550  /* angular-representation of hardcoded values used
551  polygon << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
552  << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
553  << QPointF( 0, -1 ); */
554  polygon << QPointF( -0.8660, 0.5 )
555  << QPointF( 0.8660, 0.5 )
556  << QPointF( 0, -1 )
557  << QPointF( -0.8660, 0.5 );
558  return true;
559 
561  polygon << QPointF( 0, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( 0, 1 );
562  return true;
563 
565  polygon << QPointF( -1, 1 ) << QPointF( 0, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
566  return true;
567 
569  {
570  const double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
571 
572  polygon << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ) // 324
573  << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288 ) ) ) // 288
574  << QPointF( inner_r * std::sin( DEG2RAD( 252.0 ) ), - inner_r * std::cos( DEG2RAD( 252.0 ) ) ) // 252
575  << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) ) // 216
576  << QPointF( 0, inner_r ) // 180
577  << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) ) // 144
578  << QPointF( inner_r * std::sin( DEG2RAD( 108.0 ) ), - inner_r * std::cos( DEG2RAD( 108.0 ) ) ) // 108
579  << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) ) // 72
580  << QPointF( inner_r * std::sin( DEG2RAD( 36.0 ) ), - inner_r * std::cos( DEG2RAD( 36.0 ) ) ) // 36
581  << QPointF( 0, -1 )
582  << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ); // 324; // 0
583  return true;
584  }
585 
587  polygon << QPointF( 0, -1 )
588  << QPointF( 0.5, -0.5 )
589  << QPointF( 0.25, -0.5 )
590  << QPointF( 0.25, 1 )
591  << QPointF( -0.25, 1 )
592  << QPointF( -0.25, -0.5 )
593  << QPointF( -0.5, -0.5 )
594  << QPointF( 0, -1 );
595  return true;
596 
598  polygon << QPointF( 0, 0 ) << QPointF( -1, 1 ) << QPointF( -1, -1 ) << QPointF( 0, 0 );
599  return true;
600 
602  polygon << QPointF( -1, -0.2 )
603  << QPointF( -1, -0.2 )
604  << QPointF( -1, 0.2 )
605  << QPointF( -0.2, 0.2 )
606  << QPointF( -0.2, 1 )
607  << QPointF( 0.2, 1 )
608  << QPointF( 0.2, 0.2 )
609  << QPointF( 1, 0.2 )
610  << QPointF( 1, -0.2 )
611  << QPointF( 0.2, -0.2 )
612  << QPointF( 0.2, -1 )
613  << QPointF( -0.2, -1 )
614  << QPointF( -0.2, -0.2 )
615  << QPointF( -1, -0.2 );
616  return true;
617 
619  {
620  static constexpr double THICKNESS = 0.3;
621  static constexpr double HALF_THICKNESS = THICKNESS / 2.0;
622  static constexpr double INTERSECTION_POINT = THICKNESS / M_SQRT2;
623  static constexpr double DIAGONAL1 = M_SQRT1_2 - INTERSECTION_POINT * 0.5;
624  static constexpr double DIAGONAL2 = M_SQRT1_2 + INTERSECTION_POINT * 0.5;
625 
626  polygon << QPointF( -HALF_THICKNESS, -1 )
627  << QPointF( HALF_THICKNESS, -1 )
628  << QPointF( HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
629  << QPointF( DIAGONAL1, -DIAGONAL2 )
630  << QPointF( DIAGONAL2, -DIAGONAL1 )
631  << QPointF( HALF_THICKNESS + INTERSECTION_POINT, -HALF_THICKNESS )
632  << QPointF( 1, -HALF_THICKNESS )
633  << QPointF( 1, HALF_THICKNESS )
634  << QPointF( HALF_THICKNESS + INTERSECTION_POINT, HALF_THICKNESS )
635  << QPointF( DIAGONAL2, DIAGONAL1 )
636  << QPointF( DIAGONAL1, DIAGONAL2 )
637  << QPointF( HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
638  << QPointF( HALF_THICKNESS, 1 )
639  << QPointF( -HALF_THICKNESS, 1 )
640  << QPointF( -HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
641  << QPointF( -DIAGONAL1, DIAGONAL2 )
642  << QPointF( -DIAGONAL2, DIAGONAL1 )
643  << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, HALF_THICKNESS )
644  << QPointF( -1, HALF_THICKNESS )
645  << QPointF( -1, -HALF_THICKNESS )
646  << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, -HALF_THICKNESS )
647  << QPointF( -DIAGONAL2, -DIAGONAL1 )
648  << QPointF( -DIAGONAL1, -DIAGONAL2 )
649  << QPointF( -HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
650  << QPointF( -HALF_THICKNESS, -1 );
651  return true;
652  }
653 
665  return false;
666  }
667 
668  return false;
669 }
670 
672 {
673  mPath = QPainterPath();
674 
675  switch ( symbol )
676  {
678 
679  mPath.addEllipse( QRectF( -1, -1, 2, 2 ) ); // x,y,w,h
680  return true;
681 
683  mPath.arcTo( -1, -1, 2, 2, 0, 180 );
684  mPath.lineTo( 0, 0 );
685  return true;
686 
688  mPath.arcTo( -1, -1, 2, 2, 90, 120 );
689  mPath.lineTo( 0, 0 );
690  return true;
691 
693  mPath.arcTo( -1, -1, 2, 2, 90, 90 );
694  mPath.lineTo( 0, 0 );
695  return true;
696 
698  mPath.moveTo( 1, 0 );
699  mPath.arcTo( -1, -1, 2, 2, 0, 180 );
700  return true;
701 
703  mPath.moveTo( 0, -1 );
704  mPath.arcTo( -1, -1, 2, 2, 90, 120 );
705  return true;
706 
708  mPath.moveTo( 0, -1 );
709  mPath.arcTo( -1, -1, 2, 2, 90, 90 );
710  return true;
711 
713  mPath.moveTo( -1, 0 );
714  mPath.lineTo( 1, 0 ); // horizontal
715  mPath.moveTo( 0, -1 );
716  mPath.lineTo( 0, 1 ); // vertical
717  return true;
718 
720  mPath.moveTo( -1, -1 );
721  mPath.lineTo( 1, 1 );
722  mPath.moveTo( 1, -1 );
723  mPath.lineTo( -1, 1 );
724  return true;
725 
727  mPath.moveTo( 0, -1 );
728  mPath.lineTo( 0, 1 ); // vertical line
729  return true;
730 
732  mPath.moveTo( -1, -1 );
733  mPath.lineTo( 0, 0 );
734  mPath.lineTo( -1, 1 );
735  return true;
736 
755  return false;
756  }
757  return false;
758 }
759 
760 double QgsSimpleMarkerSymbolLayerBase::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
761 {
762  double scaledSize = mSize;
763 
765  bool ok = true;
766  if ( hasDataDefinedSize )
767  {
768  context.setOriginalValueVariable( mSize );
770  mSize, &ok );
771  }
772 
773  if ( hasDataDefinedSize && ok )
774  {
775  switch ( mScaleMethod )
776  {
778  scaledSize = std::sqrt( scaledSize );
779  break;
781  break;
782  }
783  }
784 
785  return scaledSize;
786 }
787 
788 void QgsSimpleMarkerSymbolLayerBase::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle ) const
789 {
790  //offset
791  double offsetX = 0;
792  double offsetY = 0;
793  markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
794  offset = QPointF( offsetX, offsetY );
795 
796  hasDataDefinedRotation = false;
797  //angle
798  bool ok = true;
799  angle = mAngle + mLineAngle;
801  {
802  context.setOriginalValueVariable( angle );
804 
805  // If the expression evaluation was not successful, fallback to static value
806  if ( !ok )
807  angle = mAngle + mLineAngle;
808 
809  hasDataDefinedRotation = true;
810  }
811 
812  hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || hasDataDefinedRotation;
813 
814  if ( hasDataDefinedRotation )
815  {
816  // For non-point markers, "dataDefinedRotation" means following the
817  // shape (shape-data defined). For them, "field-data defined" does
818  // not work at all. TODO: if "field-data defined" ever gets implemented
819  // we'll need a way to distinguish here between the two, possibly
820  // using another flag in renderHints()
821  const QgsFeature *f = context.feature();
822  if ( f )
823  {
824  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
825  {
826  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
827  angle += m2p.mapRotation();
828  }
829  }
830  }
831 
832  if ( angle )
834 }
835 
836 
837 //
838 // QgsSimpleMarkerSymbolLayer
839 //
840 
841 QgsSimpleMarkerSymbolLayer::QgsSimpleMarkerSymbolLayer( Qgis::MarkerShape shape, double size, double angle, Qgis::ScaleMethod scaleMethod, const QColor &color, const QColor &strokeColor, Qt::PenJoinStyle penJoinStyle )
842  : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
843  , mStrokeColor( strokeColor )
844  , mPenJoinStyle( penJoinStyle )
845 {
846  mColor = color;
847 }
848 
850 
852 {
856  Qt::PenJoinStyle penJoinStyle = DEFAULT_SIMPLEMARKER_JOINSTYLE;
860 
861  if ( props.contains( QStringLiteral( "name" ) ) )
862  {
863  shape = decodeShape( props[QStringLiteral( "name" )].toString() );
864  }
865  if ( props.contains( QStringLiteral( "color" ) ) )
866  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )].toString() );
867  if ( props.contains( QStringLiteral( "color_border" ) ) )
868  {
869  //pre 2.5 projects use "color_border"
870  strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color_border" )].toString() );
871  }
872  else if ( props.contains( QStringLiteral( "outline_color" ) ) )
873  {
874  strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )].toString() );
875  }
876  else if ( props.contains( QStringLiteral( "line_color" ) ) )
877  {
878  strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )].toString() );
879  }
880  if ( props.contains( QStringLiteral( "joinstyle" ) ) )
881  {
882  penJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() );
883  }
884  if ( props.contains( QStringLiteral( "size" ) ) )
885  size = props[QStringLiteral( "size" )].toDouble();
886  if ( props.contains( QStringLiteral( "angle" ) ) )
887  angle = props[QStringLiteral( "angle" )].toDouble();
888  if ( props.contains( QStringLiteral( "scale_method" ) ) )
889  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
890 
892  if ( props.contains( QStringLiteral( "offset" ) ) )
893  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
894  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
895  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
896  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
897  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
898  if ( props.contains( QStringLiteral( "size_unit" ) ) )
899  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
900  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
901  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
902 
903  if ( props.contains( QStringLiteral( "outline_style" ) ) )
904  {
905  m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "outline_style" )].toString() ) );
906  }
907  else if ( props.contains( QStringLiteral( "line_style" ) ) )
908  {
909  m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "line_style" )].toString() ) );
910  }
911  if ( props.contains( QStringLiteral( "outline_width" ) ) )
912  {
913  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
914  }
915  else if ( props.contains( QStringLiteral( "line_width" ) ) )
916  {
917  m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
918  }
919  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
920  {
921  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
922  }
923  if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
924  {
925  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
926  }
927  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
928  {
929  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
930  }
931 
932  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
933  {
934  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
935  }
936  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
937  {
938  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
939  }
940 
941  if ( props.contains( QStringLiteral( "cap_style" ) ) )
942  {
943  m->setPenCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( props[QStringLiteral( "cap_style" )].toString() ) );
944  }
945 
947 
948  return m;
949 }
950 
951 
953 {
954  return QStringLiteral( "SimpleMarker" );
955 }
956 
958 {
960 
961  QColor brushColor = mColor;
962  QColor penColor = mStrokeColor;
963 
964  brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
965  penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
966 
967  mBrush = QBrush( brushColor );
968  mPen = QPen( penColor );
969  mPen.setStyle( mStrokeStyle );
970  mPen.setCapStyle( mPenCapStyle );
971  mPen.setJoinStyle( mPenJoinStyle );
973 
974  QColor selBrushColor = context.renderContext().selectionColor();
975  QColor selPenColor = selBrushColor == mColor ? selBrushColor : mStrokeColor;
976  if ( context.opacity() < 1 && !SELECTION_IS_OPAQUE )
977  {
978  selBrushColor.setAlphaF( context.opacity() );
979  selPenColor.setAlphaF( context.opacity() );
980  }
981  mSelBrush = QBrush( selBrushColor );
982  mSelPen = QPen( selPenColor );
983  mSelPen.setStyle( mStrokeStyle );
985 
987  const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
988 
989  // use caching only when:
990  // - size, rotation, shape, color, stroke color is not data-defined
991  // - drawing to screen (not printer)
992  mUsingCache = !hasDataDefinedRotation && !hasDataDefinedSize && !context.renderContext().forceVectorOutput()
996 
997  if ( mUsingCache )
998  mCachedOpacity = context.opacity();
999 
1000  if ( !shapeIsFilled( mShape ) )
1001  {
1002  // some markers can't be drawn as a polygon (circle, cross)
1003  // For these set the selected stroke color to the selected color
1004  mSelPen.setColor( selBrushColor );
1005  }
1006 
1007 
1008  if ( mUsingCache )
1009  {
1010  if ( !prepareCache( context ) )
1011  {
1012  mUsingCache = false;
1013  }
1014  }
1015  else
1016  {
1017  mCache = QImage();
1018  mSelCache = QImage();
1019  }
1020 }
1021 
1022 
1024 {
1025  double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
1027  {
1028  // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
1029  // and clamp it to a reasonable range. It's the best we can do in this situation!
1030  scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, QgsUnitTypes::RenderMillimeters ), 3.0 ), 100.0 );
1031  }
1032 
1033  // take into account angle (which is not data-defined otherwise cache wouldn't be used)
1034  if ( !qgsDoubleNear( mAngle, 0.0 ) )
1035  {
1036  scaledSize = ( std::abs( std::sin( mAngle * M_PI / 180 ) ) + std::abs( std::cos( mAngle * M_PI / 180 ) ) ) * scaledSize;
1037  }
1038  // calculate necessary image size for the cache
1039  const 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
1040  const int imageSize = ( static_cast< int >( scaledSize ) + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width
1041  const double center = imageSize / 2.0;
1042  if ( imageSize > MAXIMUM_CACHE_WIDTH )
1043  {
1044  return false;
1045  }
1046 
1047  mCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
1048  mCache.fill( 0 );
1049 
1050  const bool needsBrush = shapeIsFilled( mShape );
1051 
1052  QPainter p;
1053  p.begin( &mCache );
1054  p.setRenderHint( QPainter::Antialiasing );
1055  p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1056  p.setPen( mPen );
1057  p.translate( QPointF( center, center ) );
1058  drawMarker( &p, context );
1059  p.end();
1060 
1061  // Construct the selected version of the Cache
1062 
1063  const QColor selColor = context.renderContext().selectionColor();
1064 
1065  mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
1066  mSelCache.fill( 0 );
1067 
1068  p.begin( &mSelCache );
1069  p.setRenderHint( QPainter::Antialiasing );
1070  p.setBrush( needsBrush ? mSelBrush : Qt::NoBrush );
1071  p.setPen( mSelPen );
1072  p.translate( QPointF( center, center ) );
1073  drawMarker( &p, context );
1074  p.end();
1075 
1076  // Check that the selected version is different. If not, then re-render,
1077  // filling the background with the selection color and using the normal
1078  // colors for the symbol .. could be ugly!
1079 
1080  if ( mSelCache == mCache )
1081  {
1082  p.begin( &mSelCache );
1083  p.setRenderHint( QPainter::Antialiasing );
1084  p.fillRect( 0, 0, imageSize, imageSize, selColor );
1085  p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1086  p.setPen( mPen );
1087  p.translate( QPointF( center, center ) );
1088  drawMarker( &p, context );
1089  p.end();
1090  }
1091 
1092  return true;
1093 }
1094 
1095 void QgsSimpleMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
1096 {
1097  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1098  //of the rendered point!
1099 
1100  QPainter *p = context.renderContext().painter();
1101  if ( !p )
1102  {
1103  return;
1104  }
1105 
1106  QColor brushColor = mColor;
1107  brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
1108  mBrush.setColor( brushColor );
1109 
1110  QColor penColor = mStrokeColor;
1111  penColor.setAlphaF( penColor.alphaF() * context.opacity() );
1112  mPen.setColor( penColor );
1113 
1114  bool ok = true;
1116  {
1119  if ( ok )
1120  {
1121  c.setAlphaF( c.alphaF() * context.opacity() );
1122  mBrush.setColor( c );
1123  }
1124  }
1126  {
1129  if ( ok )
1130  {
1131  c.setAlphaF( c.alphaF() * context.opacity() );
1132  mPen.setColor( c );
1133  mSelPen.setColor( c );
1134  }
1135  }
1137  {
1140  if ( ok )
1141  {
1144  }
1145  }
1147  {
1150  if ( ok )
1151  {
1154  }
1155  }
1157  {
1159  const QString style = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyJoinStyle, context.renderContext().expressionContext(), QString(), &ok );
1160  if ( ok )
1161  {
1162  mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1163  mSelPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1164  }
1165  }
1167  {
1169  const QString style = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyCapStyle, context.renderContext().expressionContext(), QString(), &ok );
1170  if ( ok )
1171  {
1172  mPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1173  mSelPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1174  }
1175  }
1176 
1177  if ( shapeIsFilled( shape ) )
1178  {
1179  p->setBrush( context.selected() ? mSelBrush : mBrush );
1180  }
1181  else
1182  {
1183  p->setBrush( Qt::NoBrush );
1184  }
1185  p->setPen( context.selected() ? mSelPen : mPen );
1186 
1187  if ( !polygon.isEmpty() )
1188  p->drawPolygon( polygon );
1189  else
1190  p->drawPath( path );
1191 }
1192 
1194 {
1195  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1196  //of the rendered point!
1197 
1198  QPainter *p = context.renderContext().painter();
1199  if ( !p )
1200  {
1201  return;
1202  }
1203 
1204  if ( mUsingCache && qgsDoubleNear( mCachedOpacity, context.opacity() ) )
1205  {
1206  const QImage &img = context.selected() ? mSelCache : mCache;
1207  const double s = img.width();
1208 
1209  bool hasDataDefinedSize = false;
1210  const double scaledSize = calculateSize( context, hasDataDefinedSize );
1211 
1212  bool hasDataDefinedRotation = false;
1213  QPointF offset;
1214  double angle = 0;
1215  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
1216 
1217  p->drawImage( QRectF( point.x() - s / 2.0 + offset.x(),
1218  point.y() - s / 2.0 + offset.y(),
1219  s, s ), img );
1220  }
1221  else
1222  {
1224  }
1225 }
1226 
1228 {
1229  QVariantMap map;
1230  map[QStringLiteral( "name" )] = encodeShape( mShape );
1231  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
1232  map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
1233  map[QStringLiteral( "size" )] = QString::number( mSize );
1234  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1235  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1236  map[QStringLiteral( "angle" )] = QString::number( mAngle );
1237  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1238  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1239  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1240  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1241  map[QStringLiteral( "outline_style" )] = QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle );
1242  map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
1243  map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
1244  map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
1245  map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
1246  map[QStringLiteral( "cap_style" )] = QgsSymbolLayerUtils::encodePenCapStyle( mPenCapStyle );
1247  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1248  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1249  return map;
1250 }
1251 
1253 {
1255  m->setOffset( mOffset );
1256  m->setSizeUnit( mSizeUnit );
1258  m->setOffsetUnit( mOffsetUnit );
1268  copyPaintEffect( m );
1269  return m;
1270 }
1271 
1272 void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1273 {
1274  // <Graphic>
1275  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
1276  element.appendChild( graphicElem );
1277 
1279  const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
1281 
1282  // <Rotation>
1283  QString angleFunc;
1284  bool ok;
1285  const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
1286  if ( !ok )
1287  {
1288  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
1289  }
1290  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
1291  {
1292  angleFunc = QString::number( angle + mAngle );
1293  }
1294  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
1295 
1296  // <Displacement>
1297  const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
1299 }
1300 
1301 QString QgsSimpleMarkerSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
1302 {
1303  Q_UNUSED( mmScaleFactor )
1304  Q_UNUSED( mapUnitScaleFactor )
1305 #if 0
1306  QString ogrType = "3"; //default is circle
1307  if ( mName == "square" )
1308  {
1309  ogrType = "5";
1310  }
1311  else if ( mName == "triangle" )
1312  {
1313  ogrType = "7";
1314  }
1315  else if ( mName == "star" )
1316  {
1317  ogrType = "9";
1318  }
1319  else if ( mName == "circle" )
1320  {
1321  ogrType = "3";
1322  }
1323  else if ( mName == "cross" )
1324  {
1325  ogrType = "0";
1326  }
1327  else if ( mName == "x" || mName == "cross2" )
1328  {
1329  ogrType = "1";
1330  }
1331  else if ( mName == "line" )
1332  {
1333  ogrType = "10";
1334  }
1335 
1336  QString ogrString;
1337  ogrString.append( "SYMBOL(" );
1338  ogrString.append( "id:" );
1339  ogrString.append( '\"' );
1340  ogrString.append( "ogr-sym-" );
1341  ogrString.append( ogrType );
1342  ogrString.append( '\"' );
1343  ogrString.append( ",c:" );
1344  ogrString.append( mColor.name() );
1345  ogrString.append( ",o:" );
1346  ogrString.append( mStrokeColor.name() );
1347  ogrString.append( QString( ",s:%1mm" ).arg( mSize ) );
1348  ogrString.append( ')' );
1349  return ogrString;
1350 #endif //0
1351 
1352  QString ogrString;
1353  ogrString.append( "PEN(" );
1354  ogrString.append( "c:" );
1355  ogrString.append( mColor.name() );
1356  ogrString.append( ",w:" );
1357  ogrString.append( QString::number( mSize ) );
1358  ogrString.append( "mm" );
1359  ogrString.append( ")" );
1360  return ogrString;
1361 }
1362 
1364 {
1365  QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
1366 
1367  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
1368  if ( graphicElem.isNull() )
1369  return nullptr;
1370 
1371  QString name = QStringLiteral( "square" );
1372  QColor color, strokeColor;
1373  double strokeWidth, size;
1374  Qt::PenStyle strokeStyle;
1375 
1377  return nullptr;
1378 
1379  double angle = 0.0;
1380  QString angleFunc;
1381  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
1382  {
1383  bool ok;
1384  const double d = angleFunc.toDouble( &ok );
1385  if ( ok )
1386  angle = d;
1387  }
1388 
1389  QPointF offset;
1391 
1392  const Qgis::MarkerShape shape = decodeShape( name );
1393 
1394  const QString uom = element.attribute( QStringLiteral( "uom" ) );
1398 
1400  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
1401  m->setColor( color );
1403  m->setAngle( angle );
1404  m->setOffset( offset );
1407  return m;
1408 }
1409 
1411 {
1412  Q_UNUSED( context )
1413 
1414  if ( mPolygon.count() != 0 )
1415  {
1416  p->drawPolygon( mPolygon );
1417  }
1418  else
1419  {
1420  p->drawPath( mPath );
1421  }
1422 }
1423 
1424 bool QgsSimpleMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
1425 {
1426  //data defined size?
1427  double size = mSize;
1428 
1429  const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
1430 
1431  //data defined size
1432  bool ok = true;
1433  if ( hasDataDefinedSize )
1434  {
1436 
1437  if ( ok )
1438  {
1439  switch ( mScaleMethod )
1440  {
1442  size = std::sqrt( size );
1443  break;
1445  break;
1446  }
1447  }
1448 
1450  }
1451 
1453  {
1454  size *= mmMapUnitScaleFactor;
1455  }
1456 
1458  {
1460  }
1461  const double halfSize = size / 2.0;
1462 
1463  //strokeWidth
1464  double strokeWidth = mStrokeWidth;
1465 
1467  {
1470  }
1473  {
1475  }
1476 
1477  //color
1478  QColor pc = mPen.color();
1479  QColor bc = mBrush.color();
1481  {
1484  }
1486  {
1489  }
1490 
1491  //offset
1492  double offsetX = 0;
1493  double offsetY = 0;
1494  markerOffset( context, offsetX, offsetY );
1495  offsetX *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1496  offsetY *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1497 
1498 
1499  QPointF off( offsetX, offsetY );
1500 
1501  //angle
1502  double angle = mAngle + mLineAngle;
1504  {
1505  context.setOriginalValueVariable( mAngle );
1507  }
1508 
1511  {
1513  const QString shapeName = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyName, context.renderContext().expressionContext(), QString(), &ok );
1514  if ( ok )
1515  {
1516  shape = decodeShape( shapeName, &ok );
1517  if ( !ok )
1518  shape = mShape;
1519  }
1520  }
1521 
1522  if ( angle )
1523  off = _rotatedOffset( off, angle );
1524 
1526 
1527  QTransform t;
1528  t.translate( shift.x() + off.x(), shift.y() - off.y() );
1529 
1530  if ( !qgsDoubleNear( angle, 0.0 ) )
1531  t.rotate( angle );
1532 
1533  QPolygonF polygon;
1534  if ( shapeToPolygon( shape, polygon ) )
1535  {
1536  t.scale( halfSize, -halfSize );
1537 
1538  polygon = t.map( polygon );
1539 
1540  QgsPointSequence p;
1541  p.reserve( polygon.size() );
1542  for ( int i = 0; i < polygon.size(); i++ )
1543  {
1544  p << QgsPoint( polygon[i] );
1545  }
1546 
1547  if ( mBrush.style() != Qt::NoBrush )
1548  e.writePolygon( QgsRingSequence() << p, layerName, QStringLiteral( "SOLID" ), bc );
1549  if ( mPen.style() != Qt::NoPen )
1550  e.writePolyline( p, layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1551  }
1552  else if ( shape == Qgis::MarkerShape::Circle )
1553  {
1554  shift += QPointF( off.x(), -off.y() );
1555  if ( mBrush.style() != Qt::NoBrush )
1556  e.writeFilledCircle( layerName, bc, QgsPoint( shift ), halfSize );
1557  if ( mPen.style() != Qt::NoPen )
1558  e.writeCircle( layerName, pc, QgsPoint( shift ), halfSize, QStringLiteral( "CONTINUOUS" ), strokeWidth );
1559  }
1560  else if ( shape == Qgis::MarkerShape::Line )
1561  {
1562  const QPointF pt1 = t.map( QPointF( 0, -halfSize ) );
1563  const QPointF pt2 = t.map( QPointF( 0, halfSize ) );
1564 
1565  if ( mPen.style() != Qt::NoPen )
1566  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1567  }
1568  else if ( shape == Qgis::MarkerShape::Cross )
1569  {
1570  if ( mPen.style() != Qt::NoPen )
1571  {
1572  const QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
1573  const QPointF pt2 = t.map( QPointF( halfSize, 0 ) );
1574  const QPointF pt3 = t.map( QPointF( 0, -halfSize ) );
1575  const QPointF pt4 = t.map( QPointF( 0, halfSize ) );
1576 
1577  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1578  e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1579  }
1580  }
1581  else if ( shape == Qgis::MarkerShape::Cross2 )
1582  {
1583  if ( mPen.style() != Qt::NoPen )
1584  {
1585  const QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
1586  const QPointF pt2 = t.map( QPointF( halfSize, halfSize ) );
1587  const QPointF pt3 = t.map( QPointF( halfSize, -halfSize ) );
1588  const QPointF pt4 = t.map( QPointF( -halfSize, halfSize ) );
1589 
1590  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1591  e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1592  }
1593  }
1594  else if ( shape == Qgis::MarkerShape::ArrowHead )
1595  {
1596  if ( mPen.style() != Qt::NoPen )
1597  {
1598  const QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
1599  const QPointF pt2 = t.map( QPointF( 0, 0 ) );
1600  const QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
1601 
1602  e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1603  e.writeLine( QgsPoint( pt3 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1604  }
1605  }
1606  else
1607  {
1608  QgsDebugMsg( QStringLiteral( "Unsupported dxf marker name %1" ).arg( encodeShape( shape ) ) );
1609  return false;
1610  }
1611 
1612  return true;
1613 }
1614 
1615 
1617 {
1619  mStrokeWidthUnit = unit;
1620 }
1621 
1623 {
1625  {
1626  return mStrokeWidthUnit;
1627  }
1629 }
1630 
1632 {
1634  mStrokeWidthMapUnitScale = scale;
1635 }
1636 
1638 {
1640  {
1641  return mStrokeWidthMapUnitScale;
1642  }
1643  return QgsMapUnitScale();
1644 }
1645 
1647 {
1651 }
1652 
1654 {
1655  QRectF symbolBounds = QgsSimpleMarkerSymbolLayerBase::bounds( point, context );
1656 
1657  // need to account for stroke width
1658  double penWidth = mStrokeWidth;
1659  bool ok = true;
1661  {
1664  if ( ok )
1665  {
1666  penWidth = strokeWidth;
1667  }
1668  }
1671  {
1674  if ( ok && strokeStyle == QLatin1String( "no" ) )
1675  {
1676  penWidth = 0.0;
1677  }
1678  }
1679  else if ( mStrokeStyle == Qt::NoPen )
1680  penWidth = 0;
1681 
1682  //antialiasing, add 1 pixel
1683  penWidth += 1;
1684 
1685  //extend bounds by pen width / 2.0
1686  symbolBounds.adjust( -penWidth / 2.0, -penWidth / 2.0,
1687  penWidth / 2.0, penWidth / 2.0 );
1688 
1689  return symbolBounds;
1690 }
1691 
1692 void QgsSimpleMarkerSymbolLayer::setColor( const QColor &color )
1693 {
1694  if ( shapeIsFilled( mShape ) )
1695  {
1696  setFillColor( color );
1697  }
1698  else
1699  {
1700  setStrokeColor( color );
1701  }
1702 }
1703 
1705 {
1706  if ( shapeIsFilled( mShape ) )
1707  {
1708  return fillColor();
1709  }
1710  else
1711  {
1712  return strokeColor();
1713  }
1714 }
1715 
1716 
1717 
1718 
1719 //
1720 // QgsFilledMarkerSymbolLayer
1721 //
1722 
1724  : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
1725 {
1726  mFill.reset( static_cast<QgsFillSymbol *>( QgsFillSymbol::createSimple( QVariantMap() ) ) );
1727 }
1728 
1730 
1732 {
1733  QString name = DEFAULT_SIMPLEMARKER_NAME;
1737 
1738  if ( props.contains( QStringLiteral( "name" ) ) )
1739  name = props[QStringLiteral( "name" )].toString();
1740  if ( props.contains( QStringLiteral( "size" ) ) )
1741  size = props[QStringLiteral( "size" )].toDouble();
1742  if ( props.contains( QStringLiteral( "angle" ) ) )
1743  angle = props[QStringLiteral( "angle" )].toDouble();
1744  if ( props.contains( QStringLiteral( "scale_method" ) ) )
1745  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1746 
1748  if ( props.contains( QStringLiteral( "offset" ) ) )
1749  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1750  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1751  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1752  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1753  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1754  if ( props.contains( QStringLiteral( "size_unit" ) ) )
1755  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1756  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1757  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1758  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1759  {
1760  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1761  }
1762  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1763  {
1764  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1765  }
1766 
1768 
1769  m->restoreOldDataDefinedProperties( props );
1770 
1771  return m;
1772 }
1773 
1775 {
1776  return QStringLiteral( "FilledMarker" );
1777 }
1778 
1780 {
1781  if ( mFill )
1782  {
1783  mFill->startRender( context.renderContext(), context.fields() );
1784  }
1785 
1787 }
1788 
1790 {
1791  if ( mFill )
1792  {
1793  mFill->stopRender( context.renderContext() );
1794  }
1795 }
1796 
1798 {
1799  QVariantMap map;
1800  map[QStringLiteral( "name" )] = encodeShape( mShape );
1801  map[QStringLiteral( "size" )] = QString::number( mSize );
1802  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1803  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1804  map[QStringLiteral( "angle" )] = QString::number( mAngle );
1805  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1806  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1807  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1808  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1809  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1810  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1811 
1812  if ( mFill )
1813  {
1814  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mFill->color() );
1815  }
1816  return map;
1817 }
1818 
1820 {
1822  copyPaintEffect( m );
1824  m->setSubSymbol( mFill->clone() );
1825  return m;
1826 }
1827 
1829 {
1830  return mFill.get();
1831 }
1832 
1834 {
1835  if ( symbol && symbol->type() == Qgis::SymbolType::Fill )
1836  {
1837  mFill.reset( static_cast<QgsFillSymbol *>( symbol ) );
1838  return true;
1839  }
1840  else
1841  {
1842  delete symbol;
1843  return false;
1844  }
1845 }
1846 
1848 {
1849  if ( mFill )
1850  {
1851  return QgsSymbolLayerUtils::estimateMaxSymbolBleed( mFill.get(), context );
1852  }
1853  return 0;
1854 }
1855 
1857 {
1858  QSet<QString> attr = QgsSimpleMarkerSymbolLayerBase::usedAttributes( context );
1859  if ( mFill )
1860  attr.unite( mFill->usedAttributes( context ) );
1861  return attr;
1862 }
1863 
1865 {
1867  return true;
1868  if ( mFill && mFill->hasDataDefinedProperties() )
1869  return true;
1870  return false;
1871 }
1872 
1874 {
1875  mColor = c;
1876  if ( mFill )
1877  mFill->setColor( c );
1878 }
1879 
1881 {
1882  return mFill ? mFill->color() : mColor;
1883 }
1884 
1886 {
1889  || ( mFill && mFill->usesMapUnits() );
1890 }
1891 
1892 void QgsFilledMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
1893 {
1894  //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1895  //of the rendered point!
1896 
1897  QPainter *p = context.renderContext().painter();
1898  if ( !p )
1899  {
1900  return;
1901  }
1902 
1903  const double prevOpacity = mFill->opacity();
1904  mFill->setOpacity( mFill->opacity() * context.opacity() );
1905 
1906  if ( shapeIsFilled( shape ) )
1907  {
1908  p->setBrush( Qt::red );
1909  }
1910  else
1911  {
1912  p->setBrush( Qt::NoBrush );
1913  }
1914  p->setPen( Qt::black );
1915 
1916  const bool prevIsSubsymbol = context.renderContext().flags() & Qgis::RenderContextFlag::RenderingSubSymbol;
1918 
1919  if ( !polygon.isEmpty() )
1920  {
1921  mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
1922  }
1923  else
1924  {
1925  const QPolygonF poly = path.toFillPolygon();
1926  mFill->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
1927  }
1928 
1930 
1931  mFill->setOpacity( prevOpacity );
1932 }
1933 
1934 
1936 
1937 
1938 QgsSvgMarkerSymbolLayer::QgsSvgMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
1939 {
1940  mSize = size;
1941  mAngle = angle;
1942  mOffset = QPointF( 0, 0 );
1944  mStrokeWidth = 0.2;
1946  mColor = QColor( 35, 35, 35 );
1947  mStrokeColor = QColor( 35, 35, 35 );
1948  setPath( path );
1949 }
1950 
1952 
1954 {
1955  QString name;
1956  double size = DEFAULT_SVGMARKER_SIZE;
1957  double angle = DEFAULT_SVGMARKER_ANGLE;
1959 
1960  if ( props.contains( QStringLiteral( "name" ) ) )
1961  name = props[QStringLiteral( "name" )].toString();
1962  if ( props.contains( QStringLiteral( "size" ) ) )
1963  size = props[QStringLiteral( "size" )].toDouble();
1964  if ( props.contains( QStringLiteral( "angle" ) ) )
1965  angle = props[QStringLiteral( "angle" )].toDouble();
1966  if ( props.contains( QStringLiteral( "scale_method" ) ) )
1967  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1968 
1970 
1971  if ( props.contains( QStringLiteral( "size_unit" ) ) )
1972  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1973  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1974  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1975  if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
1976  m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
1977  if ( props.contains( QStringLiteral( "offset" ) ) )
1978  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1979  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1980  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1981  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1982  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1983  if ( props.contains( QStringLiteral( "fill" ) ) )
1984  {
1985  //pre 2.5 projects used "fill"
1986  m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "fill" )].toString() ) );
1987  }
1988  else if ( props.contains( QStringLiteral( "color" ) ) )
1989  {
1990  m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )].toString() ) );
1991  }
1992  if ( props.contains( QStringLiteral( "outline" ) ) )
1993  {
1994  //pre 2.5 projects used "outline"
1995  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline" )].toString() ) );
1996  }
1997  else if ( props.contains( QStringLiteral( "outline_color" ) ) )
1998  {
1999  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )].toString() ) );
2000  }
2001  else if ( props.contains( QStringLiteral( "line_color" ) ) )
2002  {
2003  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )].toString() ) );
2004  }
2005 
2006  if ( props.contains( QStringLiteral( "outline-width" ) ) )
2007  {
2008  //pre 2.5 projects used "outline-width"
2009  m->setStrokeWidth( props[QStringLiteral( "outline-width" )].toDouble() );
2010  }
2011  else if ( props.contains( QStringLiteral( "outline_width" ) ) )
2012  {
2013  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
2014  }
2015  else if ( props.contains( QStringLiteral( "line_width" ) ) )
2016  {
2017  m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
2018  }
2019 
2020  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
2021  {
2022  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
2023  }
2024  else if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
2025  {
2026  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
2027  }
2028  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
2029  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
2030 
2031  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2032  {
2033  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2034  }
2035  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2036  {
2037  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2038  }
2039 
2040  m->restoreOldDataDefinedProperties( props );
2041 
2043 
2044  if ( props.contains( QStringLiteral( "parameters" ) ) )
2045  {
2046  const QVariantMap parameters = props[QStringLiteral( "parameters" )].toMap();
2048  }
2049 
2050  return m;
2051 }
2052 
2053 void QgsSvgMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
2054 {
2055  const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
2056  if ( it != properties.end() )
2057  {
2058  if ( saving )
2059  {
2060  it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
2061  }
2062  else
2063  {
2064  it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
2065  }
2066  }
2067 }
2068 
2069 void QgsSvgMarkerSymbolLayer::setPath( const QString &path )
2070 {
2071  mDefaultAspectRatio = 0;
2072  mHasFillParam = false;
2073  mPath = path;
2074  QColor defaultFillColor, defaultStrokeColor;
2075  double strokeWidth, fillOpacity, strokeOpacity;
2076  bool hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
2077  bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
2078  QgsApplication::svgCache()->containsParams( path, mHasFillParam, hasDefaultFillColor, defaultFillColor,
2079  hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
2080  hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2081  hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
2082  hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
2083 
2084  const double newFillOpacity = hasFillOpacityParam ? fillColor().alphaF() : 1.0;
2085  const double newStrokeOpacity = hasStrokeOpacityParam ? strokeColor().alphaF() : 1.0;
2086 
2087  if ( hasDefaultFillColor )
2088  {
2089  defaultFillColor.setAlphaF( newFillOpacity );
2090  setFillColor( defaultFillColor );
2091  }
2092  if ( hasDefaultFillOpacity )
2093  {
2094  QColor c = fillColor();
2095  c.setAlphaF( fillOpacity );
2096  setFillColor( c );
2097  }
2098  if ( hasDefaultStrokeColor )
2099  {
2100  defaultStrokeColor.setAlphaF( newStrokeOpacity );
2101  setStrokeColor( defaultStrokeColor );
2102  }
2103  if ( hasDefaultStrokeWidth )
2104  {
2106  }
2107  if ( hasDefaultStrokeOpacity )
2108  {
2109  QColor c = strokeColor();
2110  c.setAlphaF( strokeOpacity );
2111  setStrokeColor( c );
2112  }
2113 
2115 }
2116 
2118 {
2119  if ( mDefaultAspectRatio == 0.0 )
2120  {
2121  //size
2122  const double size = mSize;
2123  //assume 88 dpi as standard value
2124  const double widthScaleFactor = 3.465;
2125  const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( mPath, size, mColor, mStrokeColor, mStrokeWidth, widthScaleFactor );
2126  // set default aspect ratio
2127  mDefaultAspectRatio = svgViewbox.isValid() ? svgViewbox.height() / svgViewbox.width() : 0.0;
2128  }
2129  return mDefaultAspectRatio;
2130 }
2131 
2133 {
2134  const bool aPreservedAspectRatio = preservedAspectRatio();
2135  if ( aPreservedAspectRatio && !par )
2136  {
2138  }
2139  else if ( !aPreservedAspectRatio && par )
2140  {
2141  mFixedAspectRatio = 0.0;
2142  }
2143  return preservedAspectRatio();
2144 }
2145 
2146 void QgsSvgMarkerSymbolLayer::setParameters( const QMap<QString, QgsProperty> &parameters )
2147 {
2149 }
2150 
2151 
2153 {
2154  return QStringLiteral( "SvgMarker" );
2155 }
2156 
2158 {
2159  QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
2160  Q_UNUSED( context )
2161 }
2162 
2164 {
2165  Q_UNUSED( context )
2166 }
2167 
2169 {
2170  QPainter *p = context.renderContext().painter();
2171  if ( !p )
2172  return;
2173 
2174  bool hasDataDefinedSize = false;
2175  const double scaledWidth = calculateSize( context, hasDataDefinedSize );
2176  const double width = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2177 
2178  //don't render symbols with a width below one or above 10,000 pixels
2179  if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2180  {
2181  return;
2182  }
2183 
2184  const QgsScopedQPainterState painterState( p );
2185 
2186  bool hasDataDefinedAspectRatio = false;
2187  const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2188  double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2189 
2191 
2192  double strokeWidth = mStrokeWidth;
2194  {
2197  }
2199 
2200  QColor fillColor = mColor;
2201  if ( context.selected() && mHasFillParam )
2202  {
2203  fillColor = context.renderContext().selectionColor();
2204  }
2206  {
2209  }
2210 
2211  QColor strokeColor = mStrokeColor;
2213  {
2216  }
2217 
2218  QString path = mPath;
2220  {
2221  context.setOriginalValueVariable( mPath );
2223  context.renderContext().pathResolver() );
2225  {
2226  // adjust height of data defined path
2227  const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2228  context.renderContext().scaleFactor(), aspectRatio,
2229  ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2230  scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2231  }
2232  }
2233 
2234  QPointF outputOffset;
2235  double angle = 0.0;
2236  calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2237 
2238  p->translate( point + outputOffset );
2239 
2240  const bool rotated = !qgsDoubleNear( angle, 0 );
2241  if ( rotated )
2242  p->rotate( angle );
2243 
2244  bool fitsInCache = true;
2245  bool usePict = true;
2246  const bool rasterizeSelected = !mHasFillParam || mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyName );
2247  if ( ( !context.renderContext().forceVectorOutput() && !rotated ) || ( context.selected() && rasterizeSelected ) )
2248  {
2250  context.renderContext().scaleFactor(), fitsInCache, aspectRatio,
2251  ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2252  if ( fitsInCache && img.width() > 1 )
2253  {
2254  usePict = false;
2255 
2256  if ( context.selected() )
2258 
2259  //consider transparency
2260  if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2261  {
2262  QImage transparentImage = img.copy();
2263  QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2264  p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
2265  }
2266  else
2267  {
2268  p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2269  }
2270  }
2271  }
2272 
2273  if ( usePict || !fitsInCache )
2274  {
2275  p->setOpacity( context.opacity() );
2276  const QPicture pct = QgsApplication::svgCache()->svgAsPicture( path, width, fillColor, strokeColor, strokeWidth,
2277  context.renderContext().scaleFactor(), context.renderContext().forceVectorOutput(), aspectRatio,
2278  ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2279  if ( pct.width() > 1 )
2280  {
2281  const QgsScopedQPainterState painterPictureState( p );
2282  _fixQPictureDPI( p );
2283  p->drawPicture( 0, 0, pct );
2284  }
2285  }
2286 
2287  // workaround issue with nested QPictures forgetting antialiasing flag - see https://github.com/qgis/QGIS/issues/22909
2289 }
2290 
2291 double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2292 {
2293  double scaledSize = mSize;
2295 
2296  bool ok = true;
2297  if ( hasDataDefinedSize )
2298  {
2299  context.setOriginalValueVariable( mSize );
2301  }
2302  else
2303  {
2305  if ( hasDataDefinedSize )
2306  {
2307  context.setOriginalValueVariable( mSize );
2309  }
2310  }
2311 
2312  if ( hasDataDefinedSize && ok )
2313  {
2314  switch ( mScaleMethod )
2315  {
2317  scaledSize = std::sqrt( scaledSize );
2318  break;
2320  break;
2321  }
2322  }
2323 
2324  return scaledSize;
2325 }
2326 
2327 double QgsSvgMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2328 {
2330  if ( !hasDataDefinedAspectRatio )
2331  return mFixedAspectRatio;
2332 
2334  return 0.0;
2335 
2336  double scaledAspectRatio = mDefaultAspectRatio;
2337  if ( mFixedAspectRatio > 0.0 )
2338  scaledAspectRatio = mFixedAspectRatio;
2339 
2340  const double defaultHeight = mSize * scaledAspectRatio;
2341  scaledAspectRatio = defaultHeight / scaledSize;
2342 
2343  bool ok = true;
2344  double scaledHeight = scaledSize * scaledAspectRatio;
2346  {
2347  context.setOriginalValueVariable( defaultHeight );
2348  scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyHeight, context.renderContext().expressionContext(), defaultHeight, &ok );
2349  }
2350 
2351  if ( hasDataDefinedAspectRatio && ok )
2352  {
2353  switch ( mScaleMethod )
2354  {
2356  scaledHeight = sqrt( scaledHeight );
2357  break;
2359  break;
2360  }
2361  }
2362 
2363  scaledAspectRatio = scaledHeight / scaledSize;
2364 
2365  return scaledAspectRatio;
2366 }
2367 
2368 void QgsSvgMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
2369 {
2370  //offset
2371  double offsetX = 0;
2372  double offsetY = 0;
2373  markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
2374  offset = QPointF( offsetX, offsetY );
2375 
2376  angle = mAngle + mLineAngle;
2378  {
2379  context.setOriginalValueVariable( mAngle );
2381  }
2382 
2384  if ( hasDataDefinedRotation )
2385  {
2386  // For non-point markers, "dataDefinedRotation" means following the
2387  // shape (shape-data defined). For them, "field-data defined" does
2388  // not work at all. TODO: if "field-data defined" ever gets implemented
2389  // we'll need a way to distinguish here between the two, possibly
2390  // using another flag in renderHints()
2391  const QgsFeature *f = context.feature();
2392  if ( f )
2393  {
2394  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
2395  {
2396  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2397  angle += m2p.mapRotation();
2398  }
2399  }
2400  }
2401 
2402  if ( angle )
2404 }
2405 
2406 
2408 {
2409  QVariantMap map;
2410  map[QStringLiteral( "name" )] = mPath;
2411  map[QStringLiteral( "size" )] = QString::number( mSize );
2412  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2413  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2414  map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2415  map[QStringLiteral( "angle" )] = QString::number( mAngle );
2416  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2417  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2418  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2419  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2420  map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
2421  map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
2422  map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
2423  map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2424  map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2425  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
2426  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
2427 
2428  map[QStringLiteral( "parameters" )] = QgsProperty::propertyMapToVariantMap( mParameters );
2429 
2430  return map;
2431 }
2432 
2434 {
2438 }
2439 
2441 {
2444  m->setColor( mColor );
2449  m->setOffset( mOffset );
2450  m->setOffsetUnit( mOffsetUnit );
2452  m->setSizeUnit( mSizeUnit );
2456  m->setParameters( mParameters );
2457 
2459  copyPaintEffect( m );
2460  return m;
2461 }
2462 
2464 {
2466  mStrokeWidthUnit = unit;
2467 }
2468 
2470 {
2472  if ( unit != mStrokeWidthUnit )
2473  {
2475  }
2476  return unit;
2477 }
2478 
2480 {
2482  mStrokeWidthMapUnitScale = scale;
2483 }
2484 
2486 {
2488  {
2489  return mStrokeWidthMapUnitScale;
2490  }
2491  return QgsMapUnitScale();
2492 }
2493 
2494 void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
2495 {
2496  // <Graphic>
2497  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2498  element.appendChild( graphicElem );
2499 
2500  // encode a parametric SVG reference
2501  const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
2504 
2505  // <Rotation>
2506  QString angleFunc;
2507  bool ok;
2508  const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2509  if ( !ok )
2510  {
2511  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
2512  }
2513  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2514  {
2515  angleFunc = QString::number( angle + mAngle );
2516  }
2517 
2518  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2519 
2520  // <Displacement>
2521  const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2523 }
2524 
2526 {
2527  QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
2528 
2529  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
2530  if ( graphicElem.isNull() )
2531  return nullptr;
2532 
2533  QString path, mimeType;
2534  QColor fillColor;
2535  double size;
2536 
2537  if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor, size ) )
2538  return nullptr;
2539 
2540  const QString uom = element.attribute( QStringLiteral( "uom" ) );
2542 
2543  if ( mimeType != QLatin1String( "image/svg+xml" ) )
2544  return nullptr;
2545 
2546  double angle = 0.0;
2547  QString angleFunc;
2548  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2549  {
2550  bool ok;
2551  const double d = angleFunc.toDouble( &ok );
2552  if ( ok )
2553  angle = d;
2554  }
2555 
2556  QPointF offset;
2558 
2560  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
2561  m->setFillColor( fillColor );
2562  //m->setStrokeColor( strokeColor );
2563  //m->setStrokeWidth( strokeWidth );
2564  m->setAngle( angle );
2565  m->setOffset( offset );
2566  return m;
2567 }
2568 
2569 bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
2570 {
2571  //size
2572  double size = mSize;
2573 
2574  const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
2575 
2576  bool ok = true;
2577  if ( hasDataDefinedSize )
2578  {
2579  context.setOriginalValueVariable( mSize );
2581  }
2582 
2583  if ( hasDataDefinedSize && ok )
2584  {
2585  switch ( mScaleMethod )
2586  {
2588  size = std::sqrt( size );
2589  break;
2591  break;
2592  }
2593  }
2594 
2596  {
2597  size *= mmMapUnitScaleFactor;
2598  }
2599 
2600  //offset, angle
2601  QPointF offset = mOffset;
2602 
2604  {
2606  const QVariant val = mDataDefinedProperties.value( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext(), QString() );
2607  const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
2608  if ( ok )
2609  offset = res;
2610  }
2611  const double offsetX = offset.x();
2612  const double offsetY = offset.y();
2613 
2614  QPointF outputOffset( offsetX, offsetY );
2615 
2616  double angle = mAngle + mLineAngle;
2618  {
2619  context.setOriginalValueVariable( mAngle );
2621  }
2622 
2623  if ( angle )
2624  outputOffset = _rotatedOffset( outputOffset, angle );
2625 
2627 
2628  QString path = mPath;
2630  {
2631  context.setOriginalValueVariable( mPath );
2633  context.renderContext().pathResolver() );
2634  }
2635 
2636  double strokeWidth = mStrokeWidth;
2638  {
2641  }
2643 
2644  QColor fillColor = mColor;
2646  {
2649  }
2650 
2651  QColor strokeColor = mStrokeColor;
2653  {
2656  }
2657 
2659 
2660  const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, size, fillColor, strokeColor, strokeWidth,
2662  ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2663 
2664  QSvgRenderer r( svgContent );
2665  if ( !r.isValid() )
2666  return false;
2667 
2668  QgsDxfPaintDevice pd( &e );
2669  pd.setDrawingSize( QSizeF( r.defaultSize() ) );
2670 
2671  QSizeF outSize( r.defaultSize() );
2672  outSize.scale( size, size, Qt::KeepAspectRatio );
2673 
2674  QPainter p;
2675  p.begin( &pd );
2676  if ( !qgsDoubleNear( angle, 0.0 ) )
2677  {
2678  p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
2679  p.rotate( angle );
2680  p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
2681  }
2682  pd.setShift( shift + QPointF( outputOffset.x(), -outputOffset.y() ) );
2683  pd.setOutputSize( QRectF( -outSize.width() / 2.0, -outSize.height() / 2.0, outSize.width(), outSize.height() ) );
2684  pd.setLayer( layerName );
2685  r.render( &p );
2686  p.end();
2687  return true;
2688 }
2689 
2691 {
2692  bool hasDataDefinedSize = false;
2693  double scaledWidth = calculateSize( context, hasDataDefinedSize );
2694 
2695  bool hasDataDefinedAspectRatio = false;
2696  const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2697  double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2698 
2699  scaledWidth = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2700  scaledHeight = context.renderContext().convertToPainterUnits( scaledHeight, mSizeUnit, mSizeMapUnitScale );
2701 
2702  //don't render symbols with size below one or above 10,000 pixels
2703  if ( static_cast< int >( scaledWidth ) < 1 || 10000.0 < scaledWidth )
2704  {
2705  return QRectF();
2706  }
2707 
2708  QPointF outputOffset;
2709  double angle = 0.0;
2710  calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2711 
2712  double strokeWidth = mStrokeWidth;
2714  {
2717  }
2719 
2720  QString path = mPath;
2722  {
2723  context.setOriginalValueVariable( mPath );
2725  context.renderContext().pathResolver() );
2727  {
2728  // need to get colors to take advantage of cached SVGs
2729  QColor fillColor = mColor;
2731  {
2734  }
2735 
2736  const QColor strokeColor = mStrokeColor;
2738  {
2741  }
2742 
2744 
2745  // adjust height of data defined path
2746  const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2747  context.renderContext().scaleFactor(), aspectRatio,
2748  ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2749  scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2750  }
2751  }
2752 
2753  QTransform transform;
2754  // move to the desired position
2755  transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
2756 
2757  if ( !qgsDoubleNear( angle, 0.0 ) )
2758  transform.rotate( angle );
2759 
2760  //antialiasing
2761  strokeWidth += 1.0 / 2.0;
2762 
2763  QRectF symbolBounds = transform.mapRect( QRectF( -scaledWidth / 2.0,
2764  -scaledHeight / 2.0,
2765  scaledWidth,
2766  scaledHeight ) );
2767 
2768  //extend bounds by pen width / 2.0
2769  symbolBounds.adjust( -strokeWidth / 2.0, -strokeWidth / 2.0,
2770  strokeWidth / 2.0, strokeWidth / 2.0 );
2771 
2772  return symbolBounds;
2773 }
2774 
2776 
2777 QgsRasterMarkerSymbolLayer::QgsRasterMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
2778  : mPath( path )
2779 {
2780  mSize = size;
2781  mAngle = angle;
2782  mOffset = QPointF( 0, 0 );
2785 }
2786 
2788 
2790 {
2791  QString path;
2795 
2796  if ( props.contains( QStringLiteral( "imageFile" ) ) )
2797  path = props[QStringLiteral( "imageFile" )].toString();
2798  if ( props.contains( QStringLiteral( "size" ) ) )
2799  size = props[QStringLiteral( "size" )].toDouble();
2800  if ( props.contains( QStringLiteral( "angle" ) ) )
2801  angle = props[QStringLiteral( "angle" )].toDouble();
2802  if ( props.contains( QStringLiteral( "scale_method" ) ) )
2803  scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
2804 
2805  std::unique_ptr< QgsRasterMarkerSymbolLayer > m = std::make_unique< QgsRasterMarkerSymbolLayer >( path, size, angle, scaleMethod );
2806  m->setCommonProperties( props );
2807  return m.release();
2808 }
2809 
2810 void QgsRasterMarkerSymbolLayer::setCommonProperties( const QVariantMap &properties )
2811 {
2812  if ( properties.contains( QStringLiteral( "alpha" ) ) )
2813  {
2814  setOpacity( properties[QStringLiteral( "alpha" )].toDouble() );
2815  }
2816 
2817  if ( properties.contains( QStringLiteral( "size_unit" ) ) )
2818  setSizeUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "size_unit" )].toString() ) );
2819  if ( properties.contains( QStringLiteral( "size_map_unit_scale" ) ) )
2820  setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "size_map_unit_scale" )].toString() ) );
2821  if ( properties.contains( QStringLiteral( "fixedAspectRatio" ) ) )
2822  setFixedAspectRatio( properties[QStringLiteral( "fixedAspectRatio" )].toDouble() );
2823 
2824  if ( properties.contains( QStringLiteral( "offset" ) ) )
2825  setOffset( QgsSymbolLayerUtils::decodePoint( properties[QStringLiteral( "offset" )].toString() ) );
2826  if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
2827  setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )].toString() ) );
2828  if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2829  setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
2830 
2831  if ( properties.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2832  {
2833  setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( properties[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2834  }
2835  if ( properties.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2836  {
2837  setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( properties[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2838  }
2839 
2842 }
2843 
2844 void QgsRasterMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
2845 {
2846  const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
2847  if ( it != properties.end() && it.value().type() == QVariant::String )
2848  {
2849  if ( saving )
2850  it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
2851  else
2852  it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
2853  }
2854 }
2855 
2856 void QgsRasterMarkerSymbolLayer::setPath( const QString &path )
2857 {
2858  mPath = path;
2860 }
2861 
2863 {
2864  const bool aPreservedAspectRatio = preservedAspectRatio();
2865  if ( aPreservedAspectRatio && !par )
2866  {
2868  }
2869  else if ( !aPreservedAspectRatio && par )
2870  {
2871  mFixedAspectRatio = 0.0;
2872  }
2873  return preservedAspectRatio();
2874 }
2875 
2877 {
2878  if ( mDefaultAspectRatio == 0.0 )
2879  {
2880  const QSize size = QgsApplication::imageCache()->originalSize( mPath );
2881  mDefaultAspectRatio = ( !size.isNull() && size.isValid() && size.width() > 0 ) ? static_cast< double >( size.height() ) / static_cast< double >( size.width() ) : 0.0;
2882  }
2883  return mDefaultAspectRatio;
2884 }
2885 
2887 {
2888  return QStringLiteral( "RasterMarker" );
2889 }
2890 
2892 {
2893  QPainter *p = context.renderContext().painter();
2894  if ( !p )
2895  return;
2896 
2897  QString path = mPath;
2899  {
2900  context.setOriginalValueVariable( mPath );
2902  }
2903 
2904  if ( path.isEmpty() )
2905  return;
2906 
2907  double width = 0.0;
2908  double height = 0.0;
2909 
2910  bool hasDataDefinedSize = false;
2911  const double scaledSize = calculateSize( context, hasDataDefinedSize );
2912 
2913  bool hasDataDefinedAspectRatio = false;
2914  const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
2915 
2916  QPointF outputOffset;
2917  double angle = 0.0;
2918 
2919  // RenderPercentage Unit Type takes original image size
2921  {
2922  const QSize size = QgsApplication::imageCache()->originalSize( path );
2923  if ( size.isEmpty() )
2924  return;
2925 
2926  width = ( scaledSize * static_cast< double >( size.width() ) ) / 100.0;
2927  height = ( scaledSize * static_cast< double >( size.height() ) ) / 100.0;
2928 
2929  // don't render symbols with size below one or above 10,000 pixels
2930  if ( static_cast< int >( width ) < 1 || 10000.0 < width || static_cast< int >( height ) < 1 || 10000.0 < height )
2931  return;
2932 
2933  calculateOffsetAndRotation( context, width, height, outputOffset, angle );
2934  }
2935  else
2936  {
2937  width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
2938  height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
2939 
2940  if ( preservedAspectRatio() && path != mPath )
2941  {
2942  const QSize size = QgsApplication::imageCache()->originalSize( path );
2943  if ( !size.isNull() && size.isValid() && size.width() > 0 )
2944  {
2945  height = width * ( static_cast< double >( size.height() ) / static_cast< double >( size.width() ) );
2946  }
2947  }
2948 
2949  // don't render symbols with size below one or above 10,000 pixels
2950  if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2951  return;
2952 
2953  calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
2954  }
2955 
2956  const QgsScopedQPainterState painterState( p );
2957  p->translate( point + outputOffset );
2958 
2959  const bool rotated = !qgsDoubleNear( angle, 0 );
2960  if ( rotated )
2961  p->rotate( angle );
2962 
2963  double opacity = mOpacity;
2965  {
2968  }
2969  opacity *= context.opacity();
2970 
2971  QImage img = fetchImage( context.renderContext(), path, QSize( width, preservedAspectRatio() ? 0 : width * aspectRatio ), preservedAspectRatio(), opacity );
2972  if ( !img.isNull() )
2973  {
2974  if ( context.selected() )
2975  {
2977  }
2978 
2979  p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2980  }
2981 }
2982 
2983 QImage QgsRasterMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
2984 {
2985  bool cached = false;
2986  return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking );
2987 }
2988 
2989 double QgsRasterMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2990 {
2991  double scaledSize = mSize;
2993 
2994  bool ok = true;
2995  if ( hasDataDefinedSize )
2996  {
2997  context.setOriginalValueVariable( mSize );
2999  }
3000  else
3001  {
3003  if ( hasDataDefinedSize )
3004  {
3005  context.setOriginalValueVariable( mSize );
3007  }
3008  }
3009 
3010  if ( hasDataDefinedSize && ok )
3011  {
3012  switch ( mScaleMethod )
3013  {
3015  scaledSize = std::sqrt( scaledSize );
3016  break;
3018  break;
3019  }
3020  }
3021 
3022  return scaledSize;
3023 }
3024 
3025 double QgsRasterMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
3026 {
3028  if ( !hasDataDefinedAspectRatio )
3029  return mFixedAspectRatio;
3030 
3032  return 0.0;
3033 
3034  double scaledAspectRatio = mDefaultAspectRatio;
3035  if ( mFixedAspectRatio > 0.0 )
3036  scaledAspectRatio = mFixedAspectRatio;
3037 
3038  const double defaultHeight = mSize * scaledAspectRatio;
3039  scaledAspectRatio = defaultHeight / scaledSize;
3040 
3041  bool ok = true;
3042  double scaledHeight = scaledSize * scaledAspectRatio;
3044  {
3045  context.setOriginalValueVariable( defaultHeight );
3046  scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyHeight, context.renderContext().expressionContext(), defaultHeight, &ok );
3047  }
3048 
3049  if ( hasDataDefinedAspectRatio && ok )
3050  {
3051  switch ( mScaleMethod )
3052  {
3054  scaledHeight = sqrt( scaledHeight );
3055  break;
3057  break;
3058  }
3059  }
3060 
3061  scaledAspectRatio = scaledHeight / scaledSize;
3062 
3063  return scaledAspectRatio;
3064 }
3065 
3066 void QgsRasterMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
3067 {
3068  //offset
3069  double offsetX = 0;
3070  double offsetY = 0;
3071  markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
3072  offset = QPointF( offsetX, offsetY );
3073 
3074  angle = mAngle + mLineAngle;
3076  {
3077  context.setOriginalValueVariable( mAngle );
3079  }
3080 
3082  if ( hasDataDefinedRotation )
3083  {
3084  const QgsFeature *f = context.feature();
3085  if ( f )
3086  {
3087  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
3088  {
3089  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3090  angle += m2p.mapRotation();
3091  }
3092  }
3093  }
3094 
3095  if ( angle )
3097 }
3098 
3099 
3101 {
3102  QVariantMap map;
3103  map[QStringLiteral( "imageFile" )] = mPath;
3104  map[QStringLiteral( "size" )] = QString::number( mSize );
3105  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3106  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3107  map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
3108  map[QStringLiteral( "angle" )] = QString::number( mAngle );
3109  map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
3110  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3111  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3112  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3113  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
3114  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3115  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3116  return map;
3117 }
3118 
3120 {
3121  std::unique_ptr< QgsRasterMarkerSymbolLayer > m = std::make_unique< QgsRasterMarkerSymbolLayer >( mPath, mSize, mAngle );
3122  copyCommonProperties( m.get() );
3123  return m.release();
3124 }
3125 
3126 
3128 {
3130  other->setOpacity( mOpacity );
3131  other->setOffset( mOffset );
3132  other->setOffsetUnit( mOffsetUnit );
3134  other->setSizeUnit( mSizeUnit );
3138  copyDataDefinedProperties( other );
3139  copyPaintEffect( other );
3140 }
3141 
3143 {
3146 }
3147 
3149 {
3150  return QColor();
3151 }
3152 
3154 {
3156 }
3157 
3159 {
3161 }
3162 
3164 {
3165  bool hasDataDefinedSize = false;
3166  const double scaledSize = calculateSize( context, hasDataDefinedSize );
3167  const double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3168  bool hasDataDefinedAspectRatio = false;
3169  const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3170  const double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3171 
3172  //don't render symbols with size below one or above 10,000 pixels
3173  if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
3174  {
3175  return QRectF();
3176  }
3177 
3178  QPointF outputOffset;
3179  double angle = 0.0;
3180  calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3181 
3182  QTransform transform;
3183 
3184  // move to the desired position
3185  transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3186 
3187  if ( !qgsDoubleNear( angle, 0.0 ) )
3188  transform.rotate( angle );
3189 
3190  QRectF symbolBounds = transform.mapRect( QRectF( -width / 2.0,
3191  -height / 2.0,
3192  width,
3193  height ) );
3194 
3195  return symbolBounds;
3196 }
3197 
3199 
3200 QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QString chr, double pointSize, const QColor &color, double angle )
3201 {
3202  mFontFamily = fontFamily;
3203  mString = chr;
3204  mColor = color;
3205  mAngle = angle;
3206  mSize = pointSize;
3207  mOrigSize = pointSize;
3209  mOffset = QPointF( 0, 0 );
3211  mStrokeColor = DEFAULT_FONTMARKER_BORDERCOLOR;
3212  mStrokeWidth = 0.0;
3213  mStrokeWidthUnit = QgsUnitTypes::RenderMillimeters;
3214  mPenJoinStyle = DEFAULT_FONTMARKER_JOINSTYLE;
3215 }
3216 
3218 
3220 {
3222  QString string = DEFAULT_FONTMARKER_CHR;
3223  double pointSize = DEFAULT_FONTMARKER_SIZE;
3226 
3227  if ( props.contains( QStringLiteral( "font" ) ) )
3228  fontFamily = props[QStringLiteral( "font" )].toString();
3229  if ( props.contains( QStringLiteral( "chr" ) ) && props[QStringLiteral( "chr" )].toString().length() > 0 )
3230  string = props[QStringLiteral( "chr" )].toString();
3231  if ( props.contains( QStringLiteral( "size" ) ) )
3232  pointSize = props[QStringLiteral( "size" )].toDouble();
3233  if ( props.contains( QStringLiteral( "color" ) ) )
3234  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )].toString() );
3235  if ( props.contains( QStringLiteral( "angle" ) ) )
3236  angle = props[QStringLiteral( "angle" )].toDouble();
3237 
3238  QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( fontFamily, string, pointSize, color, angle );
3239 
3240  if ( props.contains( QStringLiteral( "font_style" ) ) )
3241  m->setFontStyle( props[QStringLiteral( "font_style" )].toString() );
3242  if ( props.contains( QStringLiteral( "outline_color" ) ) )
3243  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )].toString() ) );
3244  if ( props.contains( QStringLiteral( "outline_width" ) ) )
3245  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
3246  if ( props.contains( QStringLiteral( "offset" ) ) )
3247  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
3248  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
3249  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
3250  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3251  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3252  if ( props.contains( QStringLiteral( "size_unit" ) ) )
3253  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
3254  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3255  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3256  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
3257  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
3258  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
3259  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
3260  if ( props.contains( QStringLiteral( "joinstyle" ) ) )
3261  m->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() ) );
3262  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3263  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3264  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3265  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3266 
3267  m->restoreOldDataDefinedProperties( props );
3268 
3269  return m;
3270 }
3271 
3273 {
3274  return QStringLiteral( "FontMarker" );
3275 }
3276 
3278 {
3279  QColor brushColor = mColor;
3280  QColor penColor = mStrokeColor;
3281 
3282  brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
3283  penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
3284 
3285  mBrush = QBrush( brushColor );
3286  mPen = QPen( penColor );
3287  mPen.setJoinStyle( mPenJoinStyle );
3288  mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
3289 
3290  mFont = QFont( mFontFamily );
3291  if ( !mFontStyle.isEmpty() )
3292  {
3293  mFont.setStyleName( QgsFontUtils::translateNamedStyle( mFontStyle ) );
3294  }
3295 
3296  double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3297  mNonZeroFontSize = !qgsDoubleNear( sizePixels, 0.0 );
3298 
3299  if ( mNonZeroFontSize && sizePixels > MAX_FONT_CHARACTER_SIZE_IN_PIXELS )
3300  {
3301  // if font is too large (e.g using map units and map is very zoomed in), then we limit
3302  // the font size and instead scale up the painter.
3303  // this avoids issues with massive font sizes (eg https://github.com/qgis/QGIS/issues/42270)
3304  mFontSizeScale = sizePixels / MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3305  sizePixels = MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3306  }
3307  else
3308  mFontSizeScale = 1.0;
3309 
3310  // if a non zero, but small pixel size results, round up to 2 pixels so that a "dot" is at least visible
3311  // (if we set a <=1 pixel size here Qt will reset the font to a default size, leading to much too large symbols)
3312  mFont.setPixelSize( std::max( 2, static_cast< int >( std::round( sizePixels ) ) ) );
3313  mFontMetrics.reset( new QFontMetrics( mFont ) );
3314  mChrWidth = mFontMetrics->horizontalAdvance( mString );
3315  mChrOffset = QPointF( mChrWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3316  mOrigSize = mSize; // save in case the size would be data defined
3317 
3318  // use caching only when not using a data defined character
3322  if ( mUseCachedPath )
3323  {
3324  QPointF chrOffset = mChrOffset;
3325  double chrWidth;
3326  const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3327  mCachedPath = QPainterPath();
3328  mCachedPath.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3329  }
3330 }
3331 
3333 {
3334  Q_UNUSED( context )
3335 }
3336 
3337 QString QgsFontMarkerSymbolLayer::characterToRender( QgsSymbolRenderContext &context, QPointF &charOffset, double &charWidth )
3338 {
3339  charOffset = mChrOffset;
3340  QString stringToRender = mString;
3342  {
3343  context.setOriginalValueVariable( mString );
3345  if ( stringToRender != mString )
3346  {
3347  charWidth = mFontMetrics->horizontalAdvance( stringToRender );
3348  charOffset = QPointF( charWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3349  }
3350  }
3351  return stringToRender;
3352 }
3353 
3354 void QgsFontMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
3355  double scaledSize,
3356  bool &hasDataDefinedRotation,
3357  QPointF &offset,
3358  double &angle ) const
3359 {
3360  //offset
3361  double offsetX = 0;
3362  double offsetY = 0;
3363  markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
3364  offset = QPointF( offsetX, offsetY );
3365 
3366  //angle
3367  bool ok = true;
3368  angle = mAngle + mLineAngle;
3370  {
3371  context.setOriginalValueVariable( angle );
3373 
3374  // If the expression evaluation was not successful, fallback to static value
3375  if ( !ok )
3376  angle = mAngle + mLineAngle;
3377  }
3378 
3379  hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation;
3380  if ( hasDataDefinedRotation )
3381  {
3382  // For non-point markers, "dataDefinedRotation" means following the
3383  // shape (shape-data defined). For them, "field-data defined" does
3384  // not work at all. TODO: if "field-data defined" ever gets implemented
3385  // we'll need a way to distinguish here between the two, possibly
3386  // using another flag in renderHints()
3387  const QgsFeature *f = context.feature();
3388  if ( f )
3389  {
3390  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
3391  {
3392  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3393  angle += m2p.mapRotation();
3394  }
3395  }
3396  }
3397 
3398  if ( angle )
3400 }
3401 
3402 double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context )
3403 {
3404  double scaledSize = mSize;
3405  const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
3406 
3407  bool ok = true;
3408  if ( hasDataDefinedSize )
3409  {
3410  context.setOriginalValueVariable( mSize );
3412  }
3413 
3414  if ( hasDataDefinedSize && ok )
3415  {
3416  switch ( mScaleMethod )
3417  {
3419  scaledSize = std::sqrt( scaledSize );
3420  break;
3422  break;
3423  }
3424  }
3425  return scaledSize;
3426 }
3427 
3429 {
3430  QPainter *p = context.renderContext().painter();
3431  if ( !p || !mNonZeroFontSize )
3432  return;
3433 
3434  QTransform transform;
3435 
3436  bool ok;
3437  QColor brushColor = mColor;
3439  {
3442  }
3443  brushColor = context.selected() ? context.renderContext().selectionColor() : brushColor;
3444  if ( !context.selected() || !SELECTION_IS_OPAQUE )
3445  {
3446  brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
3447  }
3448  mBrush.setColor( brushColor );
3449 
3450  QColor penColor = mStrokeColor;
3452  {
3455  }
3456  penColor.setAlphaF( penColor.alphaF() * context.opacity() );
3457 
3458  double penWidth = context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3460  {
3461  context.setOriginalValueVariable( mStrokeWidth );
3463  if ( ok )
3464  {
3465  penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3466  }
3467  }
3468 
3470  {
3472  const QString style = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyJoinStyle, context.renderContext().expressionContext(), QString(), &ok );
3473  if ( ok )
3474  {
3475  mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
3476  }
3477  }
3478 
3479  const QgsScopedQPainterState painterState( p );
3480  p->setBrush( mBrush );
3481  if ( !qgsDoubleNear( penWidth, 0.0 ) )
3482  {
3483  mPen.setColor( penColor );
3484  mPen.setWidthF( penWidth );
3485  p->setPen( mPen );
3486  }
3487  else
3488  {
3489  p->setPen( Qt::NoPen );
3490  }
3491 
3493  {
3494  context.setOriginalValueVariable( mFontFamily );
3496  mFont.setFamily( ok ? fontFamily : mFontFamily );
3497  }
3499  {
3500  context.setOriginalValueVariable( mFontStyle );
3503  }
3505  {
3506  mFontMetrics.reset( new QFontMetrics( mFont ) );
3507  }
3508 
3509  QPointF chrOffset = mChrOffset;
3510  double chrWidth;
3511  const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3512 
3513  const double sizeToRender = calculateSize( context );
3514 
3515  bool hasDataDefinedRotation = false;
3516  QPointF offset;
3517  double angle = 0;
3518  calculateOffsetAndRotation( context, sizeToRender, hasDataDefinedRotation, offset, angle );
3519 
3520  p->translate( point.x() + offset.x(), point.y() + offset.y() );
3521 
3522  if ( !qgsDoubleNear( angle, 0.0 ) )
3523  transform.rotate( angle );
3524 
3525  if ( !qgsDoubleNear( sizeToRender, mOrigSize ) )
3526  {
3527  const double s = sizeToRender / mOrigSize;
3528  transform.scale( s, s );
3529  }
3530 
3531  if ( !qgsDoubleNear( mFontSizeScale, 1.0 ) )
3532  transform.scale( mFontSizeScale, mFontSizeScale );
3533 
3534  if ( mUseCachedPath )
3535  {
3536  p->drawPath( transform.map( mCachedPath ) );
3537  }
3538  else
3539  {
3540  QPainterPath path;
3541  path.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3542  p->drawPath( transform.map( path ) );
3543  }
3544 }
3545 
3547 {
3548  QVariantMap props;
3549  props[QStringLiteral( "font" )] = mFontFamily;
3550  props[QStringLiteral( "font_style" )] = mFontStyle;
3551  props[QStringLiteral( "chr" )] = mString;
3552  props[QStringLiteral( "size" )] = QString::number( mSize );
3553  props[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3554  props[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3555  props[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
3556  props[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
3557  props[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
3558  props[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
3559  props[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
3560  props[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
3561  props[QStringLiteral( "angle" )] = QString::number( mAngle );
3562  props[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3563  props[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3564  props[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3565  props[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3566  props[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3567  return props;
3568 }
3569 
3571 {
3572  QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( mFontFamily, mString, mSize, mColor, mAngle );
3573  m->setFontStyle( mFontStyle );
3574  m->setStrokeColor( mStrokeColor );
3575  m->setStrokeWidth( mStrokeWidth );
3576  m->setStrokeWidthUnit( mStrokeWidthUnit );
3577  m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
3578  m->setPenJoinStyle( mPenJoinStyle );
3579  m->setOffset( mOffset );
3580  m->setOffsetUnit( mOffsetUnit );
3582  m->setSizeUnit( mSizeUnit );
3587  copyPaintEffect( m );
3588  return m;
3589 }
3590 
3591 void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
3592 {
3593  // <Graphic>
3594  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
3595  element.appendChild( graphicElem );
3596 
3597  const QString fontPath = QStringLiteral( "ttf://%1" ).arg( mFontFamily );
3598  int markIndex = !mString.isEmpty() ? mString.at( 0 ).unicode() : 0;
3599  const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
3600  QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, QStringLiteral( "ttf" ), &markIndex, mColor, size );
3601 
3602  // <Rotation>
3603  QString angleFunc;
3604  bool ok;
3605  const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
3606  if ( !ok )
3607  {
3608  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
3609  }
3610  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
3611  {
3612  angleFunc = QString::number( angle + mAngle );
3613  }
3614  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
3615 
3616  // <Displacement>
3617  const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
3619 }
3620 
3622 {
3624  || mStrokeWidthUnit == QgsUnitTypes::RenderMapUnits || mStrokeWidthUnit == QgsUnitTypes::RenderMetersInMapUnits
3626 }
3627 
3629 {
3630  QPointF chrOffset = mChrOffset;
3631  double chrWidth = mChrWidth;
3632  //calculate width of rendered character
3633  ( void )characterToRender( context, chrOffset, chrWidth );
3634 
3635  if ( !mFontMetrics )
3636  mFontMetrics.reset( new QFontMetrics( mFont ) );
3637 
3638  double scaledSize = calculateSize( context );
3639  if ( !qgsDoubleNear( scaledSize, mOrigSize ) )
3640  {
3641  chrWidth *= scaledSize / mOrigSize;
3642  }
3643  chrWidth *= mFontSizeScale;
3644 
3645  bool hasDataDefinedRotation = false;
3646  QPointF offset;
3647  double angle = 0;
3648  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
3649  scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3650 
3651  QTransform transform;
3652 
3653  // move to the desired position
3654  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
3655 
3656  if ( !qgsDoubleNear( angle, 0.0 ) )
3657  transform.rotate( angle );
3658 
3659  QRectF symbolBounds = transform.mapRect( QRectF( -chrWidth / 2.0,
3660  -scaledSize / 2.0,
3661  chrWidth,
3662  scaledSize ) );
3663  return symbolBounds;
3664 }
3665 
3667 {
3668  QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
3669 
3670  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
3671  if ( graphicElem.isNull() )
3672  return nullptr;
3673 
3674  QString name, format;
3675  QColor color;
3676  double size;
3677  int chr;
3678 
3679  if ( !QgsSymbolLayerUtils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
3680  return nullptr;
3681 
3682  if ( !name.startsWith( QLatin1String( "ttf://" ) ) || format != QLatin1String( "ttf" ) )
3683  return nullptr;
3684 
3685  const QString fontFamily = name.mid( 6 );
3686 
3687  double angle = 0.0;
3688  QString angleFunc;
3689  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
3690  {
3691  bool ok;
3692  const double d = angleFunc.toDouble( &ok );
3693  if ( ok )
3694  angle = d;
3695  }
3696 
3697  QPointF offset;
3699 
3700  const QString uom = element.attribute( QStringLiteral( "uom" ) );
3704 
3706  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
3707  m->setAngle( angle );
3708  m->setOffset( offset );
3709  return m;
3710 }
3711 
3712 void QgsFontMarkerSymbolLayer::resolveFonts( const QVariantMap &properties, const QgsReadWriteContext &context )
3713 {
3714  const QString fontFamily = properties.value( QStringLiteral( "font" ), DEFAULT_FONTMARKER_FONT ).toString();
3716  {
3717  context.pushMessage( QObject::tr( "Font “%1” not available on system" ).arg( fontFamily ) );
3718  }
3719 }
3720 
3722 {
3723  QMap<QString, QgsProperty>::iterator it = mParameters.begin();
3724  for ( ; it != mParameters.end(); ++it )
3725  it.value().prepare( context.renderContext().expressionContext() );
3726 
3728 }
3729 
3730 
3731 QSet<QString> QgsSvgMarkerSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
3732 {
3733  QSet<QString> attrs = QgsMarkerSymbolLayer::usedAttributes( context );
3734 
3735  QMap<QString, QgsProperty>::const_iterator it = mParameters.constBegin();
3736  for ( ; it != mParameters.constEnd(); ++it )
3737  {
3738  attrs.unite( it.value().referencedFields( context.expressionContext(), true ) );
3739  }
3740 
3741  return attrs;
3742 }
3743 
3744 //
3745 // QgsAnimatedMarkerSymbolLayer
3746 //
3747 
3748 QgsAnimatedMarkerSymbolLayer::QgsAnimatedMarkerSymbolLayer( const QString &path, double size, double angle )
3749  : QgsRasterMarkerSymbolLayer( path, size, angle )
3750 {
3751 
3752 }
3753 
3755 
3756 QgsSymbolLayer *QgsAnimatedMarkerSymbolLayer::create( const QVariantMap &properties )
3757 {
3758  QString path;
3761 
3762  if ( properties.contains( QStringLiteral( "imageFile" ) ) )
3763  path = properties[QStringLiteral( "imageFile" )].toString();
3764  if ( properties.contains( QStringLiteral( "size" ) ) )
3765  size = properties[QStringLiteral( "size" )].toDouble();
3766  if ( properties.contains( QStringLiteral( "angle" ) ) )
3767  angle = properties[QStringLiteral( "angle" )].toDouble();
3768 
3769  std::unique_ptr< QgsAnimatedMarkerSymbolLayer > m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( path, size, angle );
3770  m->setFrameRate( properties.value( QStringLiteral( "frameRate" ), QStringLiteral( "10" ) ).toDouble() );
3771 
3772  m->setCommonProperties( properties );
3773  return m.release();
3774 }
3775 
3777 {
3778  return QStringLiteral( "AnimatedMarker" );
3779 }
3780 
3782 {
3783  QVariantMap res = QgsRasterMarkerSymbolLayer::properties();
3784  res.insert( QStringLiteral( "frameRate" ), mFrameRateFps );
3785  return res;
3786 }
3787 
3789 {
3790  std::unique_ptr< QgsAnimatedMarkerSymbolLayer > m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( mPath, mSize, mAngle );
3791  m->setFrameRate( mFrameRateFps );
3792  copyCommonProperties( m.get() );
3793  return m.release();
3794 }
3795 
3797 {
3799 
3800  mPreparedPaths.clear();
3802  {
3804  mStaticPath = true;
3805  }
3806  else
3807  {
3808  mStaticPath = false;
3809  }
3810 }
3811 
3812 QImage QgsAnimatedMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
3813 {
3814  if ( !mStaticPath && !mPreparedPaths.contains( path ) )
3815  {
3817  mPreparedPaths.insert( path );
3818  }
3819 
3820  const long long mapFrameNumber = context.currentFrame();
3821  const int totalFrameCount = QgsApplication::imageCache()->totalFrameCount( path, context.flags() & Qgis::RenderContextFlag::RenderBlocking );
3822  const double markerAnimationDuration = totalFrameCount / mFrameRateFps;
3823 
3824  double animationTimeSeconds = 0;
3825  if ( mapFrameNumber >= 0 && context.frameRate() > 0 )
3826  {
3827  // render is part of an animation, so we base the calculated frame on that
3828  animationTimeSeconds = mapFrameNumber / context.frameRate();
3829  }
3830  else
3831  {
3832  // render is outside of animation, so base the calculated frame on the current epoch
3833  animationTimeSeconds = QDateTime::currentMSecsSinceEpoch() / 1000.0;
3834  }
3835 
3836  const double markerAnimationProgressSeconds = std::fmod( animationTimeSeconds, markerAnimationDuration );
3837  const int movieFrame = static_cast< int >( std::floor( markerAnimationProgressSeconds * mFrameRateFps ) );
3838 
3839  bool cached = false;
3840  return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking, 96, movieFrame );
3841 }
3842 
@ DynamicRotation
Rotation of symbol may be changed during rendering and symbol should not be cached.
ScaleMethod
Scale methods.
Definition: qgis.h:219
@ ScaleDiameter
Calculate scale by the diameter.
@ ScaleArea
Calculate scale by the area.
MarkerShape
Marker shapes.
Definition: qgis.h:1206
@ Pentagon
Pentagon.
@ EquilateralTriangle
Equilateral triangle.
@ SemiCircle
Semi circle (top half)
@ QuarterCircle
Quarter circle (top left quarter)
@ LeftHalfTriangle
Left half of triangle.
@ ArrowHead
Right facing arrow head (unfilled, lines only)
@ AsteriskFill
A filled asterisk shape (since QGIS 3.18)
@ Octagon
Octagon (since QGIS 3.18)
@ HalfArc
A line-only half arc (since QGIS 3.20)
@ Line
Vertical line.
@ QuarterSquare
Quarter square (top left quarter)
@ Triangle
Triangle.
@ Cross2
Rotated cross (lines only), 'x' shape.
@ ArrowHeadFilled
Right facing filled arrow head.
@ HalfSquare
Half square (left half)
@ CrossFill
Solid filled cross.
@ RightHalfTriangle
Right half of triangle.
@ ThirdCircle
One third circle (top left third)
@ ThirdArc
A line-only one third arc (since QGIS 3.20)
@ SquareWithCorners
A square with diagonal corners (since QGIS 3.18)
@ QuarterArc
A line-only one quarter arc (since QGIS 3.20)
@ Cross
Cross (lines only)
@ DiagonalHalfSquare
Diagonal half square (bottom left half)
@ RenderingSubSymbol
Set whenever a sub-symbol of a parent symbol is currently being rendered. Can be used during symbol a...
@ RenderSymbolPreview
The render is for a symbol preview only and map based properties may not be available,...
@ RenderBlocking
Render and load remote sources in the same thread to ensure rendering remote sources (svg and images)...
@ Fill
Fill symbol.
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.
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.
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.
Animated marker symbol layer class.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsAnimatedMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
~QgsAnimatedMarkerSymbolLayer() override
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
QImage fetchImage(QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity) const override
Fetches the image to render.
QgsAnimatedMarkerSymbolLayer(const QString &path=QString(), double size=DEFAULT_RASTERMARKER_SIZE, double angle=DEFAULT_RASTERMARKER_ANGLE)
Constructor for animated marker symbol layer using the specified source image path.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates an animated marker symbol layer from a string map of properties.
QString layerType() const override
Returns a string that represents this layer type.
static QgsImageCache * imageCache()
Returns the application's image cache, used for caching resampled versions of raster images.
static QgsSvgCache * svgCache()
Returns the application's SVG cache, used for caching SVG images and handling parameter replacement w...
Exports QGIS layers to the DXF format.
Definition: qgsdxfexport.h:65
void writeFilledCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius)
Write filled circle (as hatch)
void writeCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius, const QString &lineStyleName, double width)
Write circle (as polyline)
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)
static double mapUnitScaleFactor(double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits, double mapUnitsPerPixel=1.0)
Returns scale factor for conversion to map units.
QgsUnitTypes::DistanceUnit mapUnits() const
Retrieve map units.
void writePolygon(const QgsRingSequence &polygon, const QString &layer, const QString &hatchPattern, const QColor &color)
Draw dxf filled polygon (HATCH)
double symbologyScale() const
Returns the reference scale for output.
Definition: qgsdxfexport.h:229
void clipValueToMapUnitScale(double &value, const QgsMapUnitScale &scale, double pixelToMMFactor) const
Clips value to scale minimum/maximum.
void writePolyline(const QgsPointSequence &line, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Draw dxf primitives (LWPOLYLINE)
A paint device for drawing into dxf files.
void setShift(QPointF shift)
void setLayer(const QString &layer)
void setOutputSize(const QRectF &r)
void setDrawingSize(QSizeF size)
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsGeometry geometry
Definition: qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:223
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
Definition: qgsfillsymbol.h:30
static QgsFillSymbol * createSimple(const QVariantMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties.
Filled marker symbol layer, consisting of a shape which is rendered using a QgsFillSymbol.
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QColor color() const override
Returns the "representative" color of the symbol layer.
QgsFilledMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QgsSymbol * subSymbol() override
Returns the symbol's sub symbol, if present.
~QgsFilledMarkerSymbolLayer() override
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer's subsymbol. takes ownership of the passed symbol.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
void setColor(const QColor &c) override
Sets the "representative" color for the symbol layer.
QString layerType() const override
Returns a string that represents this layer type.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsFilledMarkerSymbolLayer.
QgsFilledMarkerSymbolLayer(Qgis::MarkerShape shape=Qgis::MarkerShape::Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructor for QgsFilledMarkerSymbolLayer.
~QgsFontMarkerSymbolLayer() override
void setStrokeColor(const QColor &color) override
Sets the stroke color for the symbol layer.
QgsFontMarkerSymbolLayer(const QString &fontFamily=DEFAULT_FONTMARKER_FONT, QString chr=DEFAULT_FONTMARKER_CHR, double pointSize=DEFAULT_FONTMARKER_SIZE, const QColor &color=DEFAULT_FONTMARKER_COLOR, double angle=DEFAULT_FONTMARKER_ANGLE)
Constructs a font marker symbol layer.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
Sets the stroke width map unit scale.
double strokeWidth() const
Returns the marker's stroke width.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
void setFontStyle(const QString &style)
Sets the font style for the font which will be used to render the point.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QString fontStyle() const
Returns the font style for the associated font which will be used to render the point.
QString fontFamily() const
Returns the font family name for the associated font which will be used to render the point.
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit unit)
Sets the stroke width unit.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
void writeSldMarker(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Writes the symbol layer definition as a SLD XML element.
void setStrokeWidth(double width)
Set's the marker's stroke width.
static void resolveFonts(const QVariantMap &properties, const QgsReadWriteContext &context)
Resolves fonts from a properties map, raising warnings in the specified context if the required fonts...
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
void setPenJoinStyle(Qt::PenJoinStyle style)
Sets the stroke join style.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsFontMarkerSymbolLayer from an SLD XML element.
QgsFontMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
QString layerType() const override
Returns a string that represents this layer type.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsFontMarkerSymbolLayer from a property map (see properties())
static QString translateNamedStyle(const QString &namedStyle)
Returns the localized named style of a font, if such a translation is available.
static bool fontFamilyMatchOnSystem(const QString &family, QString *chosen=nullptr, bool *match=nullptr)
Check whether font family is on system.
static bool updateFontViaStyle(QFont &f, const QString &fontstyle, bool fallback=false)
Updates font with named style and retain all font properties.
QgsWkbTypes::GeometryType type
Definition: qgsgeometry.h:128
QSize originalSize(const QString &path, bool blocking=false) const
Returns the original size (in pixels) of the image at the specified path.
int totalFrameCount(const QString &path, bool blocking=false)
Returns the total frame count of the image at the specified path.
QImage pathAsImage(const QString &path, const QSize size, const bool keepAspectRatio, const double opacity, bool &fitsInCache, bool blocking=false, double targetDpi=96, int frameNumber=-1, bool *isMissing=nullptr)
Returns the specified path rendered as an image.
void prepareAnimation(const QString &path)
Prepares for optimized retrieval of frames for the animation at the given path.
static void adjustHueSaturation(QImage &image, double saturation, const QColor &colorizeColor=QColor(), double colorizeStrength=1.0, QgsFeedback *feedback=nullptr)
Alter the hue or saturation of a QImage.
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
double mapUnitsPerPixel() const
Returns the current map units per pixel.
double mapRotation() const
Returns the current map rotation in degrees (clockwise).
Struct for storing maximum and minimum scales for measurements in map units.
Abstract base class for marker symbol layers.
double mSize
Marker size.
QPointF offset() const
Returns the marker's offset, which is the horizontal and vertical displacement which the rendered mar...
double mLineAngle
Line rotation angle (see setLineAngle() for details)
HorizontalAnchorPoint
Symbol horizontal anchor points.
void setAngle(double angle)
Sets the rotation angle for the marker.
Qgis::ScaleMethod scaleMethod() const
Returns the method to use for scaling the marker's size.
QgsUnitTypes::RenderUnit mOffsetUnit
Offset units.
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol's size.
void setVerticalAnchorPoint(VerticalAnchorPoint v)
Sets the vertical anchor point for positioning the symbol.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QPointF mOffset
Marker offset.
void setHorizontalAnchorPoint(HorizontalAnchorPoint h)
Sets the horizontal anchor point for positioning the symbol.
QgsMapUnitScale mapUnitScale() const override
void setOffset(QPointF offset)
Sets the marker's offset, which is the horizontal and vertical displacement which the rendered marker...
void setSizeMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol's size.
double size() const
Returns the symbol size.
QgsMapUnitScale mOffsetMapUnitScale
Offset map unit scale.
HorizontalAnchorPoint mHorizontalAnchorPoint
Horizontal anchor point.
static QPointF _rotatedOffset(QPointF offset, double angle)
Adjusts a marker offset to account for rotation.
Qgis::ScaleMethod mScaleMethod
Marker size scaling method.
QgsMapUnitScale mSizeMapUnitScale
Marker size map unit scale.
VerticalAnchorPoint
Symbol vertical anchor points.
void markerOffset(QgsSymbolRenderContext &context, double &offsetX, double &offsetY) const
Calculates the required marker offset, including both the symbol offset and any displacement required...
VerticalAnchorPoint mVerticalAnchorPoint
Vertical anchor point.
QgsUnitTypes::RenderUnit mSizeUnit
Marker size unit.
void setOffsetUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol's offset.
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol's offset.
double mAngle
Marker rotation angle, in degrees clockwise from north.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
double angle() const
Returns the rotation angle for the marker, in degrees clockwise from north.
void setMapUnitScale(const QgsMapUnitScale &scale) override
Resolves relative paths into absolute paths and vice versa.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
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.
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
static QVariantMap propertyMapToVariantMap(const QMap< QString, QgsProperty > &propertyMap)
Convert a map of QgsProperty to a map of QVariant This is useful to save a map of properties.
static QMap< QString, QgsProperty > variantMapToPropertyMap(const QVariantMap &variantMap)
Convert a map of QVariant to a map of QgsProperty This is useful to restore a map of properties.
Raster marker symbol layer class.
double mFixedAspectRatio
The marker fixed aspect ratio.
QColor color() const override
Returns the "representative" color of the symbol layer.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsMapUnitScale mapUnitScale() const override
void copyCommonProperties(QgsRasterMarkerSymbolLayer *other) const
Copies common properties to another layer.
void setOpacity(double opacity)
Set the marker opacity.
QString path() const
Returns the marker raster image path.
double calculateAspectRatio(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio) const
Calculates the marker aspect ratio between width and height.
QgsRasterMarkerSymbolLayer(const QString &path=QString(), double size=DEFAULT_SVGMARKER_SIZE, double angle=DEFAULT_SVGMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructs raster marker symbol layer with picture from given absolute path to a raster image file.
void setPath(const QString &path)
Set the marker raster image path.
virtual QImage fetchImage(QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity) const
Fetches the image to render.
double defaultAspectRatio() const
Returns the default marker aspect ratio between width and height, 0 if not yet calculated.
void setFixedAspectRatio(double ratio)
Set the marker aspect ratio between width and height to be used in rendering, if the value set is low...
void setMapUnitScale(const QgsMapUnitScale &scale) override
void setCommonProperties(const QVariantMap &properties)
Sets common class properties from a properties map.
QgsRasterMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
bool preservedAspectRatio() const
Returns the preserved aspect ratio value, true if fixed aspect ratio has been lower or equal to 0.
static void resolvePaths(QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving)
Turns relative paths in properties map to absolute when reading and vice versa when writing.
~QgsRasterMarkerSymbolLayer() override
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a raster marker symbol layer from a string map of properties.
double mOpacity
The marker default opacity.
double updateDefaultAspectRatio()
Calculates the default marker aspect ratio between width and height.
double mDefaultAspectRatio
The marker default aspect ratio.
bool setPreservedAspectRatio(bool par)
Set preserved the marker aspect ratio between width and height.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QString layerType() const override
Returns a string that represents this layer type.
double opacity() const
Returns the marker opacity.
The class is used as a container of context for various read/write operations on other objects.
void pushMessage(const QString &message, Qgis::MessageLevel level=Qgis::MessageLevel::Warning) const
Append a message to the context.
Contains information about the context of a rendering operation.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
QPainter * painter()
Returns the destination QPainter for the render operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
bool forceVectorOutput() const
Returns true if rendering operations should use vector operations instead of any faster raster shortc...
long long currentFrame() const
Returns the current frame number of the map (in frames per second), for maps which are part of an ani...
void setFlag(Qgis::RenderContextFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
double frameRate() const
Returns the frame rate of the map, for maps which are part of an animation.
QColor selectionColor() const
Returns the color to use when rendering selected features.
QgsFeedback * feedback() const
Returns the feedback object that can be queried regularly during rendering to check if rendering shou...
Qgis::RenderContextFlags flags() const
Returns combination of flags used for rendering.
const QgsPathResolver & pathResolver() const
Returns the path resolver for conversion between relative and absolute paths during rendering operati...
Scoped object for saving and restoring a QPainter object's state.
Abstract base class for simple marker symbol layers.
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
void calculateOffsetAndRotation(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle) const
Calculates the marker offset and rotation.
Qgis::MarkerShape mShape
Symbol shape.
QPainterPath mPath
Painter path representing shape. If mPolygon is empty then the shape is stored in mPath.
bool shapeToPolygon(Qgis::MarkerShape shape, QPolygonF &polygon) const
Creates a polygon representing the specified shape.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
static QList< Qgis::MarkerShape > availableShapes()
Returns a list of all available shape types.
~QgsSimpleMarkerSymbolLayerBase() override
static bool shapeIsFilled(Qgis::MarkerShape shape)
Returns true if a symbol shape has a fill.
QPolygonF mPolygon
Polygon of points in shape. If polygon is empty then shape is using mPath.
Qgis::MarkerShape shape() const
Returns the shape for the rendered marker symbol.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
QgsSimpleMarkerSymbolLayerBase(Qgis::MarkerShape shape=Qgis::MarkerShape::Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructor for QgsSimpleMarkerSymbolLayerBase.
static QString encodeShape(Qgis::MarkerShape shape)
Encodes a shape to its string representation.
double calculateSize(QgsSymbolRenderContext &context, bool &hasDataDefinedSize) const
Calculates the desired size of the marker, considering data defined size overrides.
static Qgis::MarkerShape decodeShape(const QString &name, bool *ok=nullptr)
Attempts to decode a string representation of a shape name to the corresponding shape.
bool prepareMarkerPath(Qgis::MarkerShape symbol)
Prepares the layer for drawing the specified shape (QPainterPath version)
bool prepareMarkerShape(Qgis::MarkerShape shape)
Prepares the layer for drawing the specified shape (QPolygonF version)
Simple marker symbol layer, consisting of a rendered shape with solid fill color and an stroke.
QPen mSelPen
QPen to use as stroke of selected symbols.
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit u)
Sets the unit for the width of the marker's stroke.
void setColor(const QColor &color) override
Sets the "representative" color for the symbol layer.
QColor mStrokeColor
Stroke color.
QImage mSelCache
Cached image of selected marker, if using cached version.
QImage mCache
Cached image of marker, if using cached version.
QBrush mSelBrush
QBrush to use as fill of selected symbols.
void setFillColor(const QColor &color) override
Sets the fill color for the symbol layer.
Qt::PenJoinStyle penJoinStyle() const
Returns the marker's stroke join style (e.g., miter, bevel, etc).
QgsUnitTypes::RenderUnit outputUnit() const override
Returns 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.
QPen mPen
QPen corresponding to marker's stroke style.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsSimpleMarkerSymbolLayer.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
void setMapUnitScale(const QgsMapUnitScale &scale) override
QColor color() const override
Returns the "representative" color of the symbol layer.
QgsMapUnitScale mapUnitScale() const override
Qt::PenStyle mStrokeStyle
Stroke style.
QgsSimpleMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsSimpleMarkerSymbolLayer from an SLD XML element.
~QgsSimpleMarkerSymbolLayer() override
Qt::PenCapStyle mPenCapStyle
Stroke pen cap style.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QString layerType() const override
Returns a string that represents this layer type.
double mStrokeWidth
Stroke width.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
Sets the map scale for the width of the marker's stroke.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
void setStrokeStyle(Qt::PenStyle strokeStyle)
Sets the marker's stroke style (e.g., solid, dashed, etc)
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QColor fillColor() const override
Returns the fill color for the symbol layer.
QColor strokeColor() const override
Returns the marker's stroke color.
QBrush mBrush
QBrush corresponding to marker's fill style.
void setStrokeWidth(double w)
Sets the width of the marker's stroke.
void setStrokeColor(const QColor &color) override
Sets the marker's stroke color.
Qt::PenStyle strokeStyle() const
Returns the marker's stroke style (e.g., solid, dashed, etc)
QgsUnitTypes::RenderUnit mStrokeWidthUnit
Stroke width units.
bool mUsingCache
true if using cached images of markers for drawing.
void setPenCapStyle(Qt::PenCapStyle style)
Sets the marker's stroke cap style (e.g., flat, round, etc).
bool writeDxf(QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift=QPointF(0.0, 0.0)) const override
write as DXF
void writeSldMarker(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Writes the symbol layer definition as a SLD XML element.
static const int MAXIMUM_CACHE_WIDTH
Maximum width/height of cache image.
QString ogrFeatureStyle(double mmScaleFactor, double mapUnitScaleFactor) const override
bool prepareCache(QgsSymbolRenderContext &context)
Prepares cache image.
QgsMapUnitScale mStrokeWidthMapUnitScale
Stroke width map unit scale.
QgsSimpleMarkerSymbolLayer(Qgis::MarkerShape shape=Qgis::MarkerShape::Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, Qgis::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.
double strokeWidth() const
Returns the width of the marker's stroke.
Qt::PenJoinStyle mPenJoinStyle
Stroke pen join style.
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
QSizeF svgViewboxSize(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >())
Calculates the viewbox size of a (possibly cached) SVG file.
QPicture svgAsPicture(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool forceVectorOutput=false, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >())
Returns an SVG drawing as a QPicture.
void containsParams(const QString &path, bool &hasFillParam, QColor &defaultFillColor, bool &hasStrokeParam, QColor &defaultStrokeColor, bool &hasStrokeWidthParam, double &defaultStrokeWidth, bool blocking=false) const
Tests if an SVG file contains parameters for fill, stroke color, stroke width.
QImage svgAsImage(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool &fitsInCache, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >())
Returns an SVG drawing as a QImage.
QByteArray svgContent(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >(), bool *isMissingImage=nullptr)
Gets the SVG content corresponding to the given path.
QgsSvgMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
QColor fillColor() const override
Returns the fill color for the symbol layer.
QgsMapUnitScale mapUnitScale() const override
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates the symbol.
double mDefaultAspectRatio
The marker default aspect ratio.
QgsUnitTypes::RenderUnit mStrokeWidthUnit
QString layerType() const override
Returns a string that represents this layer type.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QString path() const
Returns the marker SVG path.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
bool preservedAspectRatio() const
Returns the preserved aspect ratio value, true if fixed aspect ratio has been lower or equal to 0.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
void prepareExpressions(const QgsSymbolRenderContext &context) override
Prepares all data defined property expressions for evaluation.
QMap< QString, QgsProperty > mParameters
bool setPreservedAspectRatio(bool par)
Set preserved the marker aspect ratio between width and height.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the stroke width.
double calculateAspectRatio(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio) const
Calculates the marker aspect ratio between width and height.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
static QgsSymbolLayer * createFromSld(QDomElement &element)
void setStrokeColor(const QColor &c) override
Sets the stroke color for the symbol layer.
void setMapUnitScale(const QgsMapUnitScale &scale) override
double updateDefaultAspectRatio()
Calculates the default marker aspect ratio between width and height.
QColor strokeColor() const override
Returns the stroke color for the symbol layer.
void setFillColor(const QColor &color) override
Sets the fill color for the symbol layer.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
QMap< QString, QgsProperty > parameters() const
Returns the dynamic SVG parameters.
QgsSvgMarkerSymbolLayer(const QString &path, double size=DEFAULT_SVGMARKER_SIZE, double angle=DEFAULT_SVGMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructs SVG marker symbol layer with picture from given absolute path to a SVG file.
void writeSldMarker(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Writes the symbol layer definition as a SLD XML element.
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
~QgsSvgMarkerSymbolLayer() override
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
static void resolvePaths(QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving)
Turns relative paths in properties map to absolute when reading and vice versa when writing.
QgsMapUnitScale mStrokeWidthMapUnitScale
void setParameters(const QMap< QString, QgsProperty > &parameters)
Sets the dynamic SVG parameters.
double mFixedAspectRatio
The marker fixed 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
void setFixedAspectRatio(double ratio)
Set the marker aspect ratio between width and height to be used in rendering, if the value set is low...
void setPath(const QString &path)
Set the marker SVG path.
static bool externalMarkerFromSld(QDomElement &element, QString &path, QString &format, int &markIndex, QColor &color, double &size)
static bool rotationFromSldElement(QDomElement &element, QString &rotationFunc)
static QString encodePenStyle(Qt::PenStyle style)
static Qt::PenJoinStyle decodePenJoinStyle(const QString &str)
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
static QgsStringMap evaluatePropertiesMap(const QMap< QString, QgsProperty > &propertiesMap, const QgsExpressionContext &context)
Evaluates a map of properties using the given context and returns a variant map with evaluated expres...
static bool displacementFromSldElement(QDomElement &element, QPointF &offset)
static QColor decodeColor(const QString &str)
static QString svgSymbolPathToName(const QString &path, const QgsPathResolver &pathResolver)
Determines an SVG symbol's name from its path.
static QPointF toPoint(const QVariant &value, bool *ok=nullptr)
Converts a value to a point.
static double rescaleUom(double size, QgsUnitTypes::RenderUnit unit, const QVariantMap &props)
Rescales the given size based on the uomScale found in the props, if any is found,...
static void multiplyImageOpacity(QImage *image, qreal opacity)
Multiplies opacity of image pixel values with a (global) transparency value.
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
static Qt::PenCapStyle decodePenCapStyle(const QString &str)
static bool externalGraphicFromSld(QDomElement &element, QString &path, QString &mime, QColor &color, double &size)
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...
static Qgis::ScaleMethod decodeScaleMethod(const QString &str)
Decodes a symbol scale method from a string.
static QString encodePenCapStyle(Qt::PenCapStyle style)
static void externalMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &path, const QString &format, int *markIndex=nullptr, const QColor &color=QColor(), double size=-1)
static double sizeInPixelsFromSldUom(const QString &uom, double size)
Returns the size scaled in pixels according to the uom attribute.
static bool wellKnownMarkerFromSld(QDomElement &element, QString &name, QColor &color, QColor &strokeColor, Qt::PenStyle &strokeStyle, double &strokeWidth, double &size)
static void createDisplacementElement(QDomDocument &doc, QDomElement &element, QPointF offset)
static QString svgSymbolNameToPath(const QString &name, const QgsPathResolver &pathResolver)
Determines an SVG symbol's path from its name.
static QString encodeColor(const QColor &color)
static double estimateMaxSymbolBleed(QgsSymbol *symbol, const QgsRenderContext &context)
Returns the maximum estimated bleed for the symbol.
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)
static QString encodeScaleMethod(Qgis::ScaleMethod scaleMethod)
Encodes a symbol scale method to a string.
static Qt::PenStyle decodePenStyle(const QString &str)
static void createRotationElement(QDomDocument &doc, QDomElement &element, const QString &rotationFunc)
static QString encodePoint(QPointF point)
Encodes a QPointF to a string.
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
static QPointF decodePoint(const QString &string)
Decodes a QSizeF from a string.
@ PropertyStrokeStyle
Stroke style (eg solid, dashed)
@ PropertyCapStyle
Line cap style.
@ PropertyAngle
Symbol angle.
@ PropertySize
Symbol size.
@ PropertyJoinStyle
Line join style.
@ PropertyOpacity
Opacity.
@ PropertyCharacter
Character, eg for font marker symbol layers.
@ PropertyOffset
Symbol offset.
@ PropertyStrokeWidth
Stroke width.
@ PropertyFillColor
Fill color.
@ PropertyFontStyle
Font style.
@ PropertyHeight
Symbol height.
@ PropertyFontFamily
Font family.
@ PropertyName
Name, eg shape name for simple markers.
@ PropertyStrokeColor
Stroke color.
@ PropertyWidth
Symbol width.
static const bool SELECTION_IS_OPAQUE
Whether styles for selected features ignore symbol alpha.
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns the set of attributes referenced by the layer.
void copyDataDefinedProperties(QgsSymbolLayer *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
void restoreOldDataDefinedProperties(const QVariantMap &stringMap)
Restores older data defined properties from string map.
virtual void prepareExpressions(const QgsSymbolRenderContext &context)
Prepares all data defined property expressions for evaluation.
virtual void setColor(const QColor &color)
Sets the "representative" color for the symbol layer.
virtual QColor color() const
Returns the "representative" color of the symbol layer.
void copyPaintEffect(QgsSymbolLayer *destLayer) const
Copies paint effect of this layer to another symbol layer.
QgsPropertyCollection mDataDefinedProperties
virtual bool hasDataDefinedProperties() const
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
Definition: qgssymbollayer.cpp