QGIS API Documentation 3.43.0-Master (ac9f54ad1f7)
Loading...
Searching...
No Matches
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
17#include "qgssymbollayerutils.h"
18
19#include "qgsdxfexport.h"
20#include "qgsdxfpaintdevice.h"
21#include "qgsfontutils.h"
22#include "qgsimagecache.h"
23#include "qgsimageoperation.h"
24#include "qgsrendercontext.h"
25#include "qgslogger.h"
26#include "qgssvgcache.h"
27#include "qgsunittypes.h"
28#include "qgssymbol.h"
29#include "qgsfillsymbol.h"
30#include "qgsfontmanager.h"
31#include "qgscolorutils.h"
32#include "qgspainting.h"
33#include "qgssldexportcontext.h"
34
35#include <QPainter>
36#include <QSvgRenderer>
37#include <QFileInfo>
38#include <QDir>
39#include <QDomDocument>
40#include <QDomElement>
41#include <QUrlQuery>
42
43#include <cmath>
44
45static constexpr int MAX_FONT_CHARACTER_SIZE_IN_PIXELS = 500;
46
48
49
50//
51// QgsSimpleMarkerSymbolLayerBase
52//
53
97
99 : mShape( shape )
100{
101 mSize = size;
102 mAngle = angle;
103 mOffset = QPointF( 0, 0 );
107}
108
110
112{
113 switch ( shape )
114 {
145 return true;
146
154 return false;
155 }
156 return true;
157}
158
160{
161 const bool hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation
163 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
164
165 // use either QPolygonF or QPainterPath for drawing
166 if ( !prepareMarkerShape( mShape ) ) // drawing as a polygon
167 {
168 prepareMarkerPath( mShape ); // drawing as a painter path
169 }
170
171 QTransform transform;
172
173 // scale the shape (if the size is not going to be modified)
174 if ( !hasDataDefinedSize )
175 {
176 double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
178 {
179 // rendering for symbol previews -- an size in meters in map units can't be calculated, so treat the size as millimeters
180 // and clamp it to a reasonable range. It's the best we can do in this situation!
181 scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
182 }
183
184 const double half = scaledSize / 2.0;
185 transform.scale( half, half );
186 }
187
188 // rotate if the rotation is not going to be changed during the rendering
189 if ( !hasDataDefinedRotation && !qgsDoubleNear( mAngle, 0.0 ) )
190 {
191 transform.rotate( mAngle );
192 }
193
194 if ( !mPolygon.isEmpty() )
195 mPolygon = transform.map( mPolygon );
196 else
197 mPath = transform.map( mPath );
198
200}
201
203{
204 Q_UNUSED( context )
205}
206
208{
209 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
210 //of the rendered point!
211
212 QPainter *p = context.renderContext().painter();
213 if ( !p )
214 {
215 return;
216 }
217
218 bool hasDataDefinedSize = false;
219 const double scaledSize = calculateSize( context, hasDataDefinedSize );
220
221 bool hasDataDefinedRotation = false;
222 QPointF offset;
223 double angle = 0;
224 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
225
226 //data defined shape?
227 bool createdNewPath = false;
228 bool ok = true;
229 Qgis::MarkerShape symbol = mShape;
231 {
232 context.setOriginalValueVariable( encodeShape( symbol ) );
234 if ( !QgsVariantUtils::isNull( exprVal ) )
235 {
236 const Qgis::MarkerShape decoded = decodeShape( exprVal.toString(), &ok );
237 if ( ok )
238 {
239 symbol = decoded;
240
241 if ( !prepareMarkerShape( symbol ) ) // drawing as a polygon
242 {
243 prepareMarkerPath( symbol ); // drawing as a painter path
244 }
245 createdNewPath = true;
246 }
247 }
248 else
249 {
250 symbol = mShape;
251 }
252 }
253
254 QTransform transform;
255
256 // move to the desired position
257 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
258
259 // resize if necessary
260 if ( hasDataDefinedSize || createdNewPath )
261 {
262 double s = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
264 {
265 // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
266 // and clamp it to a reasonable range. It's the best we can do in this situation!
267 s = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
268 }
269 const double half = s / 2.0;
270 transform.scale( half, half );
271 }
272
273 if ( !qgsDoubleNear( angle, 0.0 ) && ( hasDataDefinedRotation || createdNewPath ) )
274 {
275 transform.rotate( angle );
276 }
277
278 //need to pass: symbol, polygon, path
279
280 QPolygonF polygon;
281 QPainterPath path;
282 if ( !mPolygon.isEmpty() )
283 {
284 polygon = transform.map( mPolygon );
285 }
286 else
287 {
288 path = transform.map( mPath );
289 }
290 draw( context, symbol, polygon, path );
291}
292
294{
295 bool hasDataDefinedSize = false;
296 double scaledSize = calculateSize( context, hasDataDefinedSize );
297
298 bool hasDataDefinedRotation = false;
299 QPointF offset;
300 double angle = 0;
301 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
302
303 scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
304
305 QTransform transform;
306
307 // move to the desired position
308 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
309
310 if ( !qgsDoubleNear( angle, 0.0 ) )
311 transform.rotate( angle );
312
313 return transform.mapRect( QRectF( -scaledSize / 2.0,
314 -scaledSize / 2.0,
315 scaledSize,
316 scaledSize ) );
317}
318
320{
321 if ( ok )
322 *ok = true;
323 const QString cleaned = name.toLower().trimmed();
324
325 if ( cleaned == QLatin1String( "square" ) || cleaned == QLatin1String( "rectangle" ) )
327 else if ( cleaned == QLatin1String( "trapezoid" ) )
329 else if ( cleaned == QLatin1String( "parallelogram_right" ) )
331 else if ( cleaned == QLatin1String( "parallelogram_left" ) )
333 else if ( cleaned == QLatin1String( "square_with_corners" ) )
335 else if ( cleaned == QLatin1String( "rounded_square" ) )
337 else if ( cleaned == QLatin1String( "diamond" ) )
339 else if ( cleaned == QLatin1String( "shield" ) )
341 else if ( cleaned == QLatin1String( "pentagon" ) )
343 else if ( cleaned == QLatin1String( "hexagon" ) )
345 else if ( cleaned == QLatin1String( "octagon" ) )
347 else if ( cleaned == QLatin1String( "decagon" ) )
349 else if ( cleaned == QLatin1String( "triangle" ) )
351 else if ( cleaned == QLatin1String( "equilateral_triangle" ) )
353 else if ( cleaned == QLatin1String( "star_diamond" ) )
355 else if ( cleaned == QLatin1String( "star" ) || cleaned == QLatin1String( "regular_star" ) )
357 else if ( cleaned == QLatin1String( "heart" ) )
359 else if ( cleaned == QLatin1String( "arrow" ) )
361 else if ( cleaned == QLatin1String( "circle" ) )
363 else if ( cleaned == QLatin1String( "cross" ) )
365 else if ( cleaned == QLatin1String( "cross_fill" ) )
367 else if ( cleaned == QLatin1String( "cross2" ) || cleaned == QLatin1String( "x" ) )
369 else if ( cleaned == QLatin1String( "line" ) )
371 else if ( cleaned == QLatin1String( "arrowhead" ) )
373 else if ( cleaned == QLatin1String( "filled_arrowhead" ) )
375 else if ( cleaned == QLatin1String( "semi_circle" ) )
377 else if ( cleaned == QLatin1String( "third_circle" ) )
379 else if ( cleaned == QLatin1String( "quarter_circle" ) )
381 else if ( cleaned == QLatin1String( "quarter_square" ) )
383 else if ( cleaned == QLatin1String( "half_square" ) )
385 else if ( cleaned == QLatin1String( "diagonal_half_square" ) )
387 else if ( cleaned == QLatin1String( "right_half_triangle" ) )
389 else if ( cleaned == QLatin1String( "left_half_triangle" ) )
391 else if ( cleaned == QLatin1String( "asterisk_fill" ) )
393 else if ( cleaned == QLatin1String( "half_arc" ) )
395 else if ( cleaned == QLatin1String( "third_arc" ) )
397 else if ( cleaned == QLatin1String( "quarter_arc" ) )
399
400 if ( ok )
401 *ok = false;
403}
404
406{
407 switch ( shape )
408 {
410 return QStringLiteral( "square" );
412 return QStringLiteral( "quarter_square" );
414 return QStringLiteral( "half_square" );
416 return QStringLiteral( "diagonal_half_square" );
418 return QStringLiteral( "parallelogram_right" );
420 return QStringLiteral( "parallelogram_left" );
422 return QStringLiteral( "trapezoid" );
424 return QStringLiteral( "shield" );
426 return QStringLiteral( "diamond" );
428 return QStringLiteral( "pentagon" );
430 return QStringLiteral( "hexagon" );
432 return QStringLiteral( "octagon" );
434 return QStringLiteral( "decagon" );
436 return QStringLiteral( "square_with_corners" );
438 return QStringLiteral( "rounded_square" );
440 return QStringLiteral( "triangle" );
442 return QStringLiteral( "equilateral_triangle" );
444 return QStringLiteral( "left_half_triangle" );
446 return QStringLiteral( "right_half_triangle" );
448 return QStringLiteral( "star_diamond" );
450 return QStringLiteral( "star" );
452 return QStringLiteral( "heart" );
454 return QStringLiteral( "arrow" );
456 return QStringLiteral( "filled_arrowhead" );
458 return QStringLiteral( "cross_fill" );
460 return QStringLiteral( "circle" );
462 return QStringLiteral( "cross" );
464 return QStringLiteral( "cross2" );
466 return QStringLiteral( "line" );
468 return QStringLiteral( "arrowhead" );
470 return QStringLiteral( "semi_circle" );
472 return QStringLiteral( "third_circle" );
474 return QStringLiteral( "quarter_circle" );
476 return QStringLiteral( "asterisk_fill" );
478 return QStringLiteral( "half_arc" );
480 return QStringLiteral( "third_arc" );
482 return QStringLiteral( "quarter_arc" );
483 }
484 return QString();
485}
486
491
493{
494 polygon.clear();
495
496 switch ( shape )
497 {
499 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 1, 1 ) ) );
500 return true;
501
503 {
504 static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 0.6072;
505
506 polygon << QPointF( - VERTEX_OFFSET_FROM_ORIGIN, 1 )
507 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, 1 )
508 << QPointF( 1, VERTEX_OFFSET_FROM_ORIGIN )
509 << QPointF( 1, -VERTEX_OFFSET_FROM_ORIGIN )
510 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, -1 )
511 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, -1 )
512 << QPointF( -1, -VERTEX_OFFSET_FROM_ORIGIN )
513 << QPointF( -1, VERTEX_OFFSET_FROM_ORIGIN )
514 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 );
515 return true;
516 }
517
519 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 0 ) ) );
520 return true;
521
523 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 1 ) ) );
524 return true;
525
527 polygon << QPointF( -1, -1 ) << QPointF( 1, 1 ) << QPointF( -1, 1 ) << QPointF( -1, -1 );
528 return true;
529
531 polygon << QPointF( 0.5, -0.5 )
532 << QPointF( 1, 0.5 )
533 << QPointF( -1, 0.5 )
534 << QPointF( -0.5, -0.5 )
535 << QPointF( 0.5, -0.5 );
536 return true;
537
539 polygon << QPointF( 0.5, 0.5 )
540 << QPointF( 1, -0.5 )
541 << QPointF( -0.5, -0.5 )
542 << QPointF( -1, 0.5 )
543 << QPointF( 0.5, 0.5 );
544 return true;
545
547 polygon << QPointF( 1, 0.5 )
548 << QPointF( 0.5, -0.5 )
549 << QPointF( -1, -0.5 )
550 << QPointF( -0.5, 0.5 )
551 << QPointF( 1, 0.5 );
552 return true;
553
555 polygon << QPointF( -1, 0 ) << QPointF( 0, 1 )
556 << QPointF( 1, 0 ) << QPointF( 0, -1 ) << QPointF( -1, 0 );
557 return true;
558
560 polygon << QPointF( 1, 0.5 )
561 << QPointF( 1, -1 )
562 << QPointF( -1, -1 )
563 << QPointF( -1, 0.5 )
564 << QPointF( 0, 1 )
565 << QPointF( 1, 0.5 );
566 return true;
567
569 /* angular-representation of hardcoded values used
570 polygon << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288.0 ) ) )
571 << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) )
572 << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) )
573 << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) )
574 << QPointF( 0, -1 ); */
575 polygon << QPointF( -0.9511, -0.3090 )
576 << QPointF( -0.5878, 0.8090 )
577 << QPointF( 0.5878, 0.8090 )
578 << QPointF( 0.9511, -0.3090 )
579 << QPointF( 0, -1 )
580 << QPointF( -0.9511, -0.3090 );
581 return true;
582
584 /* angular-representation of hardcoded values used
585 polygon << QPointF( std::sin( DEG2RAD( 300.0 ) ), - std::cos( DEG2RAD( 300.0 ) ) )
586 << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
587 << QPointF( std::sin( DEG2RAD( 180.0 ) ), - std::cos( DEG2RAD( 180.0 ) ) )
588 << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
589 << QPointF( std::sin( DEG2RAD( 60.0 ) ), - std::cos( DEG2RAD( 60.0 ) ) )
590 << QPointF( 0, -1 ); */
591 polygon << QPointF( -0.8660, -0.5 )
592 << QPointF( -0.8660, 0.5 )
593 << QPointF( 0, 1 )
594 << QPointF( 0.8660, 0.5 )
595 << QPointF( 0.8660, -0.5 )
596 << QPointF( 0, -1 )
597 << QPointF( -0.8660, -0.5 );
598 return true;
599
601 {
602 static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 1.0 / ( 1 + M_SQRT2 );
603
604 polygon << QPointF( - VERTEX_OFFSET_FROM_ORIGIN, 1 )
605 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, 1 )
606 << QPointF( 1, VERTEX_OFFSET_FROM_ORIGIN )
607 << QPointF( 1, -VERTEX_OFFSET_FROM_ORIGIN )
608 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, -1 )
609 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, -1 )
610 << QPointF( -1, -VERTEX_OFFSET_FROM_ORIGIN )
611 << QPointF( -1, VERTEX_OFFSET_FROM_ORIGIN )
612 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 );
613 return true;
614 }
615
617 {
618
619 polygon << QPointF( 0.587785252, 0.809016994 )
620 << QPointF( 0.951056516, 0.309016994 )
621 << QPointF( 0.951056516, -0.309016994 )
622 << QPointF( 0.587785252, -0.809016994 )
623 << QPointF( 0, -1 )
624 << QPointF( -0.587785252, -0.809016994 )
625 << QPointF( -0.951056516, -0.309016994 )
626 << QPointF( -0.951056516, 0.309016994 )
627 << QPointF( -0.587785252, 0.809016994 )
628 << QPointF( 0, 1 )
629 << QPointF( 0.587785252, 0.809016994 );
630 return true;
631 }
632
634 polygon << QPointF( -1, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
635 return true;
636
638 /* angular-representation of hardcoded values used
639 polygon << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
640 << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
641 << QPointF( 0, -1 ); */
642 polygon << QPointF( -0.8660, 0.5 )
643 << QPointF( 0.8660, 0.5 )
644 << QPointF( 0, -1 )
645 << QPointF( -0.8660, 0.5 );
646 return true;
647
649 polygon << QPointF( 0, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( 0, 1 );
650 return true;
651
653 polygon << QPointF( -1, 1 ) << QPointF( 0, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
654 return true;
655
657 {
658 const double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
659
660 polygon << QPointF( inner_r * std::sin( DEG2RAD( 315.0 ) ), - inner_r * std::cos( DEG2RAD( 315.0 ) ) )
661 << QPointF( std::sin( DEG2RAD( 270 ) ), - std::cos( DEG2RAD( 270 ) ) )
662 << QPointF( inner_r * std::sin( DEG2RAD( 225.0 ) ), - inner_r * std::cos( DEG2RAD( 225.0 ) ) )
663 << QPointF( std::sin( DEG2RAD( 180 ) ), - std::cos( DEG2RAD( 180 ) ) )
664 << QPointF( inner_r * std::sin( DEG2RAD( 135.0 ) ), - inner_r * std::cos( DEG2RAD( 135.0 ) ) )
665 << QPointF( std::sin( DEG2RAD( 90 ) ), - std::cos( DEG2RAD( 90 ) ) )
666 << QPointF( inner_r * std::sin( DEG2RAD( 45.0 ) ), - inner_r * std::cos( DEG2RAD( 45.0 ) ) )
667 << QPointF( std::sin( DEG2RAD( 0 ) ), - std::cos( DEG2RAD( 0 ) ) )
668 << QPointF( inner_r * std::sin( DEG2RAD( 315.0 ) ), - inner_r * std::cos( DEG2RAD( 315.0 ) ) );
669 return true;
670 }
671
673 {
674 const double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
675
676 polygon << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ) // 324
677 << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288 ) ) ) // 288
678 << QPointF( inner_r * std::sin( DEG2RAD( 252.0 ) ), - inner_r * std::cos( DEG2RAD( 252.0 ) ) ) // 252
679 << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) ) // 216
680 << QPointF( 0, inner_r ) // 180
681 << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) ) // 144
682 << QPointF( inner_r * std::sin( DEG2RAD( 108.0 ) ), - inner_r * std::cos( DEG2RAD( 108.0 ) ) ) // 108
683 << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) ) // 72
684 << QPointF( inner_r * std::sin( DEG2RAD( 36.0 ) ), - inner_r * std::cos( DEG2RAD( 36.0 ) ) ) // 36
685 << QPointF( 0, -1 )
686 << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ); // 324; // 0
687 return true;
688 }
689
691 polygon << QPointF( 0, -1 )
692 << QPointF( 0.5, -0.5 )
693 << QPointF( 0.25, -0.5 )
694 << QPointF( 0.25, 1 )
695 << QPointF( -0.25, 1 )
696 << QPointF( -0.25, -0.5 )
697 << QPointF( -0.5, -0.5 )
698 << QPointF( 0, -1 );
699 return true;
700
702 polygon << QPointF( 0, 0 ) << QPointF( -1, 1 ) << QPointF( -1, -1 ) << QPointF( 0, 0 );
703 return true;
704
706 polygon << QPointF( -1, -0.2 )
707 << QPointF( -1, -0.2 )
708 << QPointF( -1, 0.2 )
709 << QPointF( -0.2, 0.2 )
710 << QPointF( -0.2, 1 )
711 << QPointF( 0.2, 1 )
712 << QPointF( 0.2, 0.2 )
713 << QPointF( 1, 0.2 )
714 << QPointF( 1, -0.2 )
715 << QPointF( 0.2, -0.2 )
716 << QPointF( 0.2, -1 )
717 << QPointF( -0.2, -1 )
718 << QPointF( -0.2, -0.2 )
719 << QPointF( -1, -0.2 );
720 return true;
721
723 {
724 static constexpr double THICKNESS = 0.3;
725 static constexpr double HALF_THICKNESS = THICKNESS / 2.0;
726 static constexpr double INTERSECTION_POINT = THICKNESS / M_SQRT2;
727 static constexpr double DIAGONAL1 = M_SQRT1_2 - INTERSECTION_POINT * 0.5;
728 static constexpr double DIAGONAL2 = M_SQRT1_2 + INTERSECTION_POINT * 0.5;
729
730 polygon << QPointF( -HALF_THICKNESS, -1 )
731 << QPointF( HALF_THICKNESS, -1 )
732 << QPointF( HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
733 << QPointF( DIAGONAL1, -DIAGONAL2 )
734 << QPointF( DIAGONAL2, -DIAGONAL1 )
735 << QPointF( HALF_THICKNESS + INTERSECTION_POINT, -HALF_THICKNESS )
736 << QPointF( 1, -HALF_THICKNESS )
737 << QPointF( 1, HALF_THICKNESS )
738 << QPointF( HALF_THICKNESS + INTERSECTION_POINT, HALF_THICKNESS )
739 << QPointF( DIAGONAL2, DIAGONAL1 )
740 << QPointF( DIAGONAL1, DIAGONAL2 )
741 << QPointF( HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
742 << QPointF( HALF_THICKNESS, 1 )
743 << QPointF( -HALF_THICKNESS, 1 )
744 << QPointF( -HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
745 << QPointF( -DIAGONAL1, DIAGONAL2 )
746 << QPointF( -DIAGONAL2, DIAGONAL1 )
747 << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, HALF_THICKNESS )
748 << QPointF( -1, HALF_THICKNESS )
749 << QPointF( -1, -HALF_THICKNESS )
750 << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, -HALF_THICKNESS )
751 << QPointF( -DIAGONAL2, -DIAGONAL1 )
752 << QPointF( -DIAGONAL1, -DIAGONAL2 )
753 << QPointF( -HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
754 << QPointF( -HALF_THICKNESS, -1 );
755 return true;
756 }
757
771 return false;
772 }
773
774 return false;
775}
776
778{
779 mPath = QPainterPath();
780
781 switch ( symbol )
782 {
784
785 mPath.addEllipse( QRectF( -1, -1, 2, 2 ) ); // x,y,w,h
786 return true;
787
789 mPath.moveTo( -1, -1 );
790 mPath.addRoundedRect( -1, -1, 2, 2, 0.25, 0.25 );
791 return true;
792
794 mPath.arcTo( -1, -1, 2, 2, 0, 180 );
795 mPath.lineTo( 0, 0 );
796 return true;
797
799 mPath.arcTo( -1, -1, 2, 2, 90, 120 );
800 mPath.lineTo( 0, 0 );
801 return true;
802
804 mPath.arcTo( -1, -1, 2, 2, 90, 90 );
805 mPath.lineTo( 0, 0 );
806 return true;
807
809 mPath.moveTo( 1, 0 );
810 mPath.arcTo( -1, -1, 2, 2, 0, 180 );
811 return true;
812
814 mPath.moveTo( 0, -1 );
815 mPath.arcTo( -1, -1, 2, 2, 90, 120 );
816 return true;
817
819 mPath.moveTo( 0, -1 );
820 mPath.arcTo( -1, -1, 2, 2, 90, 90 );
821 return true;
822
824 mPath.moveTo( -1, 0 );
825 mPath.lineTo( 1, 0 ); // horizontal
826 mPath.moveTo( 0, -1 );
827 mPath.lineTo( 0, 1 ); // vertical
828 return true;
829
831 mPath.moveTo( -1, -1 );
832 mPath.lineTo( 1, 1 );
833 mPath.moveTo( 1, -1 );
834 mPath.lineTo( -1, 1 );
835 return true;
836
838 mPath.moveTo( 0, -1 );
839 mPath.lineTo( 0, 1 ); // vertical line
840 return true;
841
843 mPath.moveTo( -1, -1 );
844 mPath.lineTo( 0, 0 );
845 mPath.lineTo( -1, 1 );
846 return true;
847
849 mPath.moveTo( 0, 0.75 );
850 mPath.arcTo( 0, -1, 1, 1, -45, 210 );
851 mPath.arcTo( -1, -1, 1, 1, 15, 210 );
852 mPath.lineTo( 0, 0.75 );
853 return true;
854
879 return false;
880 }
881 return false;
882}
883
884double QgsSimpleMarkerSymbolLayerBase::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
885{
886 double scaledSize = mSize;
887
889 bool ok = true;
890 if ( hasDataDefinedSize )
891 {
894 mSize, &ok );
895 }
896
897 if ( hasDataDefinedSize && ok )
898 {
899 switch ( mScaleMethod )
900 {
902 scaledSize = std::sqrt( scaledSize );
903 break;
905 break;
906 }
907 }
908
909 return scaledSize;
910}
911
912void QgsSimpleMarkerSymbolLayerBase::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle ) const
913{
914 //offset
915 double offsetX = 0;
916 double offsetY = 0;
917 markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
918 offset = QPointF( offsetX, offsetY );
919
920 hasDataDefinedRotation = false;
921 //angle
922 bool ok = true;
925 {
928
929 // If the expression evaluation was not successful, fallback to static value
930 if ( !ok )
932
933 hasDataDefinedRotation = true;
934 }
935
936 hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || hasDataDefinedRotation;
937
938 if ( hasDataDefinedRotation )
939 {
940 // For non-point markers, "dataDefinedRotation" means following the
941 // shape (shape-data defined). For them, "field-data defined" does
942 // not work at all. TODO: if "field-data defined" ever gets implemented
943 // we'll need a way to distinguish here between the two, possibly
944 // using another flag in renderHints()
945 const QgsFeature *f = context.feature();
946 if ( f )
947 {
948 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
949 {
950 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
951 angle += m2p.mapRotation();
952 }
953 }
954 }
955
956 if ( angle )
958}
959
960
961//
962// QgsSimpleMarkerSymbolLayer
963//
964
965QgsSimpleMarkerSymbolLayer::QgsSimpleMarkerSymbolLayer( Qgis::MarkerShape shape, double size, double angle, Qgis::ScaleMethod scaleMethod, const QColor &color, const QColor &strokeColor, Qt::PenJoinStyle penJoinStyle )
966 : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
967 , mStrokeColor( strokeColor )
968 , mPenJoinStyle( penJoinStyle )
969{
970 mColor = color;
971}
972
974
976{
984
985 if ( props.contains( QStringLiteral( "name" ) ) )
986 {
987 shape = decodeShape( props[QStringLiteral( "name" )].toString() );
988 }
989 if ( props.contains( QStringLiteral( "color" ) ) )
990 color = QgsColorUtils::colorFromString( props[QStringLiteral( "color" )].toString() );
991 if ( props.contains( QStringLiteral( "color_border" ) ) )
992 {
993 //pre 2.5 projects use "color_border"
994 strokeColor = QgsColorUtils::colorFromString( props[QStringLiteral( "color_border" )].toString() );
995 }
996 else if ( props.contains( QStringLiteral( "outline_color" ) ) )
997 {
998 strokeColor = QgsColorUtils::colorFromString( props[QStringLiteral( "outline_color" )].toString() );
999 }
1000 else if ( props.contains( QStringLiteral( "line_color" ) ) )
1001 {
1002 strokeColor = QgsColorUtils::colorFromString( props[QStringLiteral( "line_color" )].toString() );
1003 }
1004 if ( props.contains( QStringLiteral( "joinstyle" ) ) )
1005 {
1006 penJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() );
1007 }
1008 if ( props.contains( QStringLiteral( "size" ) ) )
1009 size = props[QStringLiteral( "size" )].toDouble();
1010 if ( props.contains( QStringLiteral( "angle" ) ) )
1011 angle = props[QStringLiteral( "angle" )].toDouble();
1012 if ( props.contains( QStringLiteral( "scale_method" ) ) )
1013 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1014
1016 if ( props.contains( QStringLiteral( "offset" ) ) )
1017 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1018 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1019 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1020 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1021 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1022 if ( props.contains( QStringLiteral( "size_unit" ) ) )
1023 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1024 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1025 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1026
1027 if ( props.contains( QStringLiteral( "outline_style" ) ) )
1028 {
1029 m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "outline_style" )].toString() ) );
1030 }
1031 else if ( props.contains( QStringLiteral( "line_style" ) ) )
1032 {
1033 m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "line_style" )].toString() ) );
1034 }
1035 if ( props.contains( QStringLiteral( "outline_width" ) ) )
1036 {
1037 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
1038 }
1039 else if ( props.contains( QStringLiteral( "line_width" ) ) )
1040 {
1041 m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
1042 }
1043 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
1044 {
1045 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
1046 }
1047 if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
1048 {
1049 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
1050 }
1051 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
1052 {
1053 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
1054 }
1055
1056 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1057 {
1058 m->setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1059 }
1060 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1061 {
1062 m->setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1063 }
1064
1065 if ( props.contains( QStringLiteral( "cap_style" ) ) )
1066 {
1067 m->setPenCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( props[QStringLiteral( "cap_style" )].toString() ) );
1068 }
1069
1071
1072 return m;
1073}
1074
1075
1077{
1078 return QStringLiteral( "SimpleMarker" );
1079}
1080
1085
1087{
1089
1090 QColor brushColor = mColor;
1091 QColor penColor = mStrokeColor;
1092
1093 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
1094 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
1095
1096 mBrush = QBrush( brushColor );
1097 mPen = QPen( penColor );
1098 mPen.setStyle( mStrokeStyle );
1099 mPen.setCapStyle( mPenCapStyle );
1100 mPen.setJoinStyle( mPenJoinStyle );
1102
1103 QColor selBrushColor = context.renderContext().selectionColor();
1104 QColor selPenColor = selBrushColor == mColor ? selBrushColor : mStrokeColor;
1105 if ( context.opacity() < 1 && !SELECTION_IS_OPAQUE )
1106 {
1107 selBrushColor.setAlphaF( context.opacity() );
1108 selPenColor.setAlphaF( context.opacity() );
1109 }
1110 mSelBrush = QBrush( selBrushColor );
1111 mSelPen = QPen( selPenColor );
1112 mSelPen.setStyle( mStrokeStyle );
1114
1116 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
1117
1118 // use caching only when:
1119 // - size, rotation, shape, color, stroke color is not data-defined
1120 // - drawing to screen (not printer)
1121 mUsingCache = !hasDataDefinedRotation && !hasDataDefinedSize && !context.forceVectorRendering()
1125
1126 if ( mUsingCache )
1127 mCachedOpacity = context.opacity();
1128
1129 if ( !shapeIsFilled( mShape ) )
1130 {
1131 // some markers can't be drawn as a polygon (circle, cross)
1132 // For these set the selected stroke color to the selected color
1133 mSelPen.setColor( selBrushColor );
1134 }
1135
1136
1137 if ( mUsingCache )
1138 {
1139 if ( !prepareCache( context ) )
1140 {
1141 mUsingCache = false;
1142 }
1143 }
1144 else
1145 {
1146 mCache = QImage();
1147 mSelCache = QImage();
1148 }
1149}
1150
1151
1153{
1154 double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
1155 const double deviceRatio = context.renderContext().devicePixelRatio();
1157 {
1158 // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
1159 // and clamp it to a reasonable range. It's the best we can do in this situation!
1160 scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
1161 }
1162
1163 // take into account angle (which is not data-defined otherwise cache wouldn't be used)
1164 if ( !qgsDoubleNear( mAngle, 0.0 ) )
1165 {
1166 scaledSize = ( std::abs( std::sin( mAngle * M_PI / 180 ) ) + std::abs( std::cos( mAngle * M_PI / 180 ) ) ) * scaledSize;
1167 }
1168 // calculate necessary image size for the cache
1169 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
1170 const int imageSize = ( static_cast< int >( scaledSize ) + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width
1171 const double center = imageSize / 2.0;
1172 if ( imageSize * deviceRatio > MAXIMUM_CACHE_WIDTH )
1173 {
1174 return false;
1175 }
1176
1177 mCache = QImage( QSize( imageSize * deviceRatio,
1178 imageSize * deviceRatio ), QImage::Format_ARGB32_Premultiplied );
1179 mCache.setDevicePixelRatio( context.renderContext().devicePixelRatio() );
1180 mCache.setDotsPerMeterX( std::round( context.renderContext().scaleFactor() * 1000 ) );
1181 mCache.setDotsPerMeterY( std::round( context.renderContext().scaleFactor() * 1000 ) );
1182 mCache.fill( 0 );
1183
1184 const bool needsBrush = shapeIsFilled( mShape );
1185
1186 QPainter p;
1187 p.begin( &mCache );
1188 p.setRenderHint( QPainter::Antialiasing );
1189 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1190 p.setPen( mPen );
1191 p.translate( QPointF( center, center ) );
1192 drawMarker( &p, context );
1193 p.end();
1194
1195 // Construct the selected version of the Cache
1196
1197 const QColor selColor = context.renderContext().selectionColor();
1198
1199 mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
1200 mSelCache.fill( 0 );
1201
1202 p.begin( &mSelCache );
1203 p.setRenderHint( QPainter::Antialiasing );
1204 p.setBrush( needsBrush ? mSelBrush : Qt::NoBrush );
1205 p.setPen( mSelPen );
1206 p.translate( QPointF( center, center ) );
1207 drawMarker( &p, context );
1208 p.end();
1209
1210 // Check that the selected version is different. If not, then re-render,
1211 // filling the background with the selection color and using the normal
1212 // colors for the symbol .. could be ugly!
1213
1214 if ( mSelCache == mCache )
1215 {
1216 p.begin( &mSelCache );
1217 p.setRenderHint( QPainter::Antialiasing );
1218 p.fillRect( 0, 0, imageSize, imageSize, selColor );
1219 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1220 p.setPen( mPen );
1221 p.translate( QPointF( center, center ) );
1222 drawMarker( &p, context );
1223 p.end();
1224 }
1225
1226 return true;
1227}
1228
1229void QgsSimpleMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
1230{
1231 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1232 //of the rendered point!
1233
1234 QPainter *p = context.renderContext().painter();
1235 if ( !p )
1236 {
1237 return;
1238 }
1239
1240 QColor brushColor = mColor;
1241 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
1242 mBrush.setColor( brushColor );
1243
1244 QColor penColor = mStrokeColor;
1245 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
1246 mPen.setColor( penColor );
1247
1248 bool ok = true;
1250 {
1253 if ( ok )
1254 {
1255 c.setAlphaF( c.alphaF() * context.opacity() );
1256 mBrush.setColor( c );
1257 }
1258 }
1260 {
1263 if ( ok )
1264 {
1265 c.setAlphaF( c.alphaF() * context.opacity() );
1266 mPen.setColor( c );
1267 mSelPen.setColor( c );
1268 }
1269 }
1271 {
1274 if ( ok )
1275 {
1278 }
1279 }
1281 {
1284 if ( ok )
1285 {
1288 }
1289 }
1291 {
1294 if ( ok )
1295 {
1296 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1297 mSelPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1298 }
1299 }
1301 {
1304 if ( ok )
1305 {
1306 mPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1307 mSelPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1308 }
1309 }
1310
1311 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
1312 if ( shapeIsFilled( shape ) )
1313 {
1314 p->setBrush( useSelectedColor ? mSelBrush : mBrush );
1315 }
1316 else
1317 {
1318 p->setBrush( Qt::NoBrush );
1319 }
1320 p->setPen( useSelectedColor ? mSelPen : mPen );
1321
1322 if ( !polygon.isEmpty() )
1323 p->drawPolygon( polygon );
1324 else
1325 p->drawPath( path );
1326}
1327
1329{
1330 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1331 //of the rendered point!
1332
1333 QPainter *p = context.renderContext().painter();
1334 if ( !p )
1335 {
1336 return;
1337 }
1338
1339 if ( mUsingCache && qgsDoubleNear( mCachedOpacity, context.opacity() ) )
1340 {
1341 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
1342 const QImage &img = useSelectedColor ? mSelCache : mCache;
1343 const double s = img.width() / img.devicePixelRatioF();
1344
1345 bool hasDataDefinedSize = false;
1346 const double scaledSize = calculateSize( context, hasDataDefinedSize );
1347
1348 bool hasDataDefinedRotation = false;
1349 QPointF offset;
1350 double angle = 0;
1351 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
1352
1353 p->drawImage( QRectF( point.x() - s / 2.0 + offset.x(),
1354 point.y() - s / 2.0 + offset.y(),
1355 s, s ), img );
1356 }
1357 else
1358 {
1360 }
1361}
1362
1364{
1365 QVariantMap map;
1366 map[QStringLiteral( "name" )] = encodeShape( mShape );
1367 map[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mColor );
1368 map[QStringLiteral( "outline_color" )] = QgsColorUtils::colorToString( mStrokeColor );
1369 map[QStringLiteral( "size" )] = QString::number( mSize );
1370 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1371 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1372 map[QStringLiteral( "angle" )] = QString::number( mAngle );
1373 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1374 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1375 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1376 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1377 map[QStringLiteral( "outline_style" )] = QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle );
1378 map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
1379 map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
1380 map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
1381 map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
1382 map[QStringLiteral( "cap_style" )] = QgsSymbolLayerUtils::encodePenCapStyle( mPenCapStyle );
1383 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
1384 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
1385 return map;
1386}
1387
1407
1408void QgsSimpleMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1409{
1410 QgsSldExportContext context;
1411 context.setExtraProperties( props );
1412 toSld( doc, element, context );
1413}
1414
1415bool QgsSimpleMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
1416{
1417 return QgsSimpleMarkerSymbolLayerBase::toSld( doc, element, context );
1418}
1419
1420void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1421{
1422 QgsSldExportContext context;
1423 context.setExtraProperties( props );
1424 writeSldMarker( doc, element, context );
1425}
1426
1427bool QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
1428{
1429 // <Graphic>
1430 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
1431 element.appendChild( graphicElem );
1432
1433 const QVariantMap props = context.extraProperties();
1435 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
1437
1438 // <Rotation>
1439 QString angleFunc;
1440
1442 {
1444 }
1445 else
1446 {
1447 bool ok;
1448 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
1449 if ( !ok )
1450 {
1451 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
1452 }
1453 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
1454 {
1455 angleFunc = QString::number( angle + mAngle );
1456 }
1457 }
1458
1459 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc, context );
1460
1461 // <Displacement>
1462 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
1464 return true;
1465}
1466
1467QString QgsSimpleMarkerSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
1468{
1469 Q_UNUSED( mmScaleFactor )
1470 Q_UNUSED( mapUnitScaleFactor )
1471#if 0
1472 QString ogrType = "3"; //default is circle
1473 if ( mName == "square" )
1474 {
1475 ogrType = "5";
1476 }
1477 else if ( mName == "triangle" )
1478 {
1479 ogrType = "7";
1480 }
1481 else if ( mName == "star" )
1482 {
1483 ogrType = "9";
1484 }
1485 else if ( mName == "circle" )
1486 {
1487 ogrType = "3";
1488 }
1489 else if ( mName == "cross" )
1490 {
1491 ogrType = "0";
1492 }
1493 else if ( mName == "x" || mName == "cross2" )
1494 {
1495 ogrType = "1";
1496 }
1497 else if ( mName == "line" )
1498 {
1499 ogrType = "10";
1500 }
1501
1502 QString ogrString;
1503 ogrString.append( "SYMBOL(" );
1504 ogrString.append( "id:" );
1505 ogrString.append( '\"' );
1506 ogrString.append( "ogr-sym-" );
1507 ogrString.append( ogrType );
1508 ogrString.append( '\"' );
1509 ogrString.append( ",c:" );
1510 ogrString.append( mColor.name() );
1511 ogrString.append( ",o:" );
1512 ogrString.append( mStrokeColor.name() );
1513 ogrString.append( QString( ",s:%1mm" ).arg( mSize ) );
1514 ogrString.append( ')' );
1515 return ogrString;
1516#endif //0
1517
1518 QString ogrString;
1519 ogrString.append( "PEN(" );
1520 ogrString.append( "c:" );
1521 ogrString.append( mColor.name() );
1522 ogrString.append( ",w:" );
1523 ogrString.append( QString::number( mSize ) );
1524 ogrString.append( "mm" );
1525 ogrString.append( ")" );
1526 return ogrString;
1527}
1528
1530{
1531 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
1532
1533 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
1534 if ( graphicElem.isNull() )
1535 return nullptr;
1536
1537 QString name = QStringLiteral( "square" );
1538 QColor color, strokeColor;
1539 double strokeWidth, size;
1540 Qt::PenStyle strokeStyle;
1541
1543 return nullptr;
1544
1545 double angle = 0.0;
1546 QString angleFunc;
1547 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
1548 {
1549 bool ok;
1550 const double d = angleFunc.toDouble( &ok );
1551 if ( ok )
1552 angle = d;
1553 }
1554
1555 QPointF offset;
1557
1558 const Qgis::MarkerShape shape = decodeShape( name );
1559
1560 double scaleFactor = 1.0;
1561 const QString uom = element.attribute( QStringLiteral( "uom" ) );
1562 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
1563 size = size * scaleFactor;
1564 offset.setX( offset.x() * scaleFactor );
1565 offset.setY( offset.y() * scaleFactor );
1566
1568 m->setOutputUnit( sldUnitSize );
1569 m->setColor( color );
1571 m->setAngle( angle );
1572 m->setOffset( offset );
1575 return m;
1576}
1577
1579{
1580 Q_UNUSED( context )
1581
1582 if ( mPolygon.count() != 0 )
1583 {
1584 p->drawPolygon( mPolygon );
1585 }
1586 else
1587 {
1588 p->drawPath( mPath );
1589 }
1590}
1591
1592bool QgsSimpleMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
1593{
1594 //data defined size?
1595 double size = mSize;
1596
1597 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
1598
1599 //data defined size
1600 bool ok = true;
1601 if ( hasDataDefinedSize )
1602 {
1604
1605 if ( ok )
1606 {
1607 switch ( mScaleMethod )
1608 {
1610 size = std::sqrt( size );
1611 break;
1613 break;
1614 }
1615 }
1616 }
1617
1619 {
1620 size *= mmMapUnitScaleFactor;
1621 }
1622
1624 {
1626 }
1627 const double halfSize = size / 2.0;
1628
1629 //strokeWidth
1630 double strokeWidth = mStrokeWidth;
1631
1633 {
1636 }
1639 {
1641 }
1642
1643 //color
1644 QColor pc = mPen.color();
1645 QColor bc = mBrush.color();
1647 {
1650 }
1652 {
1655 }
1656
1657 //offset
1658 double offsetX = 0;
1659 double offsetY = 0;
1660 markerOffset( context, offsetX, offsetY );
1661 offsetX *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1662 offsetY *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1663
1664
1665 QPointF off( offsetX, offsetY );
1666
1667 //angle
1668 double angle = mAngle + mLineAngle;
1670 {
1673 }
1674
1677 {
1679 const QString shapeName = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::Name, context.renderContext().expressionContext(), QString(), &ok );
1680 if ( ok )
1681 {
1682 shape = decodeShape( shapeName, &ok );
1683 if ( !ok )
1684 shape = mShape;
1685 }
1686 }
1687
1688 if ( angle )
1689 off = _rotatedOffset( off, angle );
1690
1692
1693 QTransform t;
1694 t.translate( shift.x() + off.x(), shift.y() - off.y() );
1695
1696 if ( !qgsDoubleNear( angle, 0.0 ) )
1697 t.rotate( -angle );
1698
1699 QPolygonF polygon;
1700 if ( shapeToPolygon( shape, polygon ) )
1701 {
1702 t.scale( halfSize, -halfSize );
1703
1704 polygon = t.map( polygon );
1705
1707 p.reserve( polygon.size() );
1708 for ( int i = 0; i < polygon.size(); i++ )
1709 {
1710 p << QgsPoint( polygon[i] );
1711 }
1712
1713 if ( mBrush.style() != Qt::NoBrush )
1714 e.writePolygon( QgsRingSequence() << p, layerName, QStringLiteral( "SOLID" ), bc );
1715 if ( mPen.style() != Qt::NoPen )
1716 e.writePolyline( p, layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1717 }
1718 else if ( shape == Qgis::MarkerShape::Circle )
1719 {
1720 shift += QPointF( off.x(), -off.y() );
1721 if ( mBrush.style() != Qt::NoBrush )
1722 e.writeFilledCircle( layerName, bc, QgsPoint( shift ), halfSize );
1723 if ( mPen.style() != Qt::NoPen )
1724 e.writeCircle( layerName, pc, QgsPoint( shift ), halfSize, QStringLiteral( "CONTINUOUS" ), strokeWidth );
1725 }
1726 else if ( shape == Qgis::MarkerShape::Line )
1727 {
1728 const QPointF pt1 = t.map( QPointF( 0, -halfSize ) );
1729 const QPointF pt2 = t.map( QPointF( 0, halfSize ) );
1730
1731 if ( mPen.style() != Qt::NoPen )
1732 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1733 }
1734 else if ( shape == Qgis::MarkerShape::Cross )
1735 {
1736 if ( mPen.style() != Qt::NoPen )
1737 {
1738 const QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
1739 const QPointF pt2 = t.map( QPointF( halfSize, 0 ) );
1740 const QPointF pt3 = t.map( QPointF( 0, -halfSize ) );
1741 const QPointF pt4 = t.map( QPointF( 0, halfSize ) );
1742
1743 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1744 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1745 }
1746 }
1747 else if ( shape == Qgis::MarkerShape::Cross2 )
1748 {
1749 if ( mPen.style() != Qt::NoPen )
1750 {
1751 const QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
1752 const QPointF pt2 = t.map( QPointF( halfSize, halfSize ) );
1753 const QPointF pt3 = t.map( QPointF( halfSize, -halfSize ) );
1754 const QPointF pt4 = t.map( QPointF( -halfSize, halfSize ) );
1755
1756 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1757 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1758 }
1759 }
1760 else if ( shape == Qgis::MarkerShape::ArrowHead )
1761 {
1762 if ( mPen.style() != Qt::NoPen )
1763 {
1764 const QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
1765 const QPointF pt2 = t.map( QPointF( 0, 0 ) );
1766 const QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
1767
1768 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1769 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1770 }
1771 }
1772 else
1773 {
1774 QgsDebugError( QStringLiteral( "Unsupported dxf marker name %1" ).arg( encodeShape( shape ) ) );
1775 return false;
1776 }
1777
1778 return true;
1779}
1780
1781
1787
1796
1802
1811
1818
1820{
1821 QRectF symbolBounds = QgsSimpleMarkerSymbolLayerBase::bounds( point, context );
1822
1823 // need to account for stroke width
1824 double penWidth = mStrokeWidth;
1825 bool ok = true;
1827 {
1830 if ( ok )
1831 {
1832 penWidth = strokeWidth;
1833 }
1834 }
1837 {
1840 if ( ok && strokeStyle == QLatin1String( "no" ) )
1841 {
1842 penWidth = 0.0;
1843 }
1844 }
1845 else if ( mStrokeStyle == Qt::NoPen )
1846 penWidth = 0;
1847
1848 //antialiasing, add 1 pixel
1849 penWidth += 1;
1850
1851 //extend bounds by pen width / 2.0
1852 symbolBounds.adjust( -penWidth / 2.0, -penWidth / 2.0,
1853 penWidth / 2.0, penWidth / 2.0 );
1854
1855 return symbolBounds;
1856}
1857
1858void QgsSimpleMarkerSymbolLayer::setColor( const QColor &color )
1859{
1860 if ( shapeIsFilled( mShape ) )
1861 {
1863 }
1864 else
1865 {
1867 }
1868}
1869
1871{
1872 if ( shapeIsFilled( mShape ) )
1873 {
1874 return fillColor();
1875 }
1876 else
1877 {
1878 return strokeColor();
1879 }
1880}
1881
1882
1883
1884
1885//
1886// QgsFilledMarkerSymbolLayer
1887//
1888
1890 : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
1891{
1892 mFill = QgsFillSymbol::createSimple( QVariantMap() );
1893}
1894
1896
1898{
1899 QString name = DEFAULT_SIMPLEMARKER_NAME;
1903
1904 if ( props.contains( QStringLiteral( "name" ) ) )
1905 name = props[QStringLiteral( "name" )].toString();
1906 if ( props.contains( QStringLiteral( "size" ) ) )
1907 size = props[QStringLiteral( "size" )].toDouble();
1908 if ( props.contains( QStringLiteral( "angle" ) ) )
1909 angle = props[QStringLiteral( "angle" )].toDouble();
1910 if ( props.contains( QStringLiteral( "scale_method" ) ) )
1911 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1912
1914 if ( props.contains( QStringLiteral( "offset" ) ) )
1915 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1916 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1917 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1918 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1919 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1920 if ( props.contains( QStringLiteral( "size_unit" ) ) )
1921 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1922 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1923 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1924 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1925 {
1926 m->setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1927 }
1928 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1929 {
1930 m->setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1931 }
1932
1933 m->setSubSymbol( QgsFillSymbol::createSimple( props ).release() );
1934
1936
1937 return m;
1938}
1939
1941{
1942 return QStringLiteral( "FilledMarker" );
1943}
1944
1946{
1947 if ( mFill )
1948 {
1949 mFill->setRenderHints( mFill->renderHints() | Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol );
1950 mFill->startRender( context.renderContext(), context.fields() );
1951 }
1952
1954}
1955
1957{
1958 if ( mFill )
1959 {
1960 mFill->stopRender( context.renderContext() );
1961 }
1962}
1963
1965{
1966 installMasks( context, true );
1967
1968 // The base class version passes this on to the subsymbol, but we deliberately don't do that here.
1969}
1970
1972{
1973 removeMasks( context, true );
1974
1975 // The base class version passes this on to the subsymbol, but we deliberately don't do that here.
1976}
1977
1979{
1980 QVariantMap map;
1981 map[QStringLiteral( "name" )] = encodeShape( mShape );
1982 map[QStringLiteral( "size" )] = QString::number( mSize );
1983 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1984 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1985 map[QStringLiteral( "angle" )] = QString::number( mAngle );
1986 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1987 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1988 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1989 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1990 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
1991 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
1992
1993 if ( mFill )
1994 {
1995 map[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mFill->color() );
1996 }
1997 return map;
1998}
1999
2008
2010{
2011 return mFill.get();
2012}
2013
2015{
2016 if ( symbol && symbol->type() == Qgis::SymbolType::Fill )
2017 {
2018 mFill.reset( static_cast<QgsFillSymbol *>( symbol ) );
2019 return true;
2020 }
2021 else
2022 {
2023 delete symbol;
2024 return false;
2025 }
2026}
2027
2029{
2030 if ( mFill )
2031 {
2032 return QgsSymbolLayerUtils::estimateMaxSymbolBleed( mFill.get(), context );
2033 }
2034 return 0;
2035}
2036
2038{
2039 QSet<QString> attr = QgsSimpleMarkerSymbolLayerBase::usedAttributes( context );
2040 if ( mFill )
2041 attr.unite( mFill->usedAttributes( context ) );
2042 return attr;
2043}
2044
2046{
2048 return true;
2049 if ( mFill && mFill->hasDataDefinedProperties() )
2050 return true;
2051 return false;
2052}
2053
2055{
2056 mColor = c;
2057 if ( mFill )
2058 mFill->setColor( c );
2059}
2060
2062{
2063 return mFill ? mFill->color() : mColor;
2064}
2065
2072
2074{
2076 if ( mFill )
2077 mFill->setOutputUnit( unit );
2078}
2079
2080void QgsFilledMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
2081{
2082 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
2083 //of the rendered point!
2084
2085 QPainter *p = context.renderContext().painter();
2086 if ( !p )
2087 {
2088 return;
2089 }
2090
2091 const double prevOpacity = mFill->opacity();
2092 mFill->setOpacity( mFill->opacity() * context.opacity() );
2093
2094 if ( shapeIsFilled( shape ) )
2095 {
2096 p->setBrush( Qt::red );
2097 }
2098 else
2099 {
2100 p->setBrush( Qt::NoBrush );
2101 }
2102 p->setPen( Qt::black );
2103
2104 const bool prevIsSubsymbol = context.renderContext().flags() & Qgis::RenderContextFlag::RenderingSubSymbol;
2106
2107 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
2108 if ( !polygon.isEmpty() )
2109 {
2110 mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
2111 }
2112 else
2113 {
2114 const QPolygonF poly = path.toFillPolygon();
2115 mFill->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
2116 }
2117
2119
2120 mFill->setOpacity( prevOpacity );
2121}
2122
2123
2125
2126
2127QgsSvgMarkerSymbolLayer::QgsSvgMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
2128{
2129 mSize = size;
2130 mAngle = angle;
2131 mOffset = QPointF( 0, 0 );
2133 mStrokeWidth = 0.2;
2135 mColor = QColor( 35, 35, 35 );
2136 mStrokeColor = QColor( 35, 35, 35 );
2137 setPath( path );
2138}
2139
2141 : QgsMarkerSymbolLayer( other )
2142 , mPath( other.mPath )
2143 , mDefaultAspectRatio( other.mDefaultAspectRatio )
2144 , mFixedAspectRatio( other.mFixedAspectRatio )
2145 , mHasFillParam( other.mHasFillParam )
2146 , mStrokeColor( other.mStrokeColor )
2147 , mStrokeWidth( other.mStrokeWidth )
2148 , mParameters( other.mParameters )
2149 , mStrokeWidthUnit( other.mStrokeWidthUnit )
2150 , mStrokeWidthMapUnitScale( other.mStrokeWidthMapUnitScale )
2151{
2152}
2153
2155
2157{
2158 QString name;
2162
2163 if ( props.contains( QStringLiteral( "name" ) ) )
2164 name = props[QStringLiteral( "name" )].toString();
2165 if ( props.contains( QStringLiteral( "size" ) ) )
2166 size = props[QStringLiteral( "size" )].toDouble();
2167 if ( props.contains( QStringLiteral( "angle" ) ) )
2168 angle = props[QStringLiteral( "angle" )].toDouble();
2169 if ( props.contains( QStringLiteral( "scale_method" ) ) )
2170 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
2171
2173
2174 if ( props.contains( QStringLiteral( "size_unit" ) ) )
2175 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
2176 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
2177 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
2178 if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
2179 m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
2180 if ( props.contains( QStringLiteral( "offset" ) ) )
2181 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
2182 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
2183 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
2184 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2185 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
2186 if ( props.contains( QStringLiteral( "fill" ) ) )
2187 {
2188 //pre 2.5 projects used "fill"
2189 m->setFillColor( QgsColorUtils::colorFromString( props[QStringLiteral( "fill" )].toString() ) );
2190 }
2191 else if ( props.contains( QStringLiteral( "color" ) ) )
2192 {
2193 m->setFillColor( QgsColorUtils::colorFromString( props[QStringLiteral( "color" )].toString() ) );
2194 }
2195 if ( props.contains( QStringLiteral( "outline" ) ) )
2196 {
2197 //pre 2.5 projects used "outline"
2198 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "outline" )].toString() ) );
2199 }
2200 else if ( props.contains( QStringLiteral( "outline_color" ) ) )
2201 {
2202 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "outline_color" )].toString() ) );
2203 }
2204 else if ( props.contains( QStringLiteral( "line_color" ) ) )
2205 {
2206 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "line_color" )].toString() ) );
2207 }
2208
2209 if ( props.contains( QStringLiteral( "outline-width" ) ) )
2210 {
2211 //pre 2.5 projects used "outline-width"
2212 m->setStrokeWidth( props[QStringLiteral( "outline-width" )].toDouble() );
2213 }
2214 else if ( props.contains( QStringLiteral( "outline_width" ) ) )
2215 {
2216 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
2217 }
2218 else if ( props.contains( QStringLiteral( "line_width" ) ) )
2219 {
2220 m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
2221 }
2222
2223 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
2224 {
2225 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
2226 }
2227 else if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
2228 {
2229 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
2230 }
2231 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
2232 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
2233
2234 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2235 {
2236 m->setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2237 }
2238 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2239 {
2240 m->setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2241 }
2242
2244
2246
2247 if ( props.contains( QStringLiteral( "parameters" ) ) )
2248 {
2249 const QVariantMap parameters = props[QStringLiteral( "parameters" )].toMap();
2251 }
2252
2253 return m;
2254}
2255
2256void QgsSvgMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
2257{
2258 const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
2259 if ( it != properties.end() )
2260 {
2261 if ( saving )
2262 {
2263 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
2264 }
2265 else
2266 {
2267 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
2268 }
2269 }
2270}
2271
2272void QgsSvgMarkerSymbolLayer::setPath( const QString &path )
2273{
2275 mHasFillParam = false;
2276 mPath = path;
2277 QColor defaultFillColor, defaultStrokeColor;
2278 double strokeWidth, fillOpacity, strokeOpacity;
2279 bool hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
2280 bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
2281 QgsApplication::svgCache()->containsParams( path, mHasFillParam, hasDefaultFillColor, defaultFillColor,
2282 hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
2283 hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2284 hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
2285 hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
2286
2287 const double newFillOpacity = hasFillOpacityParam ? fillColor().alphaF() : 1.0;
2288 const double newStrokeOpacity = hasStrokeOpacityParam ? strokeColor().alphaF() : 1.0;
2289
2290 if ( hasDefaultFillColor )
2291 {
2292 defaultFillColor.setAlphaF( newFillOpacity );
2293 setFillColor( defaultFillColor );
2294 }
2295 if ( hasDefaultFillOpacity )
2296 {
2297 QColor c = fillColor();
2298 c.setAlphaF( fillOpacity );
2299 setFillColor( c );
2300 }
2301 if ( hasDefaultStrokeColor )
2302 {
2303 defaultStrokeColor.setAlphaF( newStrokeOpacity );
2304 setStrokeColor( defaultStrokeColor );
2305 }
2306 if ( hasDefaultStrokeWidth )
2307 {
2309 }
2310 if ( hasDefaultStrokeOpacity )
2311 {
2312 QColor c = strokeColor();
2313 c.setAlphaF( strokeOpacity );
2314 setStrokeColor( c );
2315 }
2316
2318}
2319
2321{
2322 if ( mDefaultAspectRatio == 0.0 )
2323 {
2324 //size
2325 const double size = mSize;
2326 //assume 88 dpi as standard value
2327 const double widthScaleFactor = 3.465;
2328 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( mPath, size, mColor, mStrokeColor, mStrokeWidth, widthScaleFactor );
2329 // set default aspect ratio
2330 mDefaultAspectRatio = svgViewbox.isValid() ? svgViewbox.height() / svgViewbox.width() : 0.0;
2331 }
2332 return mDefaultAspectRatio;
2333}
2334
2336{
2337 const bool aPreservedAspectRatio = preservedAspectRatio();
2338 if ( aPreservedAspectRatio && !par )
2339 {
2341 }
2342 else if ( !aPreservedAspectRatio && par )
2343 {
2344 mFixedAspectRatio = 0.0;
2345 }
2346 return preservedAspectRatio();
2347}
2348
2349void QgsSvgMarkerSymbolLayer::setParameters( const QMap<QString, QgsProperty> &parameters )
2350{
2352}
2353
2354
2356{
2357 return QStringLiteral( "SvgMarker" );
2358}
2359
2364
2366{
2367 QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
2368 Q_UNUSED( context )
2369}
2370
2372{
2373 Q_UNUSED( context )
2374}
2375
2377{
2378 QPainter *p = context.renderContext().painter();
2379 if ( !p )
2380 return;
2381
2382 bool hasDataDefinedSize = false;
2383 const double scaledWidth = calculateSize( context, hasDataDefinedSize );
2384 const double devicePixelRatio = context.renderContext().devicePixelRatio();
2385 const double width = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2386
2387 //don't render symbols with a width below one or above 10,000 pixels
2388 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2389 {
2390 return;
2391 }
2392
2393 const QgsScopedQPainterState painterState( p );
2394
2395 bool hasDataDefinedAspectRatio = false;
2396 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2397 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2398
2400
2401 double strokeWidth = mStrokeWidth;
2403 {
2406 }
2408
2409 QColor fillColor = mColor;
2410 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
2411 if ( useSelectedColor && mHasFillParam )
2412 {
2414 }
2416 {
2419 }
2420
2421 QColor strokeColor = mStrokeColor;
2423 {
2426 }
2427
2428 QString path = mPath;
2430 {
2433 context.renderContext().pathResolver() );
2435 {
2436 // adjust height of data defined path
2437 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2438 context.renderContext().scaleFactor(), aspectRatio,
2439 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2440 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2441 }
2442 }
2443
2444 QPointF outputOffset;
2445 double angle = 0.0;
2446 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2447
2448 p->translate( point + outputOffset );
2449
2450 const bool rotated = !qgsDoubleNear( angle, 0 );
2451 if ( rotated )
2452 p->rotate( angle );
2453
2454 bool fitsInCache = true;
2455 bool usePict = true;
2457 if ( ( !context.forceVectorRendering() && !rotated ) || ( useSelectedColor && rasterizeSelected ) )
2458 {
2459 QImage img = QgsApplication::svgCache()->svgAsImage( path, width * devicePixelRatio, fillColor, strokeColor, strokeWidth,
2460 context.renderContext().scaleFactor(), fitsInCache, aspectRatio,
2461 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2462 if ( fitsInCache && img.width() > 1 )
2463 {
2464 usePict = false;
2465
2466 if ( useSelectedColor )
2467 {
2469 }
2470
2471 //consider transparency
2472 if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2473 {
2474 QImage transparentImage = img.copy();
2475 QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2476 if ( devicePixelRatio == 1 )
2477 {
2478 p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
2479 }
2480 else
2481 {
2482 p->drawImage( QRectF( -transparentImage.width() / 2.0 / devicePixelRatio, -transparentImage.height() / 2.0 / devicePixelRatio,
2483 transparentImage.width() / devicePixelRatio, transparentImage.height() / devicePixelRatio
2484 ), transparentImage );
2485 }
2486 }
2487 else
2488 {
2489 if ( devicePixelRatio == 1 )
2490 {
2491 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2492 }
2493 else
2494 {
2495 p->drawImage( QRectF( -img.width() / 2.0 / devicePixelRatio, -img.height() / 2.0 / devicePixelRatio,
2496 img.width() / devicePixelRatio, img.height() / devicePixelRatio ), img );
2497 }
2498 }
2499 }
2500 }
2501
2502 if ( usePict || !fitsInCache )
2503 {
2504 p->setOpacity( context.opacity() );
2506 context.renderContext().scaleFactor(), context.forceVectorRendering(), aspectRatio,
2507 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2508 if ( pct.width() > 1 )
2509 {
2510 QgsPainting::drawPicture( p, QPointF( 0, 0 ), pct );
2511 }
2512 }
2513
2514 // workaround issue with nested QPictures forgetting antialiasing flag - see https://github.com/qgis/QGIS/issues/22909
2516}
2517
2518double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2519{
2520 double scaledSize = mSize;
2522
2523 bool ok = true;
2524 if ( hasDataDefinedSize )
2525 {
2528 }
2529 else
2530 {
2532 if ( hasDataDefinedSize )
2533 {
2536 }
2537 }
2538
2539 if ( hasDataDefinedSize && ok )
2540 {
2541 switch ( mScaleMethod )
2542 {
2544 scaledSize = std::sqrt( scaledSize );
2545 break;
2547 break;
2548 }
2549 }
2550
2551 return scaledSize;
2552}
2553
2554double QgsSvgMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2555{
2557 if ( !hasDataDefinedAspectRatio )
2558 return mFixedAspectRatio;
2559
2561 return 0.0;
2562
2563 double scaledAspectRatio = mDefaultAspectRatio;
2564 if ( mFixedAspectRatio > 0.0 )
2565 scaledAspectRatio = mFixedAspectRatio;
2566
2567 const double defaultHeight = mSize * scaledAspectRatio;
2568 scaledAspectRatio = defaultHeight / scaledSize;
2569
2570 bool ok = true;
2571 double scaledHeight = scaledSize * scaledAspectRatio;
2573 {
2574 context.setOriginalValueVariable( defaultHeight );
2576 }
2577
2578 if ( hasDataDefinedAspectRatio && ok )
2579 {
2580 switch ( mScaleMethod )
2581 {
2583 scaledHeight = sqrt( scaledHeight );
2584 break;
2586 break;
2587 }
2588 }
2589
2590 scaledAspectRatio = scaledHeight / scaledSize;
2591
2592 return scaledAspectRatio;
2593}
2594
2595void QgsSvgMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
2596{
2597 //offset
2598 double offsetX = 0;
2599 double offsetY = 0;
2600 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
2601 offset = QPointF( offsetX, offsetY );
2602
2605 {
2608 }
2609
2611 if ( hasDataDefinedRotation )
2612 {
2613 // For non-point markers, "dataDefinedRotation" means following the
2614 // shape (shape-data defined). For them, "field-data defined" does
2615 // not work at all. TODO: if "field-data defined" ever gets implemented
2616 // we'll need a way to distinguish here between the two, possibly
2617 // using another flag in renderHints()
2618 const QgsFeature *f = context.feature();
2619 if ( f )
2620 {
2621 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
2622 {
2623 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2624 angle += m2p.mapRotation();
2625 }
2626 }
2627 }
2628
2629 if ( angle )
2631}
2632
2633
2635{
2636 QVariantMap map;
2637 map[QStringLiteral( "name" )] = mPath;
2638 map[QStringLiteral( "size" )] = QString::number( mSize );
2639 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2640 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2641 map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2642 map[QStringLiteral( "angle" )] = QString::number( mAngle );
2643 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2644 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2645 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2646 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2647 map[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mColor );
2648 map[QStringLiteral( "outline_color" )] = QgsColorUtils::colorToString( mStrokeColor );
2649 map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
2650 map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2651 map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2652 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
2653 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
2654
2655 map[QStringLiteral( "parameters" )] = QgsProperty::propertyMapToVariantMap( mParameters );
2656
2657 return map;
2658}
2659
2666
2671
2672void QgsSvgMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
2673{
2674 QgsSldExportContext context;
2675 context.setExtraProperties( props );
2676 toSld( doc, element, context );
2677}
2678
2679bool QgsSvgMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
2680{
2681 return QgsMarkerSymbolLayer::toSld( doc, element, context );
2682}
2683
2689
2691{
2693 if ( unit != mStrokeWidthUnit )
2694 {
2696 }
2697 return unit;
2698}
2699
2705
2714
2715void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
2716{
2717 QgsSldExportContext context;
2718 context.setExtraProperties( props );
2719 writeSldMarker( doc, element, context );
2720}
2721
2722bool QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
2723{
2724 // <Graphic>
2725 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2726 element.appendChild( graphicElem );
2727
2728 const QVariantMap props = context.extraProperties();
2729 // encode a parametric SVG reference
2730 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
2733
2734 // <Rotation>
2735 QString angleFunc;
2736 bool ok;
2737 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2738 if ( !ok )
2739 {
2740 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
2741 }
2742 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2743 {
2744 angleFunc = QString::number( angle + mAngle );
2745 }
2746
2747 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc, context );
2748
2749 // <Displacement>
2750 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2752 return true;
2753}
2754
2756{
2757 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
2758
2759 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
2760 if ( graphicElem.isNull() )
2761 return nullptr;
2762
2763 QString path, mimeType;
2764 // Unused and to be DEPRECATED in externalGraphicFromSld
2765 QColor fillColor_;
2766 double size;
2767
2768 if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor_, size ) )
2769 return nullptr;
2770
2771 double scaleFactor = 1.0;
2772 const QString uom = element.attribute( QStringLiteral( "uom" ) );
2773 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
2774 size = size * scaleFactor;
2775
2776 if ( mimeType != QLatin1String( "image/svg+xml" ) )
2777 return nullptr;
2778
2779 double angle = 0.0;
2780 QString angleFunc;
2781 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2782 {
2783 bool ok;
2784 const double d = angleFunc.toDouble( &ok );
2785 if ( ok )
2786 angle = d;
2787 }
2788
2789 QPointF offset;
2791
2792 // Extract parameters from URL
2793 QString realPath { path };
2794 QUrl svgUrl { path };
2795
2796 // Because color definition can start with '#', the url parsing won't recognize the query string entirely
2797 QUrlQuery queryString;
2798
2799 if ( svgUrl.hasQuery() && svgUrl.hasFragment() )
2800 {
2801 const QString queryPart { path.mid( path.indexOf( '?' ) + 1 ) };
2802 queryString.setQuery( queryPart );
2803 }
2804
2805 // Remove query for simple file paths
2806 if ( svgUrl.scheme().isEmpty() || svgUrl.isLocalFile() )
2807 {
2808 svgUrl.setQuery( QString() );
2809 realPath = svgUrl.path();
2810 }
2811
2813
2814 QMap<QString, QgsProperty> params;
2815
2816 bool ok;
2817
2818 if ( queryString.hasQueryItem( QStringLiteral( "fill" ) ) )
2819 {
2820 const QColor fillColor { queryString.queryItemValue( QStringLiteral( "fill" ) ) };
2821 m->setFillColor( fillColor );
2822 }
2823
2824 if ( queryString.hasQueryItem( QStringLiteral( "fill-opacity" ) ) )
2825 {
2826 const double alpha { queryString.queryItemValue( QStringLiteral( "fill-opacity" ) ).toDouble( &ok ) };
2827 if ( ok )
2828 {
2829 params.insert( QStringLiteral( "fill-opacity" ), QgsProperty::fromValue( alpha ) );
2830 }
2831 }
2832
2833 if ( queryString.hasQueryItem( QStringLiteral( "outline" ) ) )
2834 {
2835 const QColor strokeColor { queryString.queryItemValue( QStringLiteral( "outline" ) ) };
2837 }
2838
2839 if ( queryString.hasQueryItem( QStringLiteral( "outline-opacity" ) ) )
2840 {
2841 const double alpha { queryString.queryItemValue( QStringLiteral( "outline-opacity" ) ).toDouble( &ok ) };
2842 if ( ok )
2843 {
2844 params.insert( QStringLiteral( "outline-opacity" ), QgsProperty::fromValue( alpha ) );
2845 }
2846 }
2847
2848 if ( queryString.hasQueryItem( QStringLiteral( "outline-width" ) ) )
2849 {
2850 const int width { queryString.queryItemValue( QStringLiteral( "outline-width" ) ).toInt( &ok )};
2851 if ( ok )
2852 {
2853 m->setStrokeWidth( width );
2854 }
2855 }
2856
2857 if ( ! params.isEmpty() )
2858 {
2859 m->setParameters( params );
2860 }
2861
2862 m->setOutputUnit( sldUnitSize );
2863 m->setAngle( angle );
2864 m->setOffset( offset );
2865 return m;
2866}
2867
2868bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
2869{
2870 //size
2871 double size = mSize;
2872
2873 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
2874
2875 bool ok = true;
2876 if ( hasDataDefinedSize )
2877 {
2880 }
2881
2882 if ( hasDataDefinedSize && ok )
2883 {
2884 switch ( mScaleMethod )
2885 {
2887 size = std::sqrt( size );
2888 break;
2890 break;
2891 }
2892 }
2893
2895 {
2896 size *= mmMapUnitScaleFactor;
2897 }
2898
2899//offset, angle
2900 QPointF offset = mOffset;
2901
2903 {
2906 const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
2907 if ( ok )
2908 offset = res;
2909 }
2910 const double offsetX = offset.x();
2911 const double offsetY = offset.y();
2912
2913 QPointF outputOffset( offsetX, offsetY );
2914
2915 double angle = mAngle + mLineAngle;
2917 {
2920 }
2921
2922 if ( angle )
2923 outputOffset = _rotatedOffset( outputOffset, angle );
2924
2926
2927 QString path = mPath;
2929 {
2932 context.renderContext().pathResolver() );
2933 }
2934
2935 double strokeWidth = mStrokeWidth;
2937 {
2940 }
2942
2943 QColor fillColor = mColor;
2945 {
2948 }
2949
2950 QColor strokeColor = mStrokeColor;
2952 {
2955 }
2956
2958
2959 const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, size, fillColor, strokeColor, strokeWidth,
2961 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2962
2963 QSvgRenderer r( svgContent );
2964 if ( !r.isValid() )
2965 return false;
2966
2967 QgsDxfPaintDevice pd( &e );
2968 pd.setDrawingSize( QSizeF( r.defaultSize() ) );
2969
2970 QSizeF outSize( r.defaultSize() );
2971 outSize.scale( size, size, Qt::KeepAspectRatio );
2972
2973 QPainter p;
2974 p.begin( &pd );
2975 if ( !qgsDoubleNear( angle, 0.0 ) )
2976 {
2977 p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
2978 p.rotate( angle );
2979 p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
2980 }
2981 pd.setShift( shift + QPointF( outputOffset.x(), -outputOffset.y() ) );
2982 pd.setOutputSize( QRectF( -outSize.width() / 2.0, -outSize.height() / 2.0, outSize.width(), outSize.height() ) );
2983 pd.setLayer( layerName );
2984 r.render( &p );
2985 p.end();
2986 return true;
2987}
2988
2990{
2991 bool hasDataDefinedSize = false;
2992 double scaledWidth = calculateSize( context, hasDataDefinedSize );
2993
2994 bool hasDataDefinedAspectRatio = false;
2995 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2996 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2997
2998 scaledWidth = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2999 scaledHeight = context.renderContext().convertToPainterUnits( scaledHeight, mSizeUnit, mSizeMapUnitScale );
3000
3001 //don't render symbols with size below one or above 10,000 pixels
3002 if ( static_cast< int >( scaledWidth ) < 1 || 10000.0 < scaledWidth )
3003 {
3004 return QRectF();
3005 }
3006
3007 QPointF outputOffset;
3008 double angle = 0.0;
3009 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
3010
3011 double strokeWidth = mStrokeWidth;
3013 {
3016 }
3018
3019 QString path = mPath;
3021 {
3024 context.renderContext().pathResolver() );
3026 {
3027 // need to get colors to take advantage of cached SVGs
3028 QColor fillColor = mColor;
3030 {
3033 }
3034
3035 const QColor strokeColor = mStrokeColor;
3037 {
3040 }
3041
3043
3044 // adjust height of data defined path
3045 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
3046 context.renderContext().scaleFactor(), aspectRatio,
3047 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
3048 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
3049 }
3050 }
3051
3052 QTransform transform;
3053 // move to the desired position
3054 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3055
3056 if ( !qgsDoubleNear( angle, 0.0 ) )
3057 transform.rotate( angle );
3058
3059 //antialiasing
3060 strokeWidth += 1.0 / 2.0;
3061
3062 QRectF symbolBounds = transform.mapRect( QRectF( -scaledWidth / 2.0,
3063 -scaledHeight / 2.0,
3064 scaledWidth,
3065 scaledHeight ) );
3066
3067 //extend bounds by pen width / 2.0
3068 symbolBounds.adjust( -strokeWidth / 2.0, -strokeWidth / 2.0,
3069 strokeWidth / 2.0, strokeWidth / 2.0 );
3070
3071 return symbolBounds;
3072}
3073
3075
3076QgsRasterMarkerSymbolLayer::QgsRasterMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
3077 : mPath( path )
3078{
3079 mSize = size;
3080 mAngle = angle;
3081 mOffset = QPointF( 0, 0 );
3084}
3085
3087
3089{
3090 QString path;
3094
3095 if ( props.contains( QStringLiteral( "imageFile" ) ) )
3096 path = props[QStringLiteral( "imageFile" )].toString();
3097 if ( props.contains( QStringLiteral( "size" ) ) )
3098 size = props[QStringLiteral( "size" )].toDouble();
3099 if ( props.contains( QStringLiteral( "angle" ) ) )
3100 angle = props[QStringLiteral( "angle" )].toDouble();
3101 if ( props.contains( QStringLiteral( "scale_method" ) ) )
3102 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
3103
3104 auto m = std::make_unique< QgsRasterMarkerSymbolLayer >( path, size, angle, scaleMethod );
3105 m->setCommonProperties( props );
3106 return m.release();
3107}
3108
3109void QgsRasterMarkerSymbolLayer::setCommonProperties( const QVariantMap &properties )
3110{
3111 if ( properties.contains( QStringLiteral( "alpha" ) ) )
3112 {
3113 setOpacity( properties[QStringLiteral( "alpha" )].toDouble() );
3114 }
3115
3116 if ( properties.contains( QStringLiteral( "size_unit" ) ) )
3117 setSizeUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "size_unit" )].toString() ) );
3118 if ( properties.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3119 setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3120 if ( properties.contains( QStringLiteral( "fixedAspectRatio" ) ) )
3121 setFixedAspectRatio( properties[QStringLiteral( "fixedAspectRatio" )].toDouble() );
3122
3123 if ( properties.contains( QStringLiteral( "offset" ) ) )
3124 setOffset( QgsSymbolLayerUtils::decodePoint( properties[QStringLiteral( "offset" )].toString() ) );
3125 if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
3126 setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )].toString() ) );
3127 if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3128 setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3129
3130 if ( properties.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3131 {
3132 setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( properties[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3133 }
3134 if ( properties.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3135 {
3136 setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( properties[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3137 }
3138
3141}
3142
3143void QgsRasterMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
3144{
3145 const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
3146 if ( it != properties.end() && it.value().userType() == QMetaType::Type::QString )
3147 {
3148 if ( saving )
3149 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
3150 else
3151 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
3152 }
3153}
3154
3155void QgsRasterMarkerSymbolLayer::setPath( const QString &path )
3156{
3157 mPath = path;
3159}
3160
3162{
3163 const bool aPreservedAspectRatio = preservedAspectRatio();
3164 if ( aPreservedAspectRatio && !par )
3165 {
3167 }
3168 else if ( !aPreservedAspectRatio && par )
3169 {
3170 mFixedAspectRatio = 0.0;
3171 }
3172 return preservedAspectRatio();
3173}
3174
3176{
3177 if ( mDefaultAspectRatio == 0.0 && !mPath.isEmpty() )
3178 {
3180 mDefaultAspectRatio = ( !size.isNull() && size.isValid() && size.width() > 0 ) ? static_cast< double >( size.height() ) / static_cast< double >( size.width() ) : 0.0;
3181 }
3182 return mDefaultAspectRatio;
3183}
3184
3186{
3187 return QStringLiteral( "RasterMarker" );
3188}
3189
3194
3196{
3197 QPainter *p = context.renderContext().painter();
3198 if ( !p )
3199 return;
3200
3201 QString path = mPath;
3203 {
3206 }
3207
3208 if ( path.isEmpty() )
3209 return;
3210
3211 double width = 0.0;
3212 double height = 0.0;
3213
3214 bool hasDataDefinedSize = false;
3215 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3216
3217 bool hasDataDefinedAspectRatio = false;
3218 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3219
3220 QPointF outputOffset;
3221 double angle = 0.0;
3222
3223 // RenderPercentage Unit Type takes original image size
3225 {
3227 if ( size.isEmpty() )
3228 return;
3229
3230 width = ( scaledSize * static_cast< double >( size.width() ) ) / 100.0;
3231 height = ( scaledSize * static_cast< double >( size.height() ) ) / 100.0;
3232
3233 // don't render symbols with size below one or above 10,000 pixels
3234 if ( static_cast< int >( width ) < 1 || 10000.0 < width || static_cast< int >( height ) < 1 || 10000.0 < height )
3235 return;
3236
3237 calculateOffsetAndRotation( context, width, height, outputOffset, angle );
3238 }
3239 else
3240 {
3241 width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3242 height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3243
3244 if ( preservedAspectRatio() && path != mPath )
3245 {
3247 if ( !size.isNull() && size.isValid() && size.width() > 0 )
3248 {
3249 height = width * ( static_cast< double >( size.height() ) / static_cast< double >( size.width() ) );
3250 }
3251 }
3252
3253 // don't render symbols with size below one or above 10,000 pixels
3254 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
3255 return;
3256
3257 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3258 }
3259
3260 const QgsScopedQPainterState painterState( p );
3261 p->translate( point + outputOffset );
3262
3263 const bool rotated = !qgsDoubleNear( angle, 0 );
3264 if ( rotated )
3265 p->rotate( angle );
3266
3267 double opacity = mOpacity;
3269 {
3272 }
3273 opacity *= context.opacity();
3274
3275 QImage img = fetchImage( context.renderContext(), path, QSize( width, preservedAspectRatio() ? 0 : width * aspectRatio ), preservedAspectRatio(), opacity );
3276 if ( !img.isNull() )
3277 {
3278 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
3279 if ( useSelectedColor )
3280 {
3282 }
3283
3284 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
3285 }
3286}
3287
3288QImage QgsRasterMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
3289{
3290 bool cached = false;
3291 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking );
3292}
3293
3294double QgsRasterMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
3295{
3296 double scaledSize = mSize;
3298
3299 bool ok = true;
3300 if ( hasDataDefinedSize )
3301 {
3304 }
3305 else
3306 {
3308 if ( hasDataDefinedSize )
3309 {
3312 }
3313 }
3314
3315 if ( hasDataDefinedSize && ok )
3316 {
3317 switch ( mScaleMethod )
3318 {
3320 scaledSize = std::sqrt( scaledSize );
3321 break;
3323 break;
3324 }
3325 }
3326
3327 return scaledSize;
3328}
3329
3330double QgsRasterMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
3331{
3333 if ( !hasDataDefinedAspectRatio )
3334 return mFixedAspectRatio;
3335
3337 return 0.0;
3338
3339 double scaledAspectRatio = mDefaultAspectRatio;
3340 if ( mFixedAspectRatio > 0.0 )
3341 scaledAspectRatio = mFixedAspectRatio;
3342
3343 const double defaultHeight = mSize * scaledAspectRatio;
3344 scaledAspectRatio = defaultHeight / scaledSize;
3345
3346 bool ok = true;
3347 double scaledHeight = scaledSize * scaledAspectRatio;
3349 {
3350 context.setOriginalValueVariable( defaultHeight );
3352 }
3353
3354 if ( hasDataDefinedAspectRatio && ok )
3355 {
3356 switch ( mScaleMethod )
3357 {
3359 scaledHeight = sqrt( scaledHeight );
3360 break;
3362 break;
3363 }
3364 }
3365
3366 scaledAspectRatio = scaledHeight / scaledSize;
3367
3368 return scaledAspectRatio;
3369}
3370
3371void QgsRasterMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
3372{
3373 //offset
3374 double offsetX = 0;
3375 double offsetY = 0;
3376 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
3377 offset = QPointF( offsetX, offsetY );
3378
3381 {
3384 }
3385
3387 if ( hasDataDefinedRotation )
3388 {
3389 const QgsFeature *f = context.feature();
3390 if ( f )
3391 {
3392 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3393 {
3394 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3395 angle += m2p.mapRotation();
3396 }
3397 }
3398 }
3399
3400 if ( angle )
3402}
3403
3404
3406{
3407 QVariantMap map;
3408 map[QStringLiteral( "imageFile" )] = mPath;
3409 map[QStringLiteral( "size" )] = QString::number( mSize );
3410 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3411 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3412 map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
3413 map[QStringLiteral( "angle" )] = QString::number( mAngle );
3414 map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
3415 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3416 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3417 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3418 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
3419 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
3420 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
3421 return map;
3422}
3423
3425{
3426 auto m = std::make_unique< QgsRasterMarkerSymbolLayer >();
3427 m->mPath = mPath;
3428 m->mDefaultAspectRatio = mDefaultAspectRatio;
3429 m->mSize = mSize;
3430 m->mAngle = mAngle;
3431 // other members are copied by:
3432 copyCommonProperties( m.get() );
3433 return m.release();
3434}
3435
3436
3451
3457
3459{
3460 return QColor();
3461}
3462
3467
3472
3474{
3475 bool hasDataDefinedSize = false;
3476 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3477 const double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3478 bool hasDataDefinedAspectRatio = false;
3479 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3480 const double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3481
3482 //don't render symbols with size below one or above 10,000 pixels
3483 if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
3484 {
3485 return QRectF();
3486 }
3487
3488 QPointF outputOffset;
3489 double angle = 0.0;
3490 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3491
3492 QTransform transform;
3493
3494 // move to the desired position
3495 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3496
3497 if ( !qgsDoubleNear( angle, 0.0 ) )
3498 transform.rotate( angle );
3499
3500 QRectF symbolBounds = transform.mapRect( QRectF( -width / 2.0,
3501 -height / 2.0,
3502 width,
3503 height ) );
3504
3505 return symbolBounds;
3506}
3507
3509
3510QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QString chr, double pointSize, const QColor &color, double angle )
3511{
3512 mFontFamily = fontFamily;
3513 mString = chr;
3514 mColor = color;
3515 mAngle = angle;
3516 mSize = pointSize;
3517 mOrigSize = pointSize;
3519 mOffset = QPointF( 0, 0 );
3521 mStrokeColor = DEFAULT_FONTMARKER_BORDERCOLOR;
3522 mStrokeWidth = 0.0;
3523 mStrokeWidthUnit = Qgis::RenderUnit::Millimeters;
3524 mPenJoinStyle = DEFAULT_FONTMARKER_JOINSTYLE;
3525}
3526
3528
3530{
3532 QString string = DEFAULT_FONTMARKER_CHR;
3533 double pointSize = DEFAULT_FONTMARKER_SIZE;
3536
3537 if ( props.contains( QStringLiteral( "font" ) ) )
3538 fontFamily = props[QStringLiteral( "font" )].toString();
3539 if ( props.contains( QStringLiteral( "chr" ) ) && props[QStringLiteral( "chr" )].toString().length() > 0 )
3540 {
3541 string = props["chr"].toString();
3542 const thread_local QRegularExpression charRegExp( QStringLiteral( "%1([0-9]+)%1" ).arg( FONTMARKER_CHR_FIX ) );
3543 QRegularExpressionMatch match = charRegExp.match( string );
3544 while ( match.hasMatch() )
3545 {
3546 QChar replacement = QChar( match.captured( 1 ).toUShort() );
3547 string = string.mid( 0, match.capturedStart( 0 ) ) + replacement + string.mid( match.capturedEnd( 0 ) );
3548 match = charRegExp.match( string );
3549 }
3550 }
3551
3552 if ( props.contains( QStringLiteral( "size" ) ) )
3553 pointSize = props[QStringLiteral( "size" )].toDouble();
3554 if ( props.contains( QStringLiteral( "color" ) ) )
3555 color = QgsColorUtils::colorFromString( props[QStringLiteral( "color" )].toString() );
3556 if ( props.contains( QStringLiteral( "angle" ) ) )
3557 angle = props[QStringLiteral( "angle" )].toDouble();
3558
3560
3561 if ( props.contains( QStringLiteral( "font_style" ) ) )
3562 m->setFontStyle( props[QStringLiteral( "font_style" )].toString() );
3563 if ( props.contains( QStringLiteral( "outline_color" ) ) )
3564 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "outline_color" )].toString() ) );
3565 if ( props.contains( QStringLiteral( "outline_width" ) ) )
3566 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
3567 if ( props.contains( QStringLiteral( "offset" ) ) )
3568 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
3569 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
3570 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
3571 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3572 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3573 if ( props.contains( QStringLiteral( "size_unit" ) ) )
3574 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
3575 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3576 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3577 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
3578 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
3579 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
3580 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
3581 if ( props.contains( QStringLiteral( "joinstyle" ) ) )
3582 m->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() ) );
3583 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3584 m->setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3585 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3586 m->setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3587
3589
3590 return m;
3591}
3592
3594{
3595 return QStringLiteral( "FontMarker" );
3596}
3597
3602
3604{
3605 QColor brushColor = mColor;
3606 QColor penColor = mStrokeColor;
3607
3608 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
3609 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
3610
3611 mBrush = QBrush( brushColor );
3612 mPen = QPen( penColor );
3613 mPen.setJoinStyle( mPenJoinStyle );
3614 mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
3615
3616 mFont = QgsFontUtils::createFont( QgsApplication::fontManager()->processFontFamilyName( mFontFamily ) );
3617 if ( !mFontStyle.isEmpty() )
3618 {
3619 mFont.setStyleName( QgsFontUtils::translateNamedStyle( mFontStyle ) );
3620 }
3621
3622 double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3623 mNonZeroFontSize = !qgsDoubleNear( sizePixels, 0.0 );
3624
3625 if ( mNonZeroFontSize && sizePixels > MAX_FONT_CHARACTER_SIZE_IN_PIXELS )
3626 {
3627 // if font is too large (e.g using map units and map is very zoomed in), then we limit
3628 // the font size and instead scale up the painter.
3629 // this avoids issues with massive font sizes (eg https://github.com/qgis/QGIS/issues/42270)
3630 mFontSizeScale = sizePixels / MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3631 sizePixels = MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3632 }
3633 else
3634 mFontSizeScale = 1.0;
3635
3636 // if a non zero, but small pixel size results, round up to 2 pixels so that a "dot" is at least visible
3637 // (if we set a <=1 pixel size here Qt will reset the font to a default size, leading to much too large symbols)
3638 mFont.setPixelSize( std::max( 2, static_cast< int >( std::round( sizePixels ) ) ) );
3639 mFontMetrics.reset( new QFontMetrics( mFont ) );
3640 mChrWidth = mFontMetrics->horizontalAdvance( mString );
3641 switch ( mVerticalAnchorPoint )
3642 {
3644 {
3645 mChrOffset = QPointF( mChrWidth / 2.0, -sizePixels / 2.0 );
3646 break;
3647 }
3651 {
3652 mChrOffset = QPointF( mChrWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3653 break;
3654 }
3655 }
3656 mOrigSize = mSize; // save in case the size would be data defined
3657
3658 // use caching only when not using a data defined character
3662 if ( mUseCachedPath )
3663 {
3664 QPointF chrOffset = mChrOffset;
3665 double chrWidth;
3666 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3667 mCachedPath = QPainterPath();
3668 mCachedPath.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3669 }
3670}
3671
3673{
3674 Q_UNUSED( context )
3675}
3676
3677QString QgsFontMarkerSymbolLayer::characterToRender( QgsSymbolRenderContext &context, QPointF &charOffset, double &charWidth )
3678{
3679 charOffset = mChrOffset;
3680 QString stringToRender = mString;
3682 {
3683 context.setOriginalValueVariable( mString );
3685 if ( stringToRender != mString )
3686 {
3687 charWidth = mFontMetrics->horizontalAdvance( stringToRender );
3688 switch ( mVerticalAnchorPoint )
3689 {
3691 {
3692 const double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3693 charOffset = QPointF( charWidth / 2.0, -sizePixels / 2.0 );
3694 break;
3695 }
3699 {
3700 charOffset = QPointF( charWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3701 break;
3702 }
3703 }
3704 }
3705 }
3706 return stringToRender;
3707}
3708
3709void QgsFontMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
3710 double scaledSize,
3711 bool &hasDataDefinedRotation,
3712 QPointF &offset,
3713 double &angle ) const
3714{
3715 //offset
3716 double offsetX = 0;
3717 double offsetY = 0;
3718 markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
3719 offset = QPointF( offsetX, offsetY );
3720 hasDataDefinedRotation = false;
3721
3722 //angle
3723 bool ok = true;
3726 {
3729
3730 // If the expression evaluation was not successful, fallback to static value
3731 if ( !ok )
3733
3734 hasDataDefinedRotation = true;
3735 }
3736
3737 hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || hasDataDefinedRotation;
3738 if ( hasDataDefinedRotation )
3739 {
3740 // For non-point markers, "dataDefinedRotation" means following the
3741 // shape (shape-data defined). For them, "field-data defined" does
3742 // not work at all. TODO: if "field-data defined" ever gets implemented
3743 // we'll need a way to distinguish here between the two, possibly
3744 // using another flag in renderHints()
3745 const QgsFeature *f = context.feature();
3746 if ( f )
3747 {
3748 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3749 {
3750 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3751 angle += m2p.mapRotation();
3752 }
3753 }
3754 }
3755
3756 if ( angle )
3758}
3759
3760double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context )
3761{
3762 double scaledSize = mSize;
3763 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
3764
3765 bool ok = true;
3766 if ( hasDataDefinedSize )
3767 {
3770 }
3771
3772 if ( hasDataDefinedSize && ok )
3773 {
3774 switch ( mScaleMethod )
3775 {
3777 scaledSize = std::sqrt( scaledSize );
3778 break;
3780 break;
3781 }
3782 }
3783 return scaledSize;
3784}
3785
3787{
3788 QPainter *p = context.renderContext().painter();
3789 if ( !p || !mNonZeroFontSize )
3790 return;
3791
3792 QTransform transform;
3793
3794 bool ok;
3795 QColor brushColor = mColor;
3797 {
3800 }
3801 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
3802 brushColor = useSelectedColor ? context.renderContext().selectionColor() : brushColor;
3803 if ( !useSelectedColor || !SELECTION_IS_OPAQUE )
3804 {
3805 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
3806 }
3807 mBrush.setColor( brushColor );
3808
3809 QColor penColor = mStrokeColor;
3811 {
3814 }
3815 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
3816
3817 double penWidth = context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3819 {
3820 context.setOriginalValueVariable( mStrokeWidth );
3822 if ( ok )
3823 {
3824 penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3825 }
3826 }
3827
3829 {
3832 if ( ok )
3833 {
3834 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
3835 }
3836 }
3837
3838 const QgsScopedQPainterState painterState( p );
3839 p->setBrush( mBrush );
3840 if ( !qgsDoubleNear( penWidth, 0.0 ) )
3841 {
3842 mPen.setColor( penColor );
3843 mPen.setWidthF( penWidth );
3844 p->setPen( mPen );
3845 }
3846 else
3847 {
3848 p->setPen( Qt::NoPen );
3849 }
3850
3852 {
3853 context.setOriginalValueVariable( mFontFamily );
3855 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( ok ? fontFamily : mFontFamily );
3856 QgsFontUtils::setFontFamily( mFont, processedFamily );
3857 }
3859 {
3860 context.setOriginalValueVariable( mFontStyle );
3863 }
3865 {
3866 mFontMetrics.reset( new QFontMetrics( mFont ) );
3867 }
3868
3869 QPointF chrOffset = mChrOffset;
3870 double chrWidth;
3871 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3872
3873 const double sizeToRender = calculateSize( context );
3874
3875 bool hasDataDefinedRotation = false;
3876 QPointF offset;
3877 double angle = 0;
3878 calculateOffsetAndRotation( context, sizeToRender, hasDataDefinedRotation, offset, angle );
3879
3880 p->translate( point.x() + offset.x(), point.y() + offset.y() );
3881
3882 if ( !qgsDoubleNear( angle, 0.0 ) )
3883 transform.rotate( angle );
3884
3885 if ( !qgsDoubleNear( sizeToRender, mOrigSize ) )
3886 {
3887 const double s = sizeToRender / mOrigSize;
3888 transform.scale( s, s );
3889 }
3890
3891 if ( !qgsDoubleNear( mFontSizeScale, 1.0 ) )
3892 transform.scale( mFontSizeScale, mFontSizeScale );
3893
3894 if ( mUseCachedPath )
3895 {
3896 p->drawPath( transform.map( mCachedPath ) );
3897 }
3898 else
3899 {
3900 QPainterPath path;
3901 path.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3902 p->drawPath( transform.map( path ) );
3903 }
3904}
3905
3907{
3908 QVariantMap props;
3909 props[QStringLiteral( "font" )] = mFontFamily;
3910 props[QStringLiteral( "font_style" )] = mFontStyle;
3911 QString chr = mString;
3912 for ( int i = 0; i < 32; i++ )
3913 {
3914 if ( i == 9 || i == 10 || i == 13 )
3915 {
3916 continue;
3917 }
3918 chr.replace( QChar( i ), QStringLiteral( "%1%2%1" ).arg( FONTMARKER_CHR_FIX, QString::number( i ) ) );
3919 }
3920 props[QStringLiteral( "chr" )] = chr;
3921 props[QStringLiteral( "size" )] = QString::number( mSize );
3922 props[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3923 props[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3924 props[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mColor );
3925 props[QStringLiteral( "outline_color" )] = QgsColorUtils::colorToString( mStrokeColor );
3926 props[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
3927 props[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
3928 props[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
3929 props[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
3930 props[QStringLiteral( "angle" )] = QString::number( mAngle );
3931 props[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3932 props[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3933 props[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3934 props[QStringLiteral( "horizontal_anchor_point" )] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
3935 props[QStringLiteral( "vertical_anchor_point" )] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
3936 return props;
3937}
3938
3940{
3941 QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( mFontFamily, mString, mSize, mColor, mAngle );
3942 m->setFontStyle( mFontStyle );
3943 m->setStrokeColor( mStrokeColor );
3944 m->setStrokeWidth( mStrokeWidth );
3945 m->setStrokeWidthUnit( mStrokeWidthUnit );
3946 m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
3947 m->setPenJoinStyle( mPenJoinStyle );
3948 m->setOffset( mOffset );
3951 m->setSizeUnit( mSizeUnit );
3956 copyPaintEffect( m );
3957 return m;
3958}
3959
3960void QgsFontMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
3961{
3962 QgsSldExportContext context;
3963 context.setExtraProperties( props );
3964 toSld( doc, element, context );
3965}
3966
3967bool QgsFontMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
3968{
3969 return QgsMarkerSymbolLayer::toSld( doc, element, context );
3970}
3971
3972void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
3973{
3974 QgsSldExportContext context;
3975 context.setExtraProperties( props );
3976 writeSldMarker( doc, element, context );
3977}
3978
3979bool QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
3980{
3981 // <Graphic>
3982 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
3983 element.appendChild( graphicElem );
3984
3985 const QVariantMap props = context.extraProperties();
3986 const QString fontPath = QStringLiteral( "ttf://%1" ).arg( mFontFamily );
3987 int markIndex = !mString.isEmpty() ? mString.at( 0 ).unicode() : 0;
3988 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
3989 QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, QStringLiteral( "ttf" ), context, &markIndex, mColor, size );
3990
3991 // <Rotation>
3992 QString angleFunc;
3993 bool ok;
3994 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
3995 if ( !ok )
3996 {
3997 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
3998 }
3999 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
4000 {
4001 angleFunc = QString::number( angle + mAngle );
4002 }
4003 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc, context );
4004
4005 // <Displacement>
4006 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
4008 return true;
4009}
4010
4017
4019{
4021 mStrokeWidthUnit = unit;
4022}
4023
4025{
4026 QPointF chrOffset = mChrOffset;
4027 double chrWidth = mChrWidth;
4028 //calculate width of rendered character
4029 ( void )characterToRender( context, chrOffset, chrWidth );
4030
4031 if ( !mFontMetrics )
4032 mFontMetrics.reset( new QFontMetrics( mFont ) );
4033
4034 double scaledSize = calculateSize( context );
4035 if ( !qgsDoubleNear( scaledSize, mOrigSize ) )
4036 {
4037 chrWidth *= scaledSize / mOrigSize;
4038 }
4039 chrWidth *= mFontSizeScale;
4040
4041 bool hasDataDefinedRotation = false;
4042 QPointF offset;
4043 double angle = 0;
4044 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
4045 scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
4046
4047 QTransform transform;
4048
4049 // move to the desired position
4050 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
4051
4052 if ( !qgsDoubleNear( angle, 0.0 ) )
4053 transform.rotate( angle );
4054
4055 QRectF symbolBounds = transform.mapRect( QRectF( -chrWidth / 2.0,
4056 -scaledSize / 2.0,
4057 chrWidth,
4058 scaledSize ) );
4059 return symbolBounds;
4060}
4061
4063{
4064 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
4065
4066 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
4067 if ( graphicElem.isNull() )
4068 return nullptr;
4069
4070 QString name, format;
4071 QColor color;
4072 double size;
4073 int chr;
4074
4075 if ( !QgsSymbolLayerUtils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
4076 return nullptr;
4077
4078 if ( !name.startsWith( QLatin1String( "ttf://" ) ) || format != QLatin1String( "ttf" ) )
4079 return nullptr;
4080
4081 const QString fontFamily = name.mid( 6 );
4082
4083 double angle = 0.0;
4084 QString angleFunc;
4085 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
4086 {
4087 bool ok;
4088 const double d = angleFunc.toDouble( &ok );
4089 if ( ok )
4090 angle = d;
4091 }
4092
4093 QPointF offset;
4095
4096 double scaleFactor = 1.0;
4097 const QString uom = element.attribute( QStringLiteral( "uom" ) );
4098 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
4099 offset.setX( offset.x() * scaleFactor );
4100 offset.setY( offset.y() * scaleFactor );
4101 size = size * scaleFactor;
4102
4104 m->setOutputUnit( sldUnitSize );
4105 m->setAngle( angle );
4106 m->setOffset( offset );
4107 return m;
4108}
4109
4110void QgsFontMarkerSymbolLayer::resolveFonts( const QVariantMap &properties, const QgsReadWriteContext &context )
4111{
4112 const QString fontFamily = properties.value( QStringLiteral( "font" ), DEFAULT_FONTMARKER_FONT ).toString();
4113 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( fontFamily );
4114 QString matched;
4115 if ( !QgsFontUtils::fontFamilyMatchOnSystem( processedFamily )
4116 && !QgsApplication::fontManager()->tryToDownloadFontFamily( processedFamily, matched ) )
4117 {
4118 context.pushMessage( QObject::tr( "Font “%1” not available on system" ).arg( processedFamily ) );
4119 }
4120}
4121
4123{
4124 QMap<QString, QgsProperty>::iterator it = mParameters.begin();
4125 for ( ; it != mParameters.end(); ++it )
4126 it.value().prepare( context.renderContext().expressionContext() );
4127
4129}
4130
4131
4133{
4134 QSet<QString> attrs = QgsMarkerSymbolLayer::usedAttributes( context );
4135
4136 QMap<QString, QgsProperty>::const_iterator it = mParameters.constBegin();
4137 for ( ; it != mParameters.constEnd(); ++it )
4138 {
4139 attrs.unite( it.value().referencedFields( context.expressionContext(), true ) );
4140 }
4141
4142 return attrs;
4143}
4144
4145//
4146// QgsAnimatedMarkerSymbolLayer
4147//
4148
4149QgsAnimatedMarkerSymbolLayer::QgsAnimatedMarkerSymbolLayer( const QString &path, double size, double angle )
4150 : QgsRasterMarkerSymbolLayer( path, size, angle )
4151{
4152
4153}
4154
4156
4157QgsSymbolLayer *QgsAnimatedMarkerSymbolLayer::create( const QVariantMap &properties ) // cppcheck-suppress duplInheritedMember
4158{
4159 QString path;
4162
4163 if ( properties.contains( QStringLiteral( "imageFile" ) ) )
4164 path = properties[QStringLiteral( "imageFile" )].toString();
4165 if ( properties.contains( QStringLiteral( "size" ) ) )
4166 size = properties[QStringLiteral( "size" )].toDouble();
4167 if ( properties.contains( QStringLiteral( "angle" ) ) )
4168 angle = properties[QStringLiteral( "angle" )].toDouble();
4169
4170 auto m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( path, size, angle );
4171 m->setFrameRate( properties.value( QStringLiteral( "frameRate" ), QStringLiteral( "10" ) ).toDouble() );
4172
4173 m->setCommonProperties( properties );
4174 return m.release();
4175}
4176
4178{
4179 return QStringLiteral( "AnimatedMarker" );
4180}
4181
4183{
4184 QVariantMap res = QgsRasterMarkerSymbolLayer::properties();
4185 res.insert( QStringLiteral( "frameRate" ), mFrameRateFps );
4186 return res;
4187}
4188
4190{
4191 auto m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( mPath, mSize, mAngle );
4192 m->setFrameRate( mFrameRateFps );
4193 copyCommonProperties( m.get() );
4194 return m.release();
4195}
4196
4198{
4200
4201 mPreparedPaths.clear();
4203 {
4205 mStaticPath = true;
4206 }
4207 else
4208 {
4209 mStaticPath = false;
4210 }
4211}
4212
4213QImage QgsAnimatedMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
4214{
4215 if ( !mStaticPath && !mPreparedPaths.contains( path ) )
4216 {
4218 mPreparedPaths.insert( path );
4219 }
4220
4221 const long long mapFrameNumber = context.currentFrame();
4223 const double markerAnimationDuration = totalFrameCount / mFrameRateFps;
4224
4225 double animationTimeSeconds = 0;
4226 if ( mapFrameNumber >= 0 && context.frameRate() > 0 )
4227 {
4228 // render is part of an animation, so we base the calculated frame on that
4229 animationTimeSeconds = mapFrameNumber / context.frameRate();
4230 }
4231 else
4232 {
4233 // render is outside of animation, so base the calculated frame on the current epoch
4234 animationTimeSeconds = QDateTime::currentMSecsSinceEpoch() / 1000.0;
4235 }
4236
4237 const double markerAnimationProgressSeconds = std::fmod( animationTimeSeconds, markerAnimationDuration );
4238 const int movieFrame = static_cast< int >( std::floor( markerAnimationProgressSeconds * mFrameRateFps ) );
4239
4240 bool cached = false;
4241 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking, 96, movieFrame );
4242}
4243
@ DynamicRotation
Rotation of symbol may be changed during rendering and symbol should not be cached.
@ IsSymbolLayerSubSymbol
Symbol is being rendered as a sub-symbol of a QgsSymbolLayer.
@ CanCalculateMaskGeometryPerFeature
If present, indicates that mask geometry can safely be calculated per feature for the symbol layer....
ScaleMethod
Scale methods.
Definition qgis.h:588
@ ScaleDiameter
Calculate scale by the diameter.
@ ScaleArea
Calculate scale by the area.
QFlags< SymbolLayerFlag > SymbolLayerFlags
Symbol layer flags.
Definition qgis.h:851
MarkerShape
Marker shapes.
Definition qgis.h:2974
@ 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)
@ ParallelogramRight
Parallelogram that slants right.
@ AsteriskFill
A filled asterisk shape.
@ HalfArc
A line-only half arc.
@ Line
Vertical line.
@ QuarterSquare
Quarter square (top left quarter)
@ Triangle
Triangle.
@ Cross2
Rotated cross (lines only), 'x' shape.
@ Trapezoid
Trapezoid.
@ ArrowHeadFilled
Right facing filled arrow head.
@ Shield
A shape consisting of a triangle attached to a rectangle.
@ HalfSquare
Half square (left half)
@ CrossFill
Solid filled cross.
@ RoundedSquare
A square with rounded corners.
@ RightHalfTriangle
Right half of triangle.
@ ThirdCircle
One third circle (top left third)
@ ThirdArc
A line-only one third arc.
@ SquareWithCorners
A square with diagonal corners.
@ QuarterArc
A line-only one quarter arc.
@ DiamondStar
A 4-sided star.
@ Cross
Cross (lines only)
@ ParallelogramLeft
Parallelogram that slants left.
@ DiagonalHalfSquare
Diagonal half square (bottom left half)
VerticalAnchorPoint
Marker symbol vertical anchor points.
Definition qgis.h:778
@ Bottom
Align to bottom of symbol.
@ Center
Align to vertical center of symbol.
@ Baseline
Align to baseline of symbol, e.g. font baseline for font marker symbol layers. Treated as Bottom if n...
@ Top
Align to top of symbol.
RenderUnit
Rendering size units.
Definition qgis.h:5029
@ Percentage
Percentage of another measurement (e.g., canvas size, feature size)
@ Millimeters
Millimeters.
@ Unknown
Mixed or unknown units.
@ MapUnits
Map units.
@ MetersInMapUnits
Meters value as Map units.
@ 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.
HorizontalAnchorPoint
Marker symbol horizontal anchor points.
Definition qgis.h:764
QColor valueAsColor(int key, const QgsExpressionContext &context, const QColor &defaultColor=QColor(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a color.
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double.
QString valueAsString(int key, const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a string.
Animated marker symbol layer class.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsAnimatedMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
~QgsAnimatedMarkerSymbolLayer() override
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
QImage fetchImage(QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity) const override
Fetches the image to render.
QgsAnimatedMarkerSymbolLayer(const QString &path=QString(), double size=DEFAULT_RASTERMARKER_SIZE, double angle=DEFAULT_RASTERMARKER_ANGLE)
Constructor for animated marker symbol layer using the specified source image path.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates an animated marker symbol layer from a string map of properties.
QString layerType() const override
Returns a string that represents this layer type.
static QgsImageCache * imageCache()
Returns the application's image cache, used for caching resampled versions of raster images.
static QgsSvgCache * svgCache()
Returns the application's SVG cache, used for caching SVG images and handling parameter replacement w...
static QgsFontManager * fontManager()
Returns the application font manager, which manages available fonts and font installation for the QGI...
static QColor colorFromString(const QString &string)
Decodes a string into a color value.
static QString colorToString(const QColor &color)
Encodes a color into a string value.
Exports QGIS layers to the DXF format.
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)
static double mapUnitScaleFactor(double scale, Qgis::RenderUnit symbolUnits, Qgis::DistanceUnit mapUnits, double mapUnitsPerPixel=1.0)
Returns scale factor for conversion to map units.
void writeLine(const QgsPoint &pt1, const QgsPoint &pt2, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Write line (as a polyline)
void writePolygon(const QgsRingSequence &polygon, const QString &layer, const QString &hatchPattern, const QColor &color)
Draw dxf filled polygon (HATCH)
Qgis::DistanceUnit mapUnits() const
Retrieve map units.
double symbologyScale() const
Returns the reference scale for output.
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:58
QgsGeometry geometry
Definition qgsfeature.h:69
bool hasGeometry() const
Returns true if the feature has an associated geometry.
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
static std::unique_ptr< 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.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol 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.
void stopFeatureRender(const QgsFeature &feature, QgsRenderContext &context) override
Called after the layer has been rendered for a particular feature.
void startFeatureRender(const QgsFeature &feature, QgsRenderContext &context) override
Called before the layer will be rendered for a particular feature.
~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.
QString processFontFamilyName(const QString &name) const
Processes a font family name, applying any matching fontFamilyReplacements() to the name.
A marker symbol layer which displays characters rendered using a font.
void setStrokeWidthUnit(Qgis::RenderUnit unit)
Sets the stroke width unit.
~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.
Q_DECL_DEPRECATED void toSld(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Saves the symbol layer as SLD.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
Q_DECL_DEPRECATED 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 setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
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())
Qgis::SymbolLayerFlags flags() const override
Returns flags which control the symbol layer's behavior.
static QString translateNamedStyle(const QString &namedStyle)
Returns the localized named style of a font, if such a translation is available.
static QFont createFont(const QString &family, int pointSize=-1, int weight=-1, bool italic=false)
Creates a font with the specified family.
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.
static void setFontFamily(QFont &font, const QString &family)
Sets the family for a font object.
Qgis::GeometryType type
QSize originalSize(const QString &path, bool blocking=false) const
Returns the original size (in pixels) of the image at the specified path.
int totalFrameCount(const QString &path, bool blocking=false)
Returns the total frame count of the image at the specified path.
QImage pathAsImage(const QString &path, const QSize size, const bool keepAspectRatio, const double opacity, bool &fitsInCache, bool blocking=false, double targetDpi=96, int frameNumber=-1, bool *isMissing=nullptr)
Returns the specified path rendered as an image.
void prepareAnimation(const QString &path)
Prepares for optimized retrieval of frames for the animation at the given path.
static void overlayColor(QImage &image, const QColor &color)
Overlays a color onto an image.
Perform transforms between map coordinates and device coordinates.
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.
void setVerticalAnchorPoint(Qgis::VerticalAnchorPoint v)
Sets the vertical anchor point for positioning the symbol.
Qgis::RenderUnit mOffsetUnit
Offset units.
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)
void setOffsetUnit(Qgis::RenderUnit unit)
Sets the units for the symbol's offset.
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.
bool toSld(QDomDocument &doc, QDomElement &element, QgsSldExportContext &context) const override
Saves the symbol layer as SLD.
Qgis::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
QPointF mOffset
Marker offset.
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.
Qgis::VerticalAnchorPoint mVerticalAnchorPoint
Vertical anchor point.
static QPointF _rotatedOffset(QPointF offset, double angle)
Adjusts a marker offset to account for rotation.
void setHorizontalAnchorPoint(Qgis::HorizontalAnchorPoint h)
Sets the horizontal anchor point for positioning the symbol.
Qgis::ScaleMethod mScaleMethod
Marker size scaling method.
QgsMapUnitScale mSizeMapUnitScale
Marker size map unit scale.
Qgis::RenderUnit mSizeUnit
Marker size unit.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units for the symbol's size.
void markerOffset(QgsSymbolRenderContext &context, double &offsetX, double &offsetY) const
Calculates the required marker offset, including both the symbol offset and any displacement required...
Qgis::HorizontalAnchorPoint mHorizontalAnchorPoint
Horizontal anchor point.
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.
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
static void drawPicture(QPainter *painter, const QPointF &point, const QPicture &picture)
Draws a picture onto a painter, correctly applying workarounds to avoid issues with incorrect scaling...
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 final
Returns the calculated value of the property with the specified key from within the collection.
bool isActive(int key) const final
Returns true if the collection contains an active property with the specified key.
QgsProperty property(int key) const final
Returns a matching property from the collection, if one exists.
QString asExpression() const
Returns an expression string representing the state of the property, or an empty string if the proper...
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.
static QgsProperty fromValue(const QVariant &value, bool isActive=true)
Returns a new StaticProperty created from the specified value.
Raster marker symbol layer class.
double mFixedAspectRatio
The marker fixed aspect ratio.
QColor color() const override
Returns the "representative" color of the symbol layer.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsMapUnitScale mapUnitScale() const override
void copyCommonProperties(QgsRasterMarkerSymbolLayer *other) const
Copies common properties to another layer.
void setOpacity(double opacity)
Set the marker opacity.
QString path() const
Returns the marker raster image path.
double calculateAspectRatio(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio) const
Calculates the marker aspect ratio between width and height.
QgsRasterMarkerSymbolLayer(const QString &path=QString(), double size=DEFAULT_SVGMARKER_SIZE, double angle=DEFAULT_SVGMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructs raster marker symbol layer with picture from given absolute path to a raster image file.
void setPath(const QString &path)
Set the marker raster image path.
virtual QImage fetchImage(QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity) const
Fetches the image to render.
double defaultAspectRatio() const
Returns the default marker aspect ratio between width and height, 0 if not yet calculated.
Qgis::SymbolLayerFlags flags() const override
Returns flags which control the symbol layer's behavior.
void setFixedAspectRatio(double ratio)
Set the marker aspect ratio between width and height to be used in rendering, if the value set is low...
void setMapUnitScale(const QgsMapUnitScale &scale) override
void setCommonProperties(const QVariantMap &properties)
Sets common class properties from a properties map.
QgsRasterMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
bool preservedAspectRatio() const
Returns the preserved aspect ratio value, true if fixed aspect ratio has been lower or equal to 0.
static void resolvePaths(QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving)
Turns relative paths in properties map to absolute when reading and vice versa when writing.
~QgsRasterMarkerSymbolLayer() override
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a raster marker symbol layer from a string map of properties.
double mOpacity
The marker default opacity.
double updateDefaultAspectRatio()
Calculates the default marker aspect ratio between width and height.
double mDefaultAspectRatio
The marker default aspect ratio.
bool setPreservedAspectRatio(bool par)
Set preserved the marker aspect ratio between width and height.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QString layerType() const override
Returns a string that represents this layer type.
double opacity() const
Returns the marker opacity.
A container for the context for various read/write operations on 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.
double convertToPainterUnits(double size, Qgis::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QPainter * painter()
Returns the destination QPainter for the render operation.
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
QgsExpressionContext & expressionContext()
Gets the expression context.
long long currentFrame() const
Returns the current frame number of the map (in frames per second), for maps which are part of an ani...
float devicePixelRatio() const
Returns the device pixel ratio.
void setFlag(Qgis::RenderContextFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
double frameRate() const
Returns the frame rate of the map, for maps which are part of an animation.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
QColor selectionColor() const
Returns the color to use when rendering selected features.
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 a stroke.
QPen mSelPen
QPen to use as stroke of selected symbols.
void setColor(const QColor &color) override
Sets the "representative" color for the symbol layer.
Q_DECL_DEPRECATED void toSld(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Saves the symbol layer as SLD.
QImage mSelCache
Cached image of selected marker, if using cached version.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
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).
void drawMarker(QPainter *p, QgsSymbolRenderContext &context)
Draws the marker shape in the specified painter.
QPen mPen
QPen corresponding to marker's stroke style.
Qgis::RenderUnit mStrokeWidthUnit
Stroke width units.
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
Qgis::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
void setStrokeWidthUnit(Qgis::RenderUnit u)
Sets the unit for the width of the marker's stroke.
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.
QString layerType() const override
Returns a string that represents this layer type.
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)
bool mUsingCache
true if using cached images of markers for drawing.
Qgis::SymbolLayerFlags flags() const override
Returns flags which control the symbol layer's behavior.
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
Q_DECL_DEPRECATED 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.
Holds SLD export options and other information related to SLD export of a QGIS layer style.
void setExtraProperties(const QVariantMap &properties)
Sets the open ended set of properties that can drive/inform the SLD encoding.
QVariantMap extraProperties() const
Returns the open ended set of properties that can drive/inform the SLD encoding.
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.
A marker symbol layer which renders an SVG graphic.
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.
QString layerType() const override
Returns a string that represents this layer type.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
QString path() const
Returns the marker SVG path.
Qgis::SymbolLayerFlags flags() const override
Returns flags which control the symbol layer's behavior.
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.
Q_DECL_DEPRECATED void toSld(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Saves the symbol layer as SLD.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
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...
Qgis::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
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 setStrokeWidthUnit(Qgis::RenderUnit unit)
Sets the units for the stroke width.
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...
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.
Q_DECL_DEPRECATED 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.
QMap< QString, QgsProperty > parameters() const
Returns the dynamic SVG parameters.
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 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 void multiplyImageOpacity(QImage *image, qreal opacity)
Multiplies opacity of image pixel values with a (global) transparency value.
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
static double rescaleUom(double size, Qgis::RenderUnit unit, const QVariantMap &props)
Rescales the given size based on the uomScale found in the props, if any is found,...
static Qt::PenCapStyle decodePenCapStyle(const QString &str)
static bool externalGraphicFromSld(QDomElement &element, QString &path, QString &mime, QColor &color, double &size)
static Q_DECL_DEPRECATED 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 Q_DECL_DEPRECATED void externalMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &path, const QString &format, int *markIndex=nullptr, const QColor &color=QColor(), double size=-1)
Exports a marker to an SLD definition.
static bool wellKnownMarkerFromSld(QDomElement &element, QString &name, QColor &color, QColor &strokeColor, Qt::PenStyle &strokeStyle, double &strokeWidth, double &size)
Extracts properties from an SLD marker definition.
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 Qgis::RenderUnit decodeSldUom(const QString &str, double *scaleFactor=nullptr)
Decodes a SLD unit of measure string to a render unit.
static double estimateMaxSymbolBleed(QgsSymbol *symbol, const QgsRenderContext &context)
Returns the maximum estimated bleed for the symbol.
static Q_DECL_DEPRECATED void wellKnownMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &name, const QColor &color, const QColor &strokeColor, Qt::PenStyle strokeStyle, double strokeWidth=-1, double size=-1)
Exports a marker to SLD.
static QString encodeScaleMethod(Qgis::ScaleMethod scaleMethod)
Encodes a symbol scale method to a string.
static Qt::PenStyle decodePenStyle(const QString &str)
static Q_DECL_DEPRECATED void createRotationElement(QDomDocument &doc, QDomElement &element, const QString &rotationFunc)
Creates SLD rotation element.
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.
Abstract base class for symbol layers.
bool shouldRenderUsingSelectionColor(const QgsSymbolRenderContext &context) const
Returns true if the symbol layer should be rendered using the selection color from the render context...
static const bool SELECTION_IS_OPAQUE
Whether styles for selected features ignore symbol alpha.
bool installMasks(QgsRenderContext &context, bool recursive, const QRectF &rect=QRectF())
When rendering, install masks on context painter.
void removeMasks(QgsRenderContext &context, bool recursive)
When rendering, remove previously installed masks from context painter if recursive is true masks are...
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.
@ StrokeStyle
Stroke style (eg solid, dashed)
@ Name
Name, eg shape name for simple markers.
@ Character
Character, eg for font marker symbol layers.
@ StrokeColor
Stroke color.
@ CapStyle
Line cap style.
@ JoinStyle
Line join style.
@ StrokeWidth
Stroke width.
@ FontFamily
Font family.
@ Offset
Symbol offset.
@ Height
Symbol height.
void restoreOldDataDefinedProperties(const QVariantMap &stringMap)
Restores older data defined properties from string map.
virtual void startRender(QgsSymbolRenderContext &context)=0
Called before a set of rendering operations commences on the supplied render context.
virtual void prepareExpressions(const QgsSymbolRenderContext &context)
Prepares all data defined property expressions for evaluation.
virtual Q_DECL_DEPRECATED void toSld(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const
Saves the symbol layer as SLD.
virtual QColor color() const
Returns the "representative" color of the symbol layer.
virtual void setOutputUnit(Qgis::RenderUnit unit)
Sets the units to use for sizes and widths within the symbol layer.
void copyPaintEffect(QgsSymbolLayer *destLayer) const
Copies paint effect of this layer to another symbol layer.
virtual Qgis::SymbolLayerFlags flags() const
Returns flags which control the symbol layer's behavior.
QgsPropertyCollection mDataDefinedProperties
virtual bool hasDataDefinedProperties() const
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
Encapsulates the context in which a symbol is being rendered.
const QgsFeature * feature() const
Returns the current feature being rendered.
QgsFields fields() const
Fields of the layer.
Qgis::SymbolRenderHints renderHints() const
Returns the rendering hint flags for the symbol.
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for data defined symbology.
bool forceVectorRendering() const
Returns true if symbol must be rendered using vector methods, and optimisations like pre-rendered ima...
qreal opacity() const
Returns the opacity for the symbol.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
Abstract base class for all rendered symbols.
Definition qgssymbol.h:231
Qgis::SymbolType type() const
Returns the symbol's type.
Definition qgssymbol.h:294
static Q_INVOKABLE Qgis::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
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:6302
QMap< QString, QString > QgsStringMap
Definition qgis.h:6816
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
#define DEG2RAD(x)
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:41
#define QgsDebugError(str)
Definition qgslogger.h:40
#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
#define FONTMARKER_CHR_FIX