QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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 
2806 
2807  if ( props.contains( QStringLiteral( "alpha" ) ) )
2808  {
2809  m->setOpacity( props[QStringLiteral( "alpha" )].toDouble() );
2810  }
2811 
2812  if ( props.contains( QStringLiteral( "size_unit" ) ) )
2813  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
2814  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
2815  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
2816  if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
2817  m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
2818 
2819  if ( props.contains( QStringLiteral( "offset" ) ) )
2820  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
2821  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
2822  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
2823  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2824  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
2825 
2826  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2827  {
2828  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2829  }
2830  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2831  {
2832  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2833  }
2834 
2835  m->restoreOldDataDefinedProperties( props );
2837 
2838  return m;
2839 }
2840 
2841 void QgsRasterMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
2842 {
2843  const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
2844  if ( it != properties.end() && it.value().type() == QVariant::String )
2845  {
2846  if ( saving )
2847  it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
2848  else
2849  it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
2850  }
2851 }
2852 
2853 void QgsRasterMarkerSymbolLayer::setPath( const QString &path )
2854 {
2855  mPath = path;
2857 }
2858 
2860 {
2861  const bool aPreservedAspectRatio = preservedAspectRatio();
2862  if ( aPreservedAspectRatio && !par )
2863  {
2865  }
2866  else if ( !aPreservedAspectRatio && par )
2867  {
2868  mFixedAspectRatio = 0.0;
2869  }
2870  return preservedAspectRatio();
2871 }
2872 
2874 {
2875  if ( mDefaultAspectRatio == 0.0 )
2876  {
2877  const QSize size = QgsApplication::imageCache()->originalSize( mPath );
2878  mDefaultAspectRatio = ( !size.isNull() && size.isValid() && size.width() > 0 ) ? static_cast< double >( size.height() ) / static_cast< double >( size.width() ) : 0.0;
2879  }
2880  return mDefaultAspectRatio;
2881 }
2882 
2884 {
2885  return QStringLiteral( "RasterMarker" );
2886 }
2887 
2889 {
2890  QPainter *p = context.renderContext().painter();
2891  if ( !p )
2892  return;
2893 
2894  QString path = mPath;
2896  {
2897  context.setOriginalValueVariable( mPath );
2899  }
2900 
2901  if ( path.isEmpty() )
2902  return;
2903 
2904  double width = 0.0;
2905  double height = 0.0;
2906 
2907  bool hasDataDefinedSize = false;
2908  const double scaledSize = calculateSize( context, hasDataDefinedSize );
2909 
2910  bool hasDataDefinedAspectRatio = false;
2911  const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
2912 
2913  QPointF outputOffset;
2914  double angle = 0.0;
2915 
2916  // RenderPercentage Unit Type takes original image size
2918  {
2919  const QSize size = QgsApplication::imageCache()->originalSize( path );
2920  if ( size.isEmpty() )
2921  return;
2922 
2923  width = ( scaledSize * static_cast< double >( size.width() ) ) / 100.0;
2924  height = ( scaledSize * static_cast< double >( size.height() ) ) / 100.0;
2925 
2926  // don't render symbols with size below one or above 10,000 pixels
2927  if ( static_cast< int >( width ) < 1 || 10000.0 < width || static_cast< int >( height ) < 1 || 10000.0 < height )
2928  return;
2929 
2930  calculateOffsetAndRotation( context, width, height, outputOffset, angle );
2931  }
2932  else
2933  {
2934  width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
2935  height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
2936 
2937  if ( preservedAspectRatio() && path != mPath )
2938  {
2939  const QSize size = QgsApplication::imageCache()->originalSize( path );
2940  if ( !size.isNull() && size.isValid() && size.width() > 0 )
2941  {
2942  height = width * ( static_cast< double >( size.height() ) / static_cast< double >( size.width() ) );
2943  }
2944  }
2945 
2946  // don't render symbols with size below one or above 10,000 pixels
2947  if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2948  return;
2949 
2950  calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
2951  }
2952 
2953  const QgsScopedQPainterState painterState( p );
2954  p->translate( point + outputOffset );
2955 
2956  const bool rotated = !qgsDoubleNear( angle, 0 );
2957  if ( rotated )
2958  p->rotate( angle );
2959 
2960  double opacity = mOpacity;
2962  {
2965  }
2966  opacity *= context.opacity();
2967 
2968  bool cached;
2969  QImage img = QgsApplication::imageCache()->pathAsImage( path, QSize( width, preservedAspectRatio() ? 0 : width * aspectRatio ), preservedAspectRatio(), opacity, cached, ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ) );
2970  if ( !img.isNull() )
2971  {
2972  if ( context.selected() )
2973  {
2975  }
2976 
2977  p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2978  }
2979 }
2980 
2981 double QgsRasterMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2982 {
2983  double scaledSize = mSize;
2985 
2986  bool ok = true;
2987  if ( hasDataDefinedSize )
2988  {
2989  context.setOriginalValueVariable( mSize );
2991  }
2992  else
2993  {
2995  if ( hasDataDefinedSize )
2996  {
2997  context.setOriginalValueVariable( mSize );
2999  }
3000  }
3001 
3002  if ( hasDataDefinedSize && ok )
3003  {
3004  switch ( mScaleMethod )
3005  {
3007  scaledSize = std::sqrt( scaledSize );
3008  break;
3010  break;
3011  }
3012  }
3013 
3014  return scaledSize;
3015 }
3016 
3017 double QgsRasterMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
3018 {
3020  if ( !hasDataDefinedAspectRatio )
3021  return mFixedAspectRatio;
3022 
3024  return 0.0;
3025 
3026  double scaledAspectRatio = mDefaultAspectRatio;
3027  if ( mFixedAspectRatio > 0.0 )
3028  scaledAspectRatio = mFixedAspectRatio;
3029 
3030  const double defaultHeight = mSize * scaledAspectRatio;
3031  scaledAspectRatio = defaultHeight / scaledSize;
3032 
3033  bool ok = true;
3034  double scaledHeight = scaledSize * scaledAspectRatio;
3036  {
3037  context.setOriginalValueVariable( defaultHeight );
3038  scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::PropertyHeight, context.renderContext().expressionContext(), defaultHeight, &ok );
3039  }
3040 
3041  if ( hasDataDefinedAspectRatio && ok )
3042  {
3043  switch ( mScaleMethod )
3044  {
3046  scaledHeight = sqrt( scaledHeight );
3047  break;
3049  break;
3050  }
3051  }
3052 
3053  scaledAspectRatio = scaledHeight / scaledSize;
3054 
3055  return scaledAspectRatio;
3056 }
3057 
3058 void QgsRasterMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
3059 {
3060  //offset
3061  double offsetX = 0;
3062  double offsetY = 0;
3063  markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
3064  offset = QPointF( offsetX, offsetY );
3065 
3066  angle = mAngle + mLineAngle;
3068  {
3069  context.setOriginalValueVariable( mAngle );
3071  }
3072 
3074  if ( hasDataDefinedRotation )
3075  {
3076  const QgsFeature *f = context.feature();
3077  if ( f )
3078  {
3079  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
3080  {
3081  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3082  angle += m2p.mapRotation();
3083  }
3084  }
3085  }
3086 
3087  if ( angle )
3089 }
3090 
3091 
3093 {
3094  QVariantMap map;
3095  map[QStringLiteral( "imageFile" )] = mPath;
3096  map[QStringLiteral( "size" )] = QString::number( mSize );
3097  map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3098  map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3099  map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
3100  map[QStringLiteral( "angle" )] = QString::number( mAngle );
3101  map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
3102  map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3103  map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3104  map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3105  map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
3106  map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3107  map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3108  return map;
3109 }
3110 
3112 {
3115  m->setOpacity( mOpacity );
3116  m->setOffset( mOffset );
3117  m->setOffsetUnit( mOffsetUnit );
3119  m->setSizeUnit( mSizeUnit );
3124  copyPaintEffect( m );
3125  return m;
3126 }
3127 
3129 {
3132 }
3133 
3135 {
3136  return QColor();
3137 }
3138 
3140 {
3142 }
3143 
3145 {
3147 }
3148 
3150 {
3151  bool hasDataDefinedSize = false;
3152  const double scaledSize = calculateSize( context, hasDataDefinedSize );
3153  const double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3154  bool hasDataDefinedAspectRatio = false;
3155  const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3156  const double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3157 
3158  //don't render symbols with size below one or above 10,000 pixels
3159  if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
3160  {
3161  return QRectF();
3162  }
3163 
3164  QPointF outputOffset;
3165  double angle = 0.0;
3166  calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3167 
3168  QTransform transform;
3169 
3170  // move to the desired position
3171  transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3172 
3173  if ( !qgsDoubleNear( angle, 0.0 ) )
3174  transform.rotate( angle );
3175 
3176  QRectF symbolBounds = transform.mapRect( QRectF( -width / 2.0,
3177  -height / 2.0,
3178  width,
3179  height ) );
3180 
3181  return symbolBounds;
3182 }
3183 
3185 
3186 QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QString chr, double pointSize, const QColor &color, double angle )
3187 {
3188  mFontFamily = fontFamily;
3189  mString = chr;
3190  mColor = color;
3191  mAngle = angle;
3192  mSize = pointSize;
3193  mOrigSize = pointSize;
3195  mOffset = QPointF( 0, 0 );
3197  mStrokeColor = DEFAULT_FONTMARKER_BORDERCOLOR;
3198  mStrokeWidth = 0.0;
3199  mStrokeWidthUnit = QgsUnitTypes::RenderMillimeters;
3200  mPenJoinStyle = DEFAULT_FONTMARKER_JOINSTYLE;
3201 }
3202 
3204 
3206 {
3208  QString string = DEFAULT_FONTMARKER_CHR;
3209  double pointSize = DEFAULT_FONTMARKER_SIZE;
3212 
3213  if ( props.contains( QStringLiteral( "font" ) ) )
3214  fontFamily = props[QStringLiteral( "font" )].toString();
3215  if ( props.contains( QStringLiteral( "chr" ) ) && props[QStringLiteral( "chr" )].toString().length() > 0 )
3216  string = props[QStringLiteral( "chr" )].toString();
3217  if ( props.contains( QStringLiteral( "size" ) ) )
3218  pointSize = props[QStringLiteral( "size" )].toDouble();
3219  if ( props.contains( QStringLiteral( "color" ) ) )
3220  color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )].toString() );
3221  if ( props.contains( QStringLiteral( "angle" ) ) )
3222  angle = props[QStringLiteral( "angle" )].toDouble();
3223 
3224  QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( fontFamily, string, pointSize, color, angle );
3225 
3226  if ( props.contains( QStringLiteral( "font_style" ) ) )
3227  m->setFontStyle( props[QStringLiteral( "font_style" )].toString() );
3228  if ( props.contains( QStringLiteral( "outline_color" ) ) )
3229  m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )].toString() ) );
3230  if ( props.contains( QStringLiteral( "outline_width" ) ) )
3231  m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
3232  if ( props.contains( QStringLiteral( "offset" ) ) )
3233  m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
3234  if ( props.contains( QStringLiteral( "offset_unit" ) ) )
3235  m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
3236  if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3237  m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3238  if ( props.contains( QStringLiteral( "size_unit" ) ) )
3239  m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
3240  if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3241  m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3242  if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
3243  m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
3244  if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
3245  m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
3246  if ( props.contains( QStringLiteral( "joinstyle" ) ) )
3247  m->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() ) );
3248  if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3249  m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3250  if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3251  m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3252 
3253  m->restoreOldDataDefinedProperties( props );
3254 
3255  return m;
3256 }
3257 
3259 {
3260  return QStringLiteral( "FontMarker" );
3261 }
3262 
3264 {
3265  QColor brushColor = mColor;
3266  QColor penColor = mStrokeColor;
3267 
3268  brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
3269  penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
3270 
3271  mBrush = QBrush( brushColor );
3272  mPen = QPen( penColor );
3273  mPen.setJoinStyle( mPenJoinStyle );
3274  mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
3275 
3276  mFont = QFont( mFontFamily );
3277  if ( !mFontStyle.isEmpty() )
3278  {
3279  mFont.setStyleName( QgsFontUtils::translateNamedStyle( mFontStyle ) );
3280  }
3281 
3282  double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3283  mNonZeroFontSize = !qgsDoubleNear( sizePixels, 0.0 );
3284 
3285  if ( mNonZeroFontSize && sizePixels > MAX_FONT_CHARACTER_SIZE_IN_PIXELS )
3286  {
3287  // if font is too large (e.g using map units and map is very zoomed in), then we limit
3288  // the font size and instead scale up the painter.
3289  // this avoids issues with massive font sizes (eg https://github.com/qgis/QGIS/issues/42270)
3290  mFontSizeScale = sizePixels / MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3291  sizePixels = MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3292  }
3293  else
3294  mFontSizeScale = 1.0;
3295 
3296  // if a non zero, but small pixel size results, round up to 2 pixels so that a "dot" is at least visible
3297  // (if we set a <=1 pixel size here Qt will reset the font to a default size, leading to much too large symbols)
3298  mFont.setPixelSize( std::max( 2, static_cast< int >( std::round( sizePixels ) ) ) );
3299  mFontMetrics.reset( new QFontMetrics( mFont ) );
3300  mChrWidth = mFontMetrics->horizontalAdvance( mString );
3301  mChrOffset = QPointF( mChrWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3302  mOrigSize = mSize; // save in case the size would be data defined
3303 
3304  // use caching only when not using a data defined character
3308  if ( mUseCachedPath )
3309  {
3310  QPointF chrOffset = mChrOffset;
3311  double chrWidth;
3312  const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3313  mCachedPath = QPainterPath();
3314  mCachedPath.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3315  }
3316 }
3317 
3319 {
3320  Q_UNUSED( context )
3321 }
3322 
3323 QString QgsFontMarkerSymbolLayer::characterToRender( QgsSymbolRenderContext &context, QPointF &charOffset, double &charWidth )
3324 {
3325  charOffset = mChrOffset;
3326  QString stringToRender = mString;
3328  {
3329  context.setOriginalValueVariable( mString );
3331  if ( stringToRender != mString )
3332  {
3333  charWidth = mFontMetrics->horizontalAdvance( stringToRender );
3334  charOffset = QPointF( charWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3335  }
3336  }
3337  return stringToRender;
3338 }
3339 
3340 void QgsFontMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
3341  double scaledSize,
3342  bool &hasDataDefinedRotation,
3343  QPointF &offset,
3344  double &angle ) const
3345 {
3346  //offset
3347  double offsetX = 0;
3348  double offsetY = 0;
3349  markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
3350  offset = QPointF( offsetX, offsetY );
3351 
3352  //angle
3353  bool ok = true;
3354  angle = mAngle + mLineAngle;
3356  {
3357  context.setOriginalValueVariable( angle );
3359 
3360  // If the expression evaluation was not successful, fallback to static value
3361  if ( !ok )
3362  angle = mAngle + mLineAngle;
3363  }
3364 
3365  hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation;
3366  if ( hasDataDefinedRotation )
3367  {
3368  // For non-point markers, "dataDefinedRotation" means following the
3369  // shape (shape-data defined). For them, "field-data defined" does
3370  // not work at all. TODO: if "field-data defined" ever gets implemented
3371  // we'll need a way to distinguish here between the two, possibly
3372  // using another flag in renderHints()
3373  const QgsFeature *f = context.feature();
3374  if ( f )
3375  {
3376  if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
3377  {
3378  const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3379  angle += m2p.mapRotation();
3380  }
3381  }
3382  }
3383 
3384  if ( angle )
3386 }
3387 
3388 double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context )
3389 {
3390  double scaledSize = mSize;
3391  const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
3392 
3393  bool ok = true;
3394  if ( hasDataDefinedSize )
3395  {
3396  context.setOriginalValueVariable( mSize );
3398  }
3399 
3400  if ( hasDataDefinedSize && ok )
3401  {
3402  switch ( mScaleMethod )
3403  {
3405  scaledSize = std::sqrt( scaledSize );
3406  break;
3408  break;
3409  }
3410  }
3411  return scaledSize;
3412 }
3413 
3415 {
3416  QPainter *p = context.renderContext().painter();
3417  if ( !p || !mNonZeroFontSize )
3418  return;
3419 
3420  QTransform transform;
3421 
3422  bool ok;
3423  QColor brushColor = mColor;
3425  {
3428  }
3429  brushColor = context.selected() ? context.renderContext().selectionColor() : brushColor;
3430  if ( !context.selected() || !SELECTION_IS_OPAQUE )
3431  {
3432  brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
3433  }
3434  mBrush.setColor( brushColor );
3435 
3436  QColor penColor = mStrokeColor;
3438  {
3441  }
3442  penColor.setAlphaF( penColor.alphaF() * context.opacity() );
3443 
3444  double penWidth = context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3446  {
3447  context.setOriginalValueVariable( mStrokeWidth );
3449  if ( ok )
3450  {
3451  penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3452  }
3453  }
3454 
3456  {
3458  const QString style = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyJoinStyle, context.renderContext().expressionContext(), QString(), &ok );
3459  if ( ok )
3460  {
3461  mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
3462  }
3463  }
3464 
3465  const QgsScopedQPainterState painterState( p );
3466  p->setBrush( mBrush );
3467  if ( !qgsDoubleNear( penWidth, 0.0 ) )
3468  {
3469  mPen.setColor( penColor );
3470  mPen.setWidthF( penWidth );
3471  p->setPen( mPen );
3472  }
3473  else
3474  {
3475  p->setPen( Qt::NoPen );
3476  }
3477 
3479  {
3480  context.setOriginalValueVariable( mFontFamily );
3482  mFont.setFamily( ok ? fontFamily : mFontFamily );
3483  }
3485  {
3486  context.setOriginalValueVariable( mFontStyle );
3489  }
3491  {
3492  mFontMetrics.reset( new QFontMetrics( mFont ) );
3493  }
3494 
3495  QPointF chrOffset = mChrOffset;
3496  double chrWidth;
3497  const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3498 
3499  const double sizeToRender = calculateSize( context );
3500 
3501  bool hasDataDefinedRotation = false;
3502  QPointF offset;
3503  double angle = 0;
3504  calculateOffsetAndRotation( context, sizeToRender, hasDataDefinedRotation, offset, angle );
3505 
3506  p->translate( point.x() + offset.x(), point.y() + offset.y() );
3507 
3508  if ( !qgsDoubleNear( angle, 0.0 ) )
3509  transform.rotate( angle );
3510 
3511  if ( !qgsDoubleNear( sizeToRender, mOrigSize ) )
3512  {
3513  const double s = sizeToRender / mOrigSize;
3514  transform.scale( s, s );
3515  }
3516 
3517  if ( !qgsDoubleNear( mFontSizeScale, 1.0 ) )
3518  transform.scale( mFontSizeScale, mFontSizeScale );
3519 
3520  if ( mUseCachedPath )
3521  {
3522  p->drawPath( transform.map( mCachedPath ) );
3523  }
3524  else
3525  {
3526  QPainterPath path;
3527  path.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3528  p->drawPath( transform.map( path ) );
3529  }
3530 }
3531 
3533 {
3534  QVariantMap props;
3535  props[QStringLiteral( "font" )] = mFontFamily;
3536  props[QStringLiteral( "font_style" )] = mFontStyle;
3537  props[QStringLiteral( "chr" )] = mString;
3538  props[QStringLiteral( "size" )] = QString::number( mSize );
3539  props[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3540  props[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3541  props[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
3542  props[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
3543  props[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
3544  props[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
3545  props[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
3546  props[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
3547  props[QStringLiteral( "angle" )] = QString::number( mAngle );
3548  props[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3549  props[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3550  props[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3551  props[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3552  props[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3553  return props;
3554 }
3555 
3557 {
3558  QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( mFontFamily, mString, mSize, mColor, mAngle );
3559  m->setFontStyle( mFontStyle );
3560  m->setStrokeColor( mStrokeColor );
3561  m->setStrokeWidth( mStrokeWidth );
3562  m->setStrokeWidthUnit( mStrokeWidthUnit );
3563  m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
3564  m->setPenJoinStyle( mPenJoinStyle );
3565  m->setOffset( mOffset );
3566  m->setOffsetUnit( mOffsetUnit );
3568  m->setSizeUnit( mSizeUnit );
3573  copyPaintEffect( m );
3574  return m;
3575 }
3576 
3577 void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
3578 {
3579  // <Graphic>
3580  QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
3581  element.appendChild( graphicElem );
3582 
3583  const QString fontPath = QStringLiteral( "ttf://%1" ).arg( mFontFamily );
3584  int markIndex = !mString.isEmpty() ? mString.at( 0 ).unicode() : 0;
3585  const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
3586  QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, QStringLiteral( "ttf" ), &markIndex, mColor, size );
3587 
3588  // <Rotation>
3589  QString angleFunc;
3590  bool ok;
3591  const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
3592  if ( !ok )
3593  {
3594  angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
3595  }
3596  else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
3597  {
3598  angleFunc = QString::number( angle + mAngle );
3599  }
3600  QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
3601 
3602  // <Displacement>
3603  const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
3605 }
3606 
3608 {
3610  || mStrokeWidthUnit == QgsUnitTypes::RenderMapUnits || mStrokeWidthUnit == QgsUnitTypes::RenderMetersInMapUnits
3612 }
3613 
3615 {
3616  QPointF chrOffset = mChrOffset;
3617  double chrWidth = mChrWidth;
3618  //calculate width of rendered character
3619  ( void )characterToRender( context, chrOffset, chrWidth );
3620 
3621  if ( !mFontMetrics )
3622  mFontMetrics.reset( new QFontMetrics( mFont ) );
3623 
3624  double scaledSize = calculateSize( context );
3625  if ( !qgsDoubleNear( scaledSize, mOrigSize ) )
3626  {
3627  chrWidth *= scaledSize / mOrigSize;
3628  }
3629  chrWidth *= mFontSizeScale;
3630 
3631  bool hasDataDefinedRotation = false;
3632  QPointF offset;
3633  double angle = 0;
3634  calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
3635  scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3636 
3637  QTransform transform;
3638 
3639  // move to the desired position
3640  transform.translate( point.x() + offset.x(), point.y() + offset.y() );
3641 
3642  if ( !qgsDoubleNear( angle, 0.0 ) )
3643  transform.rotate( angle );
3644 
3645  QRectF symbolBounds = transform.mapRect( QRectF( -chrWidth / 2.0,
3646  -scaledSize / 2.0,
3647  chrWidth,
3648  scaledSize ) );
3649  return symbolBounds;
3650 }
3651 
3653 {
3654  QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
3655 
3656  QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
3657  if ( graphicElem.isNull() )
3658  return nullptr;
3659 
3660  QString name, format;
3661  QColor color;
3662  double size;
3663  int chr;
3664 
3665  if ( !QgsSymbolLayerUtils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
3666  return nullptr;
3667 
3668  if ( !name.startsWith( QLatin1String( "ttf://" ) ) || format != QLatin1String( "ttf" ) )
3669  return nullptr;
3670 
3671  const QString fontFamily = name.mid( 6 );
3672 
3673  double angle = 0.0;
3674  QString angleFunc;
3675  if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
3676  {
3677  bool ok;
3678  const double d = angleFunc.toDouble( &ok );
3679  if ( ok )
3680  angle = d;
3681  }
3682 
3683  QPointF offset;
3685 
3686  const QString uom = element.attribute( QStringLiteral( "uom" ) );
3690 
3692  m->setOutputUnit( QgsUnitTypes::RenderUnit::RenderPixels );
3693  m->setAngle( angle );
3694  m->setOffset( offset );
3695  return m;
3696 }
3697 
3698 void QgsFontMarkerSymbolLayer::resolveFonts( const QVariantMap &properties, const QgsReadWriteContext &context )
3699 {
3700  const QString fontFamily = properties.value( QStringLiteral( "font" ), DEFAULT_FONTMARKER_FONT ).toString();
3702  {
3703  context.pushMessage( QObject::tr( "Font “%1” not available on system" ).arg( fontFamily ) );
3704  }
3705 }
3706 
3708 {
3709  QMap<QString, QgsProperty>::iterator it = mParameters.begin();
3710  for ( ; it != mParameters.end(); ++it )
3711  it.value().prepare( context.renderContext().expressionContext() );
3712 
3714 }
3715 
3716 
3717 QSet<QString> QgsSvgMarkerSymbolLayer::usedAttributes( const QgsRenderContext &context ) const
3718 {
3719  QSet<QString> attrs = QgsMarkerSymbolLayer::usedAttributes( context );
3720 
3721  QMap<QString, QgsProperty>::const_iterator it = mParameters.constBegin();
3722  for ( ; it != mParameters.constEnd(); ++it )
3723  {
3724  attrs.unite( it.value().referencedFields( context.expressionContext(), true ) );
3725  }
3726 
3727  return attrs;
3728 }
@ DynamicRotation
Rotation of symbol may be changed during rendering and symbol should not be cached.
ScaleMethod
Scale methods.
Definition: qgis.h:197
@ ScaleDiameter
Calculate scale by the diameter.
@ ScaleArea
Calculate scale by the area.
MarkerShape
Marker shapes.
Definition: qgis.h:1042
@ 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.
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.
QImage pathAsImage(const QString &path, const QSize size, const bool keepAspectRatio, const double opacity, bool &fitsInCache, bool blocking=false, double targetDpi=96, bool *isMissing=nullptr)
Returns the specified path rendered as an image.
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 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.
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
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...
void setFlag(Qgis::RenderContextFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
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.
QgsFields fields() const
Fields of the layer.
bool selected() const
Returns true if symbols should be rendered using the selected symbol coloring and style.
Qgis::SymbolRenderHints renderHints() const
Returns the rendering hint flags for the symbol.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
const QgsFeature * feature() const
Returns the current feature being rendered.
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for data defined symbology.
qreal opacity() const
Returns the opacity for the symbol.
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:38
Qgis::SymbolType type() const
Returns the symbol's type.
Definition: qgssymbol.h:97
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
static Q_INVOKABLE QgsUnitTypes::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:168
@ RenderUnknownUnit
Mixed or unknown units.
Definition: qgsunittypes.h:175
@ RenderMetersInMapUnits
Meters value as Map units.
Definition: qgsunittypes.h:176
@ RenderPercentage
Percentage of another measurement (e.g., canvas size, feature size)
Definition: qgsunittypes.h:172
@ RenderMillimeters
Millimeters.
Definition: qgsunittypes.h:169
@ RenderMapUnits
Map units.
Definition: qgsunittypes.h:170
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:1578
QMap< QString, QString > QgsStringMap
Definition: qgis.h:2026
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
#define DEG2RAD(x)
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
Q_GUI_EXPORT int qt_defaultDpiX()
Q_GUI_EXPORT int qt_defaultDpiY()
#define DEFAULT_FONTMARKER_JOINSTYLE
#define DEFAULT_RASTERMARKER_ANGLE
#define DEFAULT_RASTERMARKER_SIZE
#define DEFAULT_SVGMARKER_ANGLE
#define DEFAULT_SIMPLEMARKER_JOINSTYLE
#define DEFAULT_FONTMARKER_CHR
#define DEFAULT_SIMPLEMARKER_BORDERCOLOR
#define DEFAULT_SIMPLEMARKER_SIZE
#define DEFAULT_SIMPLEMARKER_NAME
#define DEFAULT_SIMPLEMARKER_ANGLE
#define DEFAULT_SVGMARKER_SIZE
#define DEFAULT_FONTMARKER_FONT
#define DEFAULT_FONTMARKER_BORDERCOLOR
#define DEFAULT_FONTMARKER_ANGLE
#define DEFAULT_FONTMARKER_COLOR
#define DEFAULT_FONTMARKER_SIZE
#define DEFAULT_SIMPLEMARKER_COLOR
#define DEFAULT_SCALE_METHOD