QGIS API Documentation 3.99.0-Master (d270888f95f)
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
18#include <cmath>
19#include <memory>
20
21#include "qgscolorutils.h"
22#include "qgsdxfexport.h"
23#include "qgsdxfpaintdevice.h"
24#include "qgsfillsymbol.h"
25#include "qgsfontmanager.h"
26#include "qgsfontutils.h"
27#include "qgsimagecache.h"
28#include "qgsimageoperation.h"
29#include "qgslogger.h"
30#include "qgspainting.h"
31#include "qgsrendercontext.h"
32#include "qgssldexportcontext.h"
33#include "qgssvgcache.h"
34#include "qgssymbol.h"
35#include "qgssymbollayerutils.h"
36#include "qgsunittypes.h"
37
38#include <QDir>
39#include <QDomDocument>
40#include <QDomElement>
41#include <QFileInfo>
42#include <QMimeDatabase>
43#include <QMimeType>
44#include <QPainter>
45#include <QString>
46#include <QSvgRenderer>
47#include <QUrlQuery>
48
49using namespace Qt::StringLiterals;
50
51static constexpr int MAX_FONT_CHARACTER_SIZE_IN_PIXELS = 500;
52
54
55
56//
57// QgsSimpleMarkerSymbolLayerBase
58//
59
103
114
116
118{
119 switch ( shape )
120 {
151 return true;
152
160 return false;
161 }
162 return true;
163}
164
166{
167 const bool hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation
169 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
170
171 // use either QPolygonF or QPainterPath for drawing
172 if ( !prepareMarkerShape( mShape ) ) // drawing as a polygon
173 {
174 prepareMarkerPath( mShape ); // drawing as a painter path
175 }
176
177 QTransform transform;
178
179 // scale the shape (if the size is not going to be modified)
180 if ( !hasDataDefinedSize )
181 {
182 double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
184 {
185 // rendering for symbol previews -- an size in meters in map units can't be calculated, so treat the size as millimeters
186 // and clamp it to a reasonable range. It's the best we can do in this situation!
187 scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
188 }
189
190 const double half = scaledSize / 2.0;
191 transform.scale( half, half );
192 }
193
194 // rotate if the rotation is not going to be changed during the rendering
195 if ( !hasDataDefinedRotation && !qgsDoubleNear( mAngle, 0.0 ) )
196 {
197 transform.rotate( mAngle );
198 }
199
200 if ( !mPolygon.isEmpty() )
201 mPolygon = transform.map( mPolygon );
202 else
203 mPath = transform.map( mPath );
204
206}
207
209{
210 Q_UNUSED( context )
211}
212
214{
215 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
216 //of the rendered point!
217
218 QPainter *p = context.renderContext().painter();
219 if ( !p )
220 {
221 return;
222 }
223
224 bool hasDataDefinedSize = false;
225 const double scaledSize = calculateSize( context, hasDataDefinedSize );
226
227 bool hasDataDefinedRotation = false;
228 QPointF offset;
229 double angle = 0;
230 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
231
232 //data defined shape?
233 bool createdNewPath = false;
234 bool ok = true;
235 Qgis::MarkerShape symbol = mShape;
237 {
238 context.setOriginalValueVariable( encodeShape( symbol ) );
239 const QVariant exprVal = mDataDefinedProperties.value( QgsSymbolLayer::Property::Name, context.renderContext().expressionContext() );
240 if ( !QgsVariantUtils::isNull( exprVal ) )
241 {
242 const Qgis::MarkerShape decoded = decodeShape( exprVal.toString(), &ok );
243 if ( ok )
244 {
245 symbol = decoded;
246
247 if ( !prepareMarkerShape( symbol ) ) // drawing as a polygon
248 {
249 prepareMarkerPath( symbol ); // drawing as a painter path
250 }
251 createdNewPath = true;
252 }
253 }
254 else
255 {
256 symbol = mShape;
257 }
258 }
259
260 QTransform transform;
261
262 // move to the desired position
263 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
264
265 // resize if necessary
266 if ( hasDataDefinedSize || createdNewPath )
267 {
268 double s = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
270 {
271 // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
272 // and clamp it to a reasonable range. It's the best we can do in this situation!
273 s = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
274 }
275 const double half = s / 2.0;
276 transform.scale( half, half );
277 }
278
279 if ( !qgsDoubleNear( angle, 0.0 ) && ( hasDataDefinedRotation || createdNewPath ) )
280 {
281 transform.rotate( angle );
282 }
283
284 //need to pass: symbol, polygon, path
285
286 QPolygonF polygon;
287 QPainterPath path;
288 if ( !mPolygon.isEmpty() )
289 {
290 polygon = transform.map( mPolygon );
291 }
292 else
293 {
294 path = transform.map( mPath );
295 }
296 draw( context, symbol, polygon, path );
297}
298
300{
301 bool hasDataDefinedSize = false;
302 double scaledSize = calculateSize( context, hasDataDefinedSize );
303
304 bool hasDataDefinedRotation = false;
305 QPointF offset;
306 double angle = 0;
307 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
308
309 scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
310
311 QTransform transform;
312
313 // move to the desired position
314 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
315
316 if ( !qgsDoubleNear( angle, 0.0 ) )
317 transform.rotate( angle );
318
319 return transform.mapRect( QRectF( -scaledSize / 2.0,
320 -scaledSize / 2.0,
321 scaledSize,
322 scaledSize ) );
323}
324
326{
327 if ( ok )
328 *ok = true;
329 const QString cleaned = name.toLower().trimmed();
330
331 if ( cleaned == "square"_L1 || cleaned == "rectangle"_L1 )
333 else if ( cleaned == "trapezoid"_L1 )
335 else if ( cleaned == "parallelogram_right"_L1 )
337 else if ( cleaned == "parallelogram_left"_L1 )
339 else if ( cleaned == "square_with_corners"_L1 )
341 else if ( cleaned == "rounded_square"_L1 )
343 else if ( cleaned == "diamond"_L1 )
345 else if ( cleaned == "shield"_L1 )
347 else if ( cleaned == "pentagon"_L1 )
349 else if ( cleaned == "hexagon"_L1 )
351 else if ( cleaned == "octagon"_L1 )
353 else if ( cleaned == "decagon"_L1 )
355 else if ( cleaned == "triangle"_L1 )
357 else if ( cleaned == "equilateral_triangle"_L1 )
359 else if ( cleaned == "star_diamond"_L1 )
361 else if ( cleaned == "star"_L1 || cleaned == "regular_star"_L1 )
363 else if ( cleaned == "heart"_L1 )
365 else if ( cleaned == "arrow"_L1 )
367 else if ( cleaned == "circle"_L1 )
369 else if ( cleaned == "cross"_L1 )
371 else if ( cleaned == "cross_fill"_L1 )
373 else if ( cleaned == "cross2"_L1 || cleaned == "x"_L1 )
375 else if ( cleaned == "line"_L1 )
377 else if ( cleaned == "arrowhead"_L1 )
379 else if ( cleaned == "filled_arrowhead"_L1 )
381 else if ( cleaned == "semi_circle"_L1 )
383 else if ( cleaned == "third_circle"_L1 )
385 else if ( cleaned == "quarter_circle"_L1 )
387 else if ( cleaned == "quarter_square"_L1 )
389 else if ( cleaned == "half_square"_L1 )
391 else if ( cleaned == "diagonal_half_square"_L1 )
393 else if ( cleaned == "right_half_triangle"_L1 )
395 else if ( cleaned == "left_half_triangle"_L1 )
397 else if ( cleaned == "asterisk_fill"_L1 )
399 else if ( cleaned == "half_arc"_L1 )
401 else if ( cleaned == "third_arc"_L1 )
403 else if ( cleaned == "quarter_arc"_L1 )
405
406 if ( ok )
407 *ok = false;
409}
410
412{
413 switch ( shape )
414 {
416 return u"square"_s;
418 return u"quarter_square"_s;
420 return u"half_square"_s;
422 return u"diagonal_half_square"_s;
424 return u"parallelogram_right"_s;
426 return u"parallelogram_left"_s;
428 return u"trapezoid"_s;
430 return u"shield"_s;
432 return u"diamond"_s;
434 return u"pentagon"_s;
436 return u"hexagon"_s;
438 return u"octagon"_s;
440 return u"decagon"_s;
442 return u"square_with_corners"_s;
444 return u"rounded_square"_s;
446 return u"triangle"_s;
448 return u"equilateral_triangle"_s;
450 return u"left_half_triangle"_s;
452 return u"right_half_triangle"_s;
454 return u"star_diamond"_s;
456 return u"star"_s;
458 return u"heart"_s;
460 return u"arrow"_s;
462 return u"filled_arrowhead"_s;
464 return u"cross_fill"_s;
466 return u"circle"_s;
468 return u"cross"_s;
470 return u"cross2"_s;
472 return u"line"_s;
474 return u"arrowhead"_s;
476 return u"semi_circle"_s;
478 return u"third_circle"_s;
480 return u"quarter_circle"_s;
482 return u"asterisk_fill"_s;
484 return u"half_arc"_s;
486 return u"third_arc"_s;
488 return u"quarter_arc"_s;
489 }
490 return QString();
491}
492
497
499{
500 polygon.clear();
501
502 switch ( shape )
503 {
505 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 1, 1 ) ) );
506 return true;
507
509 {
510 static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 0.6072;
511
512 polygon << QPointF( - VERTEX_OFFSET_FROM_ORIGIN, 1 )
513 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, 1 )
514 << QPointF( 1, VERTEX_OFFSET_FROM_ORIGIN )
515 << QPointF( 1, -VERTEX_OFFSET_FROM_ORIGIN )
516 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, -1 )
517 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, -1 )
518 << QPointF( -1, -VERTEX_OFFSET_FROM_ORIGIN )
519 << QPointF( -1, VERTEX_OFFSET_FROM_ORIGIN )
520 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 );
521 return true;
522 }
523
525 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 0 ) ) );
526 return true;
527
529 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 1 ) ) );
530 return true;
531
533 polygon << QPointF( -1, -1 ) << QPointF( 1, 1 ) << QPointF( -1, 1 ) << QPointF( -1, -1 );
534 return true;
535
537 polygon << QPointF( 0.5, -0.5 )
538 << QPointF( 1, 0.5 )
539 << QPointF( -1, 0.5 )
540 << QPointF( -0.5, -0.5 )
541 << QPointF( 0.5, -0.5 );
542 return true;
543
545 polygon << QPointF( 0.5, 0.5 )
546 << QPointF( 1, -0.5 )
547 << QPointF( -0.5, -0.5 )
548 << QPointF( -1, 0.5 )
549 << QPointF( 0.5, 0.5 );
550 return true;
551
553 polygon << QPointF( 1, 0.5 )
554 << QPointF( 0.5, -0.5 )
555 << QPointF( -1, -0.5 )
556 << QPointF( -0.5, 0.5 )
557 << QPointF( 1, 0.5 );
558 return true;
559
561 polygon << QPointF( -1, 0 ) << QPointF( 0, 1 )
562 << QPointF( 1, 0 ) << QPointF( 0, -1 ) << QPointF( -1, 0 );
563 return true;
564
566 polygon << QPointF( 1, 0.5 )
567 << QPointF( 1, -1 )
568 << QPointF( -1, -1 )
569 << QPointF( -1, 0.5 )
570 << QPointF( 0, 1 )
571 << QPointF( 1, 0.5 );
572 return true;
573
575 /* angular-representation of hardcoded values used
576 polygon << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288.0 ) ) )
577 << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) )
578 << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) )
579 << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) )
580 << QPointF( 0, -1 ); */
581 polygon << QPointF( -0.9511, -0.3090 )
582 << QPointF( -0.5878, 0.8090 )
583 << QPointF( 0.5878, 0.8090 )
584 << QPointF( 0.9511, -0.3090 )
585 << QPointF( 0, -1 )
586 << QPointF( -0.9511, -0.3090 );
587 return true;
588
590 /* angular-representation of hardcoded values used
591 polygon << QPointF( std::sin( DEG2RAD( 300.0 ) ), - std::cos( DEG2RAD( 300.0 ) ) )
592 << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
593 << QPointF( std::sin( DEG2RAD( 180.0 ) ), - std::cos( DEG2RAD( 180.0 ) ) )
594 << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
595 << QPointF( std::sin( DEG2RAD( 60.0 ) ), - std::cos( DEG2RAD( 60.0 ) ) )
596 << QPointF( 0, -1 ); */
597 polygon << QPointF( -0.8660, -0.5 )
598 << QPointF( -0.8660, 0.5 )
599 << QPointF( 0, 1 )
600 << QPointF( 0.8660, 0.5 )
601 << QPointF( 0.8660, -0.5 )
602 << QPointF( 0, -1 )
603 << QPointF( -0.8660, -0.5 );
604 return true;
605
607 {
608 static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 1.0 / ( 1 + M_SQRT2 );
609
610 polygon << QPointF( - VERTEX_OFFSET_FROM_ORIGIN, 1 )
611 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, 1 )
612 << QPointF( 1, VERTEX_OFFSET_FROM_ORIGIN )
613 << QPointF( 1, -VERTEX_OFFSET_FROM_ORIGIN )
614 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, -1 )
615 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, -1 )
616 << QPointF( -1, -VERTEX_OFFSET_FROM_ORIGIN )
617 << QPointF( -1, VERTEX_OFFSET_FROM_ORIGIN )
618 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 );
619 return true;
620 }
621
623 {
624
625 polygon << QPointF( 0.587785252, 0.809016994 )
626 << QPointF( 0.951056516, 0.309016994 )
627 << QPointF( 0.951056516, -0.309016994 )
628 << QPointF( 0.587785252, -0.809016994 )
629 << QPointF( 0, -1 )
630 << QPointF( -0.587785252, -0.809016994 )
631 << QPointF( -0.951056516, -0.309016994 )
632 << QPointF( -0.951056516, 0.309016994 )
633 << QPointF( -0.587785252, 0.809016994 )
634 << QPointF( 0, 1 )
635 << QPointF( 0.587785252, 0.809016994 );
636 return true;
637 }
638
640 polygon << QPointF( -1, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
641 return true;
642
644 /* angular-representation of hardcoded values used
645 polygon << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
646 << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
647 << QPointF( 0, -1 ); */
648 polygon << QPointF( -0.8660, 0.5 )
649 << QPointF( 0.8660, 0.5 )
650 << QPointF( 0, -1 )
651 << QPointF( -0.8660, 0.5 );
652 return true;
653
655 polygon << QPointF( 0, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( 0, 1 );
656 return true;
657
659 polygon << QPointF( -1, 1 ) << QPointF( 0, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
660 return true;
661
663 {
664 const double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
665
666 polygon << QPointF( inner_r * std::sin( DEG2RAD( 315.0 ) ), - inner_r * std::cos( DEG2RAD( 315.0 ) ) )
667 << QPointF( std::sin( DEG2RAD( 270 ) ), - std::cos( DEG2RAD( 270 ) ) )
668 << QPointF( inner_r * std::sin( DEG2RAD( 225.0 ) ), - inner_r * std::cos( DEG2RAD( 225.0 ) ) )
669 << QPointF( std::sin( DEG2RAD( 180 ) ), - std::cos( DEG2RAD( 180 ) ) )
670 << QPointF( inner_r * std::sin( DEG2RAD( 135.0 ) ), - inner_r * std::cos( DEG2RAD( 135.0 ) ) )
671 << QPointF( std::sin( DEG2RAD( 90 ) ), - std::cos( DEG2RAD( 90 ) ) )
672 << QPointF( inner_r * std::sin( DEG2RAD( 45.0 ) ), - inner_r * std::cos( DEG2RAD( 45.0 ) ) )
673 << QPointF( std::sin( DEG2RAD( 0 ) ), - std::cos( DEG2RAD( 0 ) ) )
674 << QPointF( inner_r * std::sin( DEG2RAD( 315.0 ) ), - inner_r * std::cos( DEG2RAD( 315.0 ) ) );
675 return true;
676 }
677
679 {
680 const double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
681
682 polygon << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ) // 324
683 << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288 ) ) ) // 288
684 << QPointF( inner_r * std::sin( DEG2RAD( 252.0 ) ), - inner_r * std::cos( DEG2RAD( 252.0 ) ) ) // 252
685 << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) ) // 216
686 << QPointF( 0, inner_r ) // 180
687 << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) ) // 144
688 << QPointF( inner_r * std::sin( DEG2RAD( 108.0 ) ), - inner_r * std::cos( DEG2RAD( 108.0 ) ) ) // 108
689 << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) ) // 72
690 << QPointF( inner_r * std::sin( DEG2RAD( 36.0 ) ), - inner_r * std::cos( DEG2RAD( 36.0 ) ) ) // 36
691 << QPointF( 0, -1 )
692 << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ); // 324; // 0
693 return true;
694 }
695
697 polygon << QPointF( 0, -1 )
698 << QPointF( 0.5, -0.5 )
699 << QPointF( 0.25, -0.5 )
700 << QPointF( 0.25, 1 )
701 << QPointF( -0.25, 1 )
702 << QPointF( -0.25, -0.5 )
703 << QPointF( -0.5, -0.5 )
704 << QPointF( 0, -1 );
705 return true;
706
708 polygon << QPointF( 0, 0 ) << QPointF( -1, 1 ) << QPointF( -1, -1 ) << QPointF( 0, 0 );
709 return true;
710
712 polygon << QPointF( -1, -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 << QPointF( 1, -0.2 )
721 << QPointF( 0.2, -0.2 )
722 << QPointF( 0.2, -1 )
723 << QPointF( -0.2, -1 )
724 << QPointF( -0.2, -0.2 )
725 << QPointF( -1, -0.2 );
726 return true;
727
729 {
730 static constexpr double THICKNESS = 0.3;
731 static constexpr double HALF_THICKNESS = THICKNESS / 2.0;
732 static constexpr double INTERSECTION_POINT = THICKNESS / M_SQRT2;
733 static constexpr double DIAGONAL1 = M_SQRT1_2 - INTERSECTION_POINT * 0.5;
734 static constexpr double DIAGONAL2 = M_SQRT1_2 + INTERSECTION_POINT * 0.5;
735
736 polygon << QPointF( -HALF_THICKNESS, -1 )
737 << QPointF( HALF_THICKNESS, -1 )
738 << QPointF( HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
739 << QPointF( DIAGONAL1, -DIAGONAL2 )
740 << QPointF( DIAGONAL2, -DIAGONAL1 )
741 << QPointF( HALF_THICKNESS + INTERSECTION_POINT, -HALF_THICKNESS )
742 << QPointF( 1, -HALF_THICKNESS )
743 << QPointF( 1, HALF_THICKNESS )
744 << QPointF( HALF_THICKNESS + INTERSECTION_POINT, HALF_THICKNESS )
745 << QPointF( DIAGONAL2, DIAGONAL1 )
746 << QPointF( DIAGONAL1, DIAGONAL2 )
747 << QPointF( HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
748 << QPointF( HALF_THICKNESS, 1 )
749 << QPointF( -HALF_THICKNESS, 1 )
750 << QPointF( -HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
751 << QPointF( -DIAGONAL1, DIAGONAL2 )
752 << QPointF( -DIAGONAL2, DIAGONAL1 )
753 << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, HALF_THICKNESS )
754 << QPointF( -1, HALF_THICKNESS )
755 << QPointF( -1, -HALF_THICKNESS )
756 << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, -HALF_THICKNESS )
757 << QPointF( -DIAGONAL2, -DIAGONAL1 )
758 << QPointF( -DIAGONAL1, -DIAGONAL2 )
759 << QPointF( -HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
760 << QPointF( -HALF_THICKNESS, -1 );
761 return true;
762 }
763
777 return false;
778 }
779
780 return false;
781}
782
784{
785 mPath = QPainterPath();
786
787 switch ( symbol )
788 {
790
791 mPath.addEllipse( QRectF( -1, -1, 2, 2 ) ); // x,y,w,h
792 return true;
793
795 mPath.moveTo( -1, -1 );
796 mPath.addRoundedRect( -1, -1, 2, 2, 0.25, 0.25 );
797 return true;
798
800 mPath.arcTo( -1, -1, 2, 2, 0, 180 );
801 mPath.lineTo( 0, 0 );
802 return true;
803
805 mPath.arcTo( -1, -1, 2, 2, 90, 120 );
806 mPath.lineTo( 0, 0 );
807 return true;
808
810 mPath.arcTo( -1, -1, 2, 2, 90, 90 );
811 mPath.lineTo( 0, 0 );
812 return true;
813
815 mPath.moveTo( 1, 0 );
816 mPath.arcTo( -1, -1, 2, 2, 0, 180 );
817 return true;
818
820 mPath.moveTo( 0, -1 );
821 mPath.arcTo( -1, -1, 2, 2, 90, 120 );
822 return true;
823
825 mPath.moveTo( 0, -1 );
826 mPath.arcTo( -1, -1, 2, 2, 90, 90 );
827 return true;
828
830 mPath.moveTo( -1, 0 );
831 mPath.lineTo( 1, 0 ); // horizontal
832 mPath.moveTo( 0, -1 );
833 mPath.lineTo( 0, 1 ); // vertical
834 return true;
835
837 mPath.moveTo( -1, -1 );
838 mPath.lineTo( 1, 1 );
839 mPath.moveTo( 1, -1 );
840 mPath.lineTo( -1, 1 );
841 return true;
842
844 mPath.moveTo( 0, -1 );
845 mPath.lineTo( 0, 1 ); // vertical line
846 return true;
847
849 mPath.moveTo( -1, -1 );
850 mPath.lineTo( 0, 0 );
851 mPath.lineTo( -1, 1 );
852 return true;
853
855 mPath.moveTo( 0, 0.75 );
856 mPath.arcTo( 0, -1, 1, 1, -45, 210 );
857 mPath.arcTo( -1, -1, 1, 1, 15, 210 );
858 mPath.lineTo( 0, 0.75 );
859 return true;
860
885 return false;
886 }
887 return false;
888}
889
890double QgsSimpleMarkerSymbolLayerBase::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
891{
892 double scaledSize = mSize;
893
894 hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
895 bool ok = true;
896 if ( hasDataDefinedSize )
897 {
900 mSize, &ok );
901 }
902
903 if ( hasDataDefinedSize && ok )
904 {
905 switch ( mScaleMethod )
906 {
908 scaledSize = std::sqrt( scaledSize );
909 break;
911 break;
912 }
913 }
914
915 return scaledSize;
916}
917
918void QgsSimpleMarkerSymbolLayerBase::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle ) const
919{
920 //offset
921 double offsetX = 0;
922 double offsetY = 0;
923 markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
924 offset = QPointF( offsetX, offsetY );
925
926 hasDataDefinedRotation = false;
927 //angle
928 bool ok = true;
931 {
934
935 // If the expression evaluation was not successful, fallback to static value
936 if ( !ok )
938
939 hasDataDefinedRotation = true;
940 }
941
942 hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || hasDataDefinedRotation;
943
944 if ( hasDataDefinedRotation )
945 {
946 // For non-point markers, "dataDefinedRotation" means following the
947 // shape (shape-data defined). For them, "field-data defined" does
948 // not work at all. TODO: if "field-data defined" ever gets implemented
949 // we'll need a way to distinguish here between the two, possibly
950 // using another flag in renderHints()
951 const QgsFeature *f = context.feature();
952 if ( f )
953 {
954 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
955 {
956 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
957 angle += m2p.mapRotation();
958 }
959 }
960 }
961
962 if ( angle )
964}
965
966
967//
968// QgsSimpleMarkerSymbolLayer
969//
970
978
980
982{
990
991 if ( props.contains( u"name"_s ) )
992 {
993 shape = decodeShape( props[u"name"_s].toString() );
994 }
995 if ( props.contains( u"color"_s ) )
996 color = QgsColorUtils::colorFromString( props[u"color"_s].toString() );
997 if ( props.contains( u"color_border"_s ) )
998 {
999 //pre 2.5 projects use "color_border"
1000 strokeColor = QgsColorUtils::colorFromString( props[u"color_border"_s].toString() );
1001 }
1002 else if ( props.contains( u"outline_color"_s ) )
1003 {
1004 strokeColor = QgsColorUtils::colorFromString( props[u"outline_color"_s].toString() );
1005 }
1006 else if ( props.contains( u"line_color"_s ) )
1007 {
1008 strokeColor = QgsColorUtils::colorFromString( props[u"line_color"_s].toString() );
1009 }
1010 if ( props.contains( u"joinstyle"_s ) )
1011 {
1012 penJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( props[u"joinstyle"_s].toString() );
1013 }
1014 if ( props.contains( u"size"_s ) )
1015 size = props[u"size"_s].toDouble();
1016 if ( props.contains( u"angle"_s ) )
1017 angle = props[u"angle"_s].toDouble();
1018 if ( props.contains( u"scale_method"_s ) )
1019 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[u"scale_method"_s].toString() );
1020
1022 if ( props.contains( u"offset"_s ) )
1023 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[u"offset"_s].toString() ) );
1024 if ( props.contains( u"offset_unit"_s ) )
1025 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[u"offset_unit"_s].toString() ) );
1026 if ( props.contains( u"offset_map_unit_scale"_s ) )
1027 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"offset_map_unit_scale"_s].toString() ) );
1028 if ( props.contains( u"size_unit"_s ) )
1029 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[u"size_unit"_s].toString() ) );
1030 if ( props.contains( u"size_map_unit_scale"_s ) )
1031 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"size_map_unit_scale"_s].toString() ) );
1032
1033 if ( props.contains( u"outline_style"_s ) )
1034 {
1035 m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[u"outline_style"_s].toString() ) );
1036 }
1037 else if ( props.contains( u"line_style"_s ) )
1038 {
1039 m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[u"line_style"_s].toString() ) );
1040 }
1041 if ( props.contains( u"outline_width"_s ) )
1042 {
1043 m->setStrokeWidth( props[u"outline_width"_s].toDouble() );
1044 }
1045 else if ( props.contains( u"line_width"_s ) )
1046 {
1047 m->setStrokeWidth( props[u"line_width"_s].toDouble() );
1048 }
1049 if ( props.contains( u"outline_width_unit"_s ) )
1050 {
1051 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[u"outline_width_unit"_s].toString() ) );
1052 }
1053 if ( props.contains( u"line_width_unit"_s ) )
1054 {
1055 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[u"line_width_unit"_s].toString() ) );
1056 }
1057 if ( props.contains( u"outline_width_map_unit_scale"_s ) )
1058 {
1059 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"outline_width_map_unit_scale"_s].toString() ) );
1060 }
1061
1062 if ( props.contains( u"horizontal_anchor_point"_s ) )
1063 {
1064 m->setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( props[ u"horizontal_anchor_point"_s].toInt() ) );
1065 }
1066 if ( props.contains( u"vertical_anchor_point"_s ) )
1067 {
1068 m->setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( props[ u"vertical_anchor_point"_s].toInt() ) );
1069 }
1070
1071 if ( props.contains( u"cap_style"_s ) )
1072 {
1073 m->setPenCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( props[u"cap_style"_s].toString() ) );
1074 }
1075
1077
1078 return m;
1079}
1080
1081
1083{
1084 return u"SimpleMarker"_s;
1085}
1086
1091
1093{
1095
1096 QColor brushColor = mColor;
1097 QColor penColor = mStrokeColor;
1098
1099 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
1100 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
1101
1102 mBrush = QBrush( brushColor );
1103 mPen = QPen( penColor );
1104 mPen.setStyle( mStrokeStyle );
1105 mPen.setCapStyle( mPenCapStyle );
1106 mPen.setJoinStyle( mPenJoinStyle );
1108
1109 QColor selBrushColor = context.renderContext().selectionColor();
1110 QColor selPenColor = selBrushColor == mColor ? selBrushColor : mStrokeColor;
1111 if ( context.opacity() < 1 && !SELECTION_IS_OPAQUE )
1112 {
1113 selBrushColor.setAlphaF( context.opacity() );
1114 selPenColor.setAlphaF( context.opacity() );
1115 }
1116 mSelBrush = QBrush( selBrushColor );
1117 mSelPen = QPen( selPenColor );
1118 mSelPen.setStyle( mStrokeStyle );
1120
1121 const bool hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Angle );
1122 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
1123
1124 // use caching only when:
1125 // - size, rotation, shape, color, stroke color is not data-defined
1126 // - drawing to screen (not printer)
1127 mUsingCache = !hasDataDefinedRotation && !hasDataDefinedSize && !context.forceVectorRendering()
1131
1132 if ( mUsingCache )
1133 mCachedOpacity = context.opacity();
1134
1135 if ( !shapeIsFilled( mShape ) )
1136 {
1137 // some markers can't be drawn as a polygon (circle, cross)
1138 // For these set the selected stroke color to the selected color
1139 mSelPen.setColor( selBrushColor );
1140 }
1141
1142
1143 if ( mUsingCache )
1144 {
1145 if ( !prepareCache( context ) )
1146 {
1147 mUsingCache = false;
1148 }
1149 }
1150 else
1151 {
1152 mCache = QImage();
1153 mSelCache = QImage();
1154 }
1155}
1156
1157
1159{
1160 double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
1161 const double deviceRatio = context.renderContext().devicePixelRatio();
1163 {
1164 // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
1165 // and clamp it to a reasonable range. It's the best we can do in this situation!
1166 scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
1167 }
1168
1169 // take into account angle (which is not data-defined otherwise cache wouldn't be used)
1170 if ( !qgsDoubleNear( mAngle, 0.0 ) )
1171 {
1172 scaledSize = ( std::abs( std::sin( mAngle * M_PI / 180 ) ) + std::abs( std::cos( mAngle * M_PI / 180 ) ) ) * scaledSize;
1173 }
1174 // calculate necessary image size for the cache
1175 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
1176 const int imageSize = ( static_cast< int >( scaledSize ) + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width
1177 const double center = imageSize / 2.0;
1178 if ( imageSize * deviceRatio > MAXIMUM_CACHE_WIDTH )
1179 {
1180 return false;
1181 }
1182
1183 mCache = QImage( QSize( imageSize * deviceRatio,
1184 imageSize * deviceRatio ), QImage::Format_ARGB32_Premultiplied );
1185 mCache.setDevicePixelRatio( context.renderContext().devicePixelRatio() );
1186 mCache.setDotsPerMeterX( std::round( context.renderContext().scaleFactor() * 1000 ) );
1187 mCache.setDotsPerMeterY( std::round( context.renderContext().scaleFactor() * 1000 ) );
1188 mCache.fill( 0 );
1189
1190 const bool needsBrush = shapeIsFilled( mShape );
1191
1192 QPainter p;
1193 p.begin( &mCache );
1194 p.setRenderHint( QPainter::Antialiasing );
1195 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1196 p.setPen( mPen );
1197 p.translate( QPointF( center, center ) );
1198 drawMarker( &p, context );
1199 p.end();
1200
1201 // Construct the selected version of the Cache
1202
1203 const QColor selColor = context.renderContext().selectionColor();
1204
1205 mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
1206 mSelCache.fill( 0 );
1207
1208 p.begin( &mSelCache );
1209 p.setRenderHint( QPainter::Antialiasing );
1210 p.setBrush( needsBrush ? mSelBrush : Qt::NoBrush );
1211 p.setPen( mSelPen );
1212 p.translate( QPointF( center, center ) );
1213 drawMarker( &p, context );
1214 p.end();
1215
1216 // Check that the selected version is different. If not, then re-render,
1217 // filling the background with the selection color and using the normal
1218 // colors for the symbol .. could be ugly!
1219
1220 if ( mSelCache == mCache )
1221 {
1222 p.begin( &mSelCache );
1223 p.setRenderHint( QPainter::Antialiasing );
1224 p.fillRect( 0, 0, imageSize, imageSize, selColor );
1225 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1226 p.setPen( mPen );
1227 p.translate( QPointF( center, center ) );
1228 drawMarker( &p, context );
1229 p.end();
1230 }
1231
1232 return true;
1233}
1234
1235void QgsSimpleMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
1236{
1237 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1238 //of the rendered point!
1239
1240 QPainter *p = context.renderContext().painter();
1241 if ( !p )
1242 {
1243 return;
1244 }
1245
1246 QColor brushColor = mColor;
1247 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
1248 mBrush.setColor( brushColor );
1249
1250 QColor penColor = mStrokeColor;
1251 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
1252 mPen.setColor( penColor );
1253
1254 bool ok = true;
1256 {
1259 if ( ok )
1260 {
1261 c.setAlphaF( c.alphaF() * context.opacity() );
1262 mBrush.setColor( c );
1263 }
1264 }
1266 {
1269 if ( ok )
1270 {
1271 c.setAlphaF( c.alphaF() * context.opacity() );
1272 mPen.setColor( c );
1273 mSelPen.setColor( c );
1274 }
1275 }
1277 {
1280 if ( ok )
1281 {
1284 }
1285 }
1287 {
1289 const QString strokeStyle = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::StrokeStyle, context.renderContext().expressionContext(), QString(), &ok );
1290 if ( ok )
1291 {
1294 }
1295 }
1297 {
1299 const QString style = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::JoinStyle, context.renderContext().expressionContext(), QString(), &ok );
1300 if ( ok )
1301 {
1302 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1303 mSelPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1304 }
1305 }
1307 {
1309 const QString style = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::CapStyle, context.renderContext().expressionContext(), QString(), &ok );
1310 if ( ok )
1311 {
1312 mPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1313 mSelPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1314 }
1315 }
1316
1317 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
1318 if ( shapeIsFilled( shape ) )
1319 {
1320 p->setBrush( useSelectedColor ? mSelBrush : mBrush );
1321 }
1322 else
1323 {
1324 p->setBrush( Qt::NoBrush );
1325 }
1326 p->setPen( useSelectedColor ? mSelPen : mPen );
1327
1328 if ( !polygon.isEmpty() )
1329 p->drawPolygon( polygon );
1330 else
1331 p->drawPath( path );
1332}
1333
1335{
1336 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1337 //of the rendered point!
1338
1339 QPainter *p = context.renderContext().painter();
1340 if ( !p )
1341 {
1342 return;
1343 }
1344
1345 if ( mUsingCache && qgsDoubleNear( mCachedOpacity, context.opacity() ) )
1346 {
1347 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
1348 const QImage &img = useSelectedColor ? mSelCache : mCache;
1349 const double s = img.width() / img.devicePixelRatioF();
1350
1351 bool hasDataDefinedSize = false;
1352 const double scaledSize = calculateSize( context, hasDataDefinedSize );
1353
1354 bool hasDataDefinedRotation = false;
1355 QPointF offset;
1356 double angle = 0;
1357 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
1358
1359 p->drawImage( QRectF( point.x() - s / 2.0 + offset.x(),
1360 point.y() - s / 2.0 + offset.y(),
1361 s, s ), img );
1362 }
1363 else
1364 {
1366 }
1367}
1368
1370{
1371 QVariantMap map;
1372 map[u"name"_s] = encodeShape( mShape );
1373 map[u"color"_s] = QgsColorUtils::colorToString( mColor );
1374 map[u"outline_color"_s] = QgsColorUtils::colorToString( mStrokeColor );
1375 map[u"size"_s] = QString::number( mSize );
1376 map[u"size_unit"_s] = QgsUnitTypes::encodeUnit( mSizeUnit );
1377 map[u"size_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1378 map[u"angle"_s] = QString::number( mAngle );
1379 map[u"offset"_s] = QgsSymbolLayerUtils::encodePoint( mOffset );
1380 map[u"offset_unit"_s] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1381 map[u"offset_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1382 map[u"scale_method"_s] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1383 map[u"outline_style"_s] = QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle );
1384 map[u"outline_width"_s] = QString::number( mStrokeWidth );
1385 map[u"outline_width_unit"_s] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
1386 map[u"outline_width_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
1389 map[u"horizontal_anchor_point"_s] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
1390 map[u"vertical_anchor_point"_s] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
1391 return map;
1392}
1393
1413
1414void QgsSimpleMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1415{
1416 QgsSldExportContext context;
1417 context.setExtraProperties( props );
1418 toSld( doc, element, context );
1419}
1420
1421bool QgsSimpleMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
1422{
1423 return QgsSimpleMarkerSymbolLayerBase::toSld( doc, element, context );
1424}
1425
1426void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1427{
1428 QgsSldExportContext context;
1429 context.setExtraProperties( props );
1430 writeSldMarker( doc, element, context );
1431}
1432
1433bool QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
1434{
1435 // <Graphic>
1436 QDomElement graphicElem = doc.createElement( u"se:Graphic"_s );
1437 element.appendChild( graphicElem );
1438
1439 const QVariantMap props = context.extraProperties();
1441 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
1443
1444 // <Rotation>
1445 QString angleFunc;
1446
1448 {
1449 angleFunc = mDataDefinedProperties.property( QgsSymbolLayer::Property::Angle ).asExpression();
1450 }
1451 else
1452 {
1453 bool ok;
1454 const double angle = props.value( u"angle"_s, u"0"_s ).toDouble( &ok );
1455 if ( !ok )
1456 {
1457 angleFunc = u"%1 + %2"_s.arg( props.value( u"angle"_s, u"0"_s ).toString() ).arg( mAngle );
1458 }
1459 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
1460 {
1461 angleFunc = QString::number( angle + mAngle );
1462 }
1463 }
1464
1465 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc, context );
1466
1467 // <Displacement>
1468 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
1470 return true;
1471}
1472
1473QString QgsSimpleMarkerSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
1474{
1475 Q_UNUSED( mmScaleFactor )
1476 Q_UNUSED( mapUnitScaleFactor )
1477#if 0
1478 QString ogrType = "3"; //default is circle
1479 if ( mName == "square" )
1480 {
1481 ogrType = "5";
1482 }
1483 else if ( mName == "triangle" )
1484 {
1485 ogrType = "7";
1486 }
1487 else if ( mName == "star" )
1488 {
1489 ogrType = "9";
1490 }
1491 else if ( mName == "circle" )
1492 {
1493 ogrType = "3";
1494 }
1495 else if ( mName == "cross" )
1496 {
1497 ogrType = "0";
1498 }
1499 else if ( mName == "x" || mName == "cross2" )
1500 {
1501 ogrType = "1";
1502 }
1503 else if ( mName == "line" )
1504 {
1505 ogrType = "10";
1506 }
1507
1508 QString ogrString;
1509 ogrString.append( "SYMBOL(" );
1510 ogrString.append( "id:" );
1511 ogrString.append( '\"' );
1512 ogrString.append( "ogr-sym-" );
1513 ogrString.append( ogrType );
1514 ogrString.append( '\"' );
1515 ogrString.append( ",c:" );
1516 ogrString.append( mColor.name() );
1517 ogrString.append( ",o:" );
1518 ogrString.append( mStrokeColor.name() );
1519 ogrString.append( QString( ",s:%1mm" ).arg( mSize ) );
1520 ogrString.append( ')' );
1521 return ogrString;
1522#endif //0
1523
1524 QString ogrString;
1525 ogrString.append( "PEN(" );
1526 ogrString.append( "c:" );
1527 ogrString.append( mColor.name() );
1528 ogrString.append( ",w:" );
1529 ogrString.append( QString::number( mSize ) );
1530 ogrString.append( "mm" );
1531 ogrString.append( ")" );
1532 return ogrString;
1533}
1534
1536{
1537 QgsDebugMsgLevel( u"Entered."_s, 4 );
1538
1539 QDomElement graphicElem = element.firstChildElement( u"Graphic"_s );
1540 if ( graphicElem.isNull() )
1541 return nullptr;
1542
1543 QString name = u"square"_s;
1544 QColor color, strokeColor;
1545 double strokeWidth, size;
1546 Qt::PenStyle strokeStyle;
1547
1549 return nullptr;
1550
1551 double angle = 0.0;
1552 QString angleFunc;
1553 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
1554 {
1555 bool ok;
1556 const double d = angleFunc.toDouble( &ok );
1557 if ( ok )
1558 angle = d;
1559 }
1560
1561 QPointF offset;
1563
1564 const Qgis::MarkerShape shape = decodeShape( name );
1565
1566 double scaleFactor = 1.0;
1567 const QString uom = element.attribute( u"uom"_s );
1568 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
1569 size = size * scaleFactor;
1570 offset.setX( offset.x() * scaleFactor );
1571 offset.setY( offset.y() * scaleFactor );
1572
1574 m->setOutputUnit( sldUnitSize );
1575 m->setColor( color );
1577 m->setAngle( angle );
1578 m->setOffset( offset );
1581 return m;
1582}
1583
1585{
1586 Q_UNUSED( context )
1587
1588 if ( mPolygon.count() != 0 )
1589 {
1590 p->drawPolygon( mPolygon );
1591 }
1592 else
1593 {
1594 p->drawPath( mPath );
1595 }
1596}
1597
1598bool QgsSimpleMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
1599{
1600 //data defined size?
1601 double size = mSize;
1602
1603 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
1604
1605 //data defined size
1606 bool ok = true;
1607 if ( hasDataDefinedSize )
1608 {
1610
1611 if ( ok )
1612 {
1613 switch ( mScaleMethod )
1614 {
1616 size = std::sqrt( size );
1617 break;
1619 break;
1620 }
1621 }
1622 }
1623
1625 {
1626 size *= mmMapUnitScaleFactor;
1627 }
1628
1630 {
1632 }
1633 const double halfSize = size / 2.0;
1634
1635 //strokeWidth
1636 double strokeWidth = mStrokeWidth;
1637
1639 {
1642 }
1645 {
1647 }
1648
1649 //color
1650 QColor pc = mPen.color();
1651 QColor bc = mBrush.color();
1653 {
1656 }
1658 {
1661 }
1662
1663 //offset
1664 double offsetX = 0;
1665 double offsetY = 0;
1666 markerOffset( context, offsetX, offsetY );
1667 offsetX *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1668 offsetY *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1669
1670
1671 QPointF off( offsetX, offsetY );
1672
1673 //angle
1674 double angle = mAngle + mLineAngle;
1676 {
1679 }
1680
1683 {
1685 const QString shapeName = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::Name, context.renderContext().expressionContext(), QString(), &ok );
1686 if ( ok )
1687 {
1688 shape = decodeShape( shapeName, &ok );
1689 if ( !ok )
1690 shape = mShape;
1691 }
1692 }
1693
1694 if ( angle )
1695 off = _rotatedOffset( off, angle );
1696
1698
1699 QTransform t;
1700 t.translate( shift.x() + off.x(), shift.y() - off.y() );
1701
1702 if ( !qgsDoubleNear( angle, 0.0 ) )
1703 t.rotate( -angle );
1704
1705 QPolygonF polygon;
1706 if ( shapeToPolygon( shape, polygon ) )
1707 {
1708 t.scale( halfSize, -halfSize );
1709
1710 polygon = t.map( polygon );
1711
1713 p.reserve( polygon.size() );
1714 for ( int i = 0; i < polygon.size(); i++ )
1715 {
1716 p << QgsPoint( polygon[i] );
1717 }
1718
1719 if ( mBrush.style() != Qt::NoBrush )
1720 e.writePolygon( QgsRingSequence() << p, layerName, u"SOLID"_s, bc );
1721 if ( mPen.style() != Qt::NoPen )
1722 e.writePolyline( p, layerName, u"CONTINUOUS"_s, pc, strokeWidth );
1723 }
1724 else if ( shape == Qgis::MarkerShape::Circle )
1725 {
1726 shift += QPointF( off.x(), -off.y() );
1727 if ( mBrush.style() != Qt::NoBrush )
1728 e.writeFilledCircle( layerName, bc, QgsPoint( shift ), halfSize );
1729 if ( mPen.style() != Qt::NoPen )
1730 e.writeCircle( layerName, pc, QgsPoint( shift ), halfSize, u"CONTINUOUS"_s, strokeWidth );
1731 }
1732 else if ( shape == Qgis::MarkerShape::Line )
1733 {
1734 const QPointF pt1 = t.map( QPointF( 0, -halfSize ) );
1735 const QPointF pt2 = t.map( QPointF( 0, halfSize ) );
1736
1737 if ( mPen.style() != Qt::NoPen )
1738 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, u"CONTINUOUS"_s, pc, strokeWidth );
1739 }
1740 else if ( shape == Qgis::MarkerShape::Cross )
1741 {
1742 if ( mPen.style() != Qt::NoPen )
1743 {
1744 const QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
1745 const QPointF pt2 = t.map( QPointF( halfSize, 0 ) );
1746 const QPointF pt3 = t.map( QPointF( 0, -halfSize ) );
1747 const QPointF pt4 = t.map( QPointF( 0, halfSize ) );
1748
1749 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, u"CONTINUOUS"_s, pc, strokeWidth );
1750 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, u"CONTINUOUS"_s, pc, strokeWidth );
1751 }
1752 }
1753 else if ( shape == Qgis::MarkerShape::Cross2 )
1754 {
1755 if ( mPen.style() != Qt::NoPen )
1756 {
1757 const QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
1758 const QPointF pt2 = t.map( QPointF( halfSize, halfSize ) );
1759 const QPointF pt3 = t.map( QPointF( halfSize, -halfSize ) );
1760 const QPointF pt4 = t.map( QPointF( -halfSize, halfSize ) );
1761
1762 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, u"CONTINUOUS"_s, pc, strokeWidth );
1763 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, u"CONTINUOUS"_s, pc, strokeWidth );
1764 }
1765 }
1766 else if ( shape == Qgis::MarkerShape::ArrowHead )
1767 {
1768 if ( mPen.style() != Qt::NoPen )
1769 {
1770 const QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
1771 const QPointF pt2 = t.map( QPointF( 0, 0 ) );
1772 const QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
1773
1774 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, u"CONTINUOUS"_s, pc, strokeWidth );
1775 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt2 ), layerName, u"CONTINUOUS"_s, pc, strokeWidth );
1776 }
1777 }
1778 else
1779 {
1780 QgsDebugError( u"Unsupported dxf marker name %1"_s.arg( encodeShape( shape ) ) );
1781 return false;
1782 }
1783
1784 return true;
1785}
1786
1787
1793
1802
1808
1817
1824
1826{
1827 QRectF symbolBounds = QgsSimpleMarkerSymbolLayerBase::bounds( point, context );
1828
1829 // need to account for stroke width
1830 double penWidth = mStrokeWidth;
1831 bool ok = true;
1833 {
1836 if ( ok )
1837 {
1838 penWidth = strokeWidth;
1839 }
1840 }
1843 {
1845 const QString strokeStyle = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::StrokeStyle, context.renderContext().expressionContext(), QString(), &ok );
1846 if ( ok && strokeStyle == "no"_L1 )
1847 {
1848 penWidth = 0.0;
1849 }
1850 }
1851 else if ( mStrokeStyle == Qt::NoPen )
1852 penWidth = 0;
1853
1854 //antialiasing, add 1 pixel
1855 penWidth += 1;
1856
1857 //extend bounds by pen width / 2.0
1858 symbolBounds.adjust( -penWidth / 2.0, -penWidth / 2.0,
1859 penWidth / 2.0, penWidth / 2.0 );
1860
1861 return symbolBounds;
1862}
1863
1865{
1866 if ( shapeIsFilled( mShape ) )
1867 {
1869 }
1870 else
1871 {
1873 }
1874}
1875
1877{
1878 if ( shapeIsFilled( mShape ) )
1879 {
1880 return fillColor();
1881 }
1882 else
1883 {
1884 return strokeColor();
1885 }
1886}
1887
1888
1889
1890
1891//
1892// QgsFilledMarkerSymbolLayer
1893//
1894
1900
1902
1904{
1905 QString name = DEFAULT_SIMPLEMARKER_NAME;
1909
1910 if ( props.contains( u"name"_s ) )
1911 name = props[u"name"_s].toString();
1912 if ( props.contains( u"size"_s ) )
1913 size = props[u"size"_s].toDouble();
1914 if ( props.contains( u"angle"_s ) )
1915 angle = props[u"angle"_s].toDouble();
1916 if ( props.contains( u"scale_method"_s ) )
1917 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[u"scale_method"_s].toString() );
1918
1920 if ( props.contains( u"offset"_s ) )
1921 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[u"offset"_s].toString() ) );
1922 if ( props.contains( u"offset_unit"_s ) )
1923 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[u"offset_unit"_s].toString() ) );
1924 if ( props.contains( u"offset_map_unit_scale"_s ) )
1925 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"offset_map_unit_scale"_s].toString() ) );
1926 if ( props.contains( u"size_unit"_s ) )
1927 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[u"size_unit"_s].toString() ) );
1928 if ( props.contains( u"size_map_unit_scale"_s ) )
1929 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"size_map_unit_scale"_s].toString() ) );
1930 if ( props.contains( u"horizontal_anchor_point"_s ) )
1931 {
1932 m->setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( props[ u"horizontal_anchor_point"_s].toInt() ) );
1933 }
1934 if ( props.contains( u"vertical_anchor_point"_s ) )
1935 {
1936 m->setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( props[ u"vertical_anchor_point"_s].toInt() ) );
1937 }
1938
1939 m->setSubSymbol( QgsFillSymbol::createSimple( props ).release() );
1940
1942
1943 return m;
1944}
1945
1947{
1948 return u"FilledMarker"_s;
1949}
1950
1952{
1953 if ( mFill )
1954 {
1955 mFill->setRenderHints( mFill->renderHints() | Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol );
1956 mFill->startRender( context.renderContext(), context.fields() );
1957 }
1958
1960}
1961
1963{
1964 if ( mFill )
1965 {
1966 mFill->stopRender( context.renderContext() );
1967 }
1968}
1969
1971{
1972 installMasks( context, true );
1973
1974 // The base class version passes this on to the subsymbol, but we deliberately don't do that here.
1975}
1976
1978{
1979 removeMasks( context, true );
1980
1981 // The base class version passes this on to the subsymbol, but we deliberately don't do that here.
1982}
1983
1985{
1986 QVariantMap map;
1987 map[u"name"_s] = encodeShape( mShape );
1988 map[u"size"_s] = QString::number( mSize );
1989 map[u"size_unit"_s] = QgsUnitTypes::encodeUnit( mSizeUnit );
1990 map[u"size_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1991 map[u"angle"_s] = QString::number( mAngle );
1992 map[u"offset"_s] = QgsSymbolLayerUtils::encodePoint( mOffset );
1993 map[u"offset_unit"_s] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1994 map[u"offset_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1995 map[u"scale_method"_s] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1996 map[u"horizontal_anchor_point"_s] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
1997 map[u"vertical_anchor_point"_s] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
1998
1999 if ( mFill )
2000 {
2001 map[u"color"_s] = QgsColorUtils::colorToString( mFill->color() );
2002 }
2003 return map;
2004}
2005
2014
2016{
2017 return mFill.get();
2018}
2019
2021{
2022 if ( symbol && symbol->type() == Qgis::SymbolType::Fill )
2023 {
2024 mFill.reset( static_cast<QgsFillSymbol *>( symbol ) );
2025 return true;
2026 }
2027 else
2028 {
2029 delete symbol;
2030 return false;
2031 }
2032}
2033
2035{
2036 if ( mFill )
2037 {
2038 return QgsSymbolLayerUtils::estimateMaxSymbolBleed( mFill.get(), context );
2039 }
2040 return 0;
2041}
2042
2044{
2045 QSet<QString> attr = QgsSimpleMarkerSymbolLayerBase::usedAttributes( context );
2046 if ( mFill )
2047 attr.unite( mFill->usedAttributes( context ) );
2048 return attr;
2049}
2050
2052{
2054 return true;
2055 if ( mFill && mFill->hasDataDefinedProperties() )
2056 return true;
2057 return false;
2058}
2059
2061{
2062 mColor = c;
2063 if ( mFill )
2064 mFill->setColor( c );
2065}
2066
2068{
2069 return mFill ? mFill->color() : mColor;
2070}
2071
2078
2080{
2082 if ( mFill )
2083 mFill->setOutputUnit( unit );
2084}
2085
2086void QgsFilledMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
2087{
2088 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
2089 //of the rendered point!
2090
2091 QPainter *p = context.renderContext().painter();
2092 if ( !p )
2093 {
2094 return;
2095 }
2096
2097 const double prevOpacity = mFill->opacity();
2098 mFill->setOpacity( mFill->opacity() * context.opacity() );
2099
2100 if ( shapeIsFilled( shape ) )
2101 {
2102 p->setBrush( Qt::red );
2103 }
2104 else
2105 {
2106 p->setBrush( Qt::NoBrush );
2107 }
2108 p->setPen( Qt::black );
2109
2110 const bool prevIsSubsymbol = context.renderContext().flags() & Qgis::RenderContextFlag::RenderingSubSymbol;
2112
2113 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
2114 if ( !polygon.isEmpty() )
2115 {
2116 mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
2117 }
2118 else
2119 {
2120 const QPolygonF poly = path.toFillPolygon();
2121 mFill->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
2122 }
2123
2125
2126 mFill->setOpacity( prevOpacity );
2127}
2128
2129
2131
2132
2134{
2135 mSize = size;
2136 mAngle = angle;
2137 mOffset = QPointF( 0, 0 );
2139 mStrokeWidth = 0.2;
2141 mColor = QColor( 35, 35, 35 );
2142 mStrokeColor = QColor( 35, 35, 35 );
2143 setPath( path );
2144}
2145
2159
2161
2163{
2164 QString name;
2168
2169 if ( props.contains( u"name"_s ) )
2170 name = props[u"name"_s].toString();
2171 if ( props.contains( u"size"_s ) )
2172 size = props[u"size"_s].toDouble();
2173 if ( props.contains( u"angle"_s ) )
2174 angle = props[u"angle"_s].toDouble();
2175 if ( props.contains( u"scale_method"_s ) )
2176 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[u"scale_method"_s].toString() );
2177
2179
2180 if ( props.contains( u"size_unit"_s ) )
2181 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[u"size_unit"_s].toString() ) );
2182 if ( props.contains( u"size_map_unit_scale"_s ) )
2183 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"size_map_unit_scale"_s].toString() ) );
2184 if ( props.contains( u"fixedAspectRatio"_s ) )
2185 m->setFixedAspectRatio( props[u"fixedAspectRatio"_s].toDouble() );
2186 if ( props.contains( u"offset"_s ) )
2187 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[u"offset"_s].toString() ) );
2188 if ( props.contains( u"offset_unit"_s ) )
2189 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[u"offset_unit"_s].toString() ) );
2190 if ( props.contains( u"offset_map_unit_scale"_s ) )
2191 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"offset_map_unit_scale"_s].toString() ) );
2192 if ( props.contains( u"fill"_s ) )
2193 {
2194 //pre 2.5 projects used "fill"
2195 m->setFillColor( QgsColorUtils::colorFromString( props[u"fill"_s].toString() ) );
2196 }
2197 else if ( props.contains( u"color"_s ) )
2198 {
2199 m->setFillColor( QgsColorUtils::colorFromString( props[u"color"_s].toString() ) );
2200 }
2201 if ( props.contains( u"outline"_s ) )
2202 {
2203 //pre 2.5 projects used "outline"
2204 m->setStrokeColor( QgsColorUtils::colorFromString( props[u"outline"_s].toString() ) );
2205 }
2206 else if ( props.contains( u"outline_color"_s ) )
2207 {
2208 m->setStrokeColor( QgsColorUtils::colorFromString( props[u"outline_color"_s].toString() ) );
2209 }
2210 else if ( props.contains( u"line_color"_s ) )
2211 {
2212 m->setStrokeColor( QgsColorUtils::colorFromString( props[u"line_color"_s].toString() ) );
2213 }
2214
2215 if ( props.contains( u"outline-width"_s ) )
2216 {
2217 //pre 2.5 projects used "outline-width"
2218 m->setStrokeWidth( props[u"outline-width"_s].toDouble() );
2219 }
2220 else if ( props.contains( u"outline_width"_s ) )
2221 {
2222 m->setStrokeWidth( props[u"outline_width"_s].toDouble() );
2223 }
2224 else if ( props.contains( u"line_width"_s ) )
2225 {
2226 m->setStrokeWidth( props[u"line_width"_s].toDouble() );
2227 }
2228
2229 if ( props.contains( u"outline_width_unit"_s ) )
2230 {
2231 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[u"outline_width_unit"_s].toString() ) );
2232 }
2233 else if ( props.contains( u"line_width_unit"_s ) )
2234 {
2235 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[u"line_width_unit"_s].toString() ) );
2236 }
2237 if ( props.contains( u"outline_width_map_unit_scale"_s ) )
2238 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"outline_width_map_unit_scale"_s].toString() ) );
2239
2240 if ( props.contains( u"horizontal_anchor_point"_s ) )
2241 {
2242 m->setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( props[ u"horizontal_anchor_point"_s].toInt() ) );
2243 }
2244 if ( props.contains( u"vertical_anchor_point"_s ) )
2245 {
2246 m->setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( props[ u"vertical_anchor_point"_s].toInt() ) );
2247 }
2248
2250
2252
2253 if ( props.contains( u"parameters"_s ) )
2254 {
2255 const QVariantMap parameters = props[u"parameters"_s].toMap();
2257 }
2258
2259 return m;
2260}
2261
2262void QgsSvgMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
2263{
2264 const QVariantMap::iterator it = properties.find( u"name"_s );
2265 if ( it != properties.end() )
2266 {
2267 if ( saving )
2268 {
2269 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
2270 }
2271 else
2272 {
2273 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
2274 }
2275 }
2276}
2277
2279{
2281 mHasFillParam = false;
2282 mPath = path;
2283 QColor defaultFillColor, defaultStrokeColor;
2284 double strokeWidth, fillOpacity, strokeOpacity;
2285 bool hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
2286 bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
2287 QgsApplication::svgCache()->containsParams( path, mHasFillParam, hasDefaultFillColor, defaultFillColor,
2288 hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
2289 hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2290 hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
2291 hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
2292
2293 const double newFillOpacity = hasFillOpacityParam ? fillColor().alphaF() : 1.0;
2294 const double newStrokeOpacity = hasStrokeOpacityParam ? strokeColor().alphaF() : 1.0;
2295
2296 if ( hasDefaultFillColor )
2297 {
2298 defaultFillColor.setAlphaF( newFillOpacity );
2299 setFillColor( defaultFillColor );
2300 }
2301 if ( hasDefaultFillOpacity )
2302 {
2303 QColor c = fillColor();
2304 c.setAlphaF( fillOpacity );
2305 setFillColor( c );
2306 }
2307 if ( hasDefaultStrokeColor )
2308 {
2309 defaultStrokeColor.setAlphaF( newStrokeOpacity );
2310 setStrokeColor( defaultStrokeColor );
2311 }
2312 if ( hasDefaultStrokeWidth )
2313 {
2315 }
2316 if ( hasDefaultStrokeOpacity )
2317 {
2318 QColor c = strokeColor();
2319 c.setAlphaF( strokeOpacity );
2320 setStrokeColor( c );
2321 }
2322
2324}
2325
2327{
2328 if ( mDefaultAspectRatio == 0.0 )
2329 {
2330 //size
2331 const double size = mSize;
2332 //assume 88 dpi as standard value
2333 const double widthScaleFactor = 3.465;
2334 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( mPath, size, mColor, mStrokeColor, mStrokeWidth, widthScaleFactor );
2335 // set default aspect ratio
2336 mDefaultAspectRatio = svgViewbox.isValid() ? svgViewbox.height() / svgViewbox.width() : 0.0;
2337 }
2338 return mDefaultAspectRatio;
2339}
2340
2342{
2343 const bool aPreservedAspectRatio = preservedAspectRatio();
2344 if ( aPreservedAspectRatio && !par )
2345 {
2347 }
2348 else if ( !aPreservedAspectRatio && par )
2349 {
2350 mFixedAspectRatio = 0.0;
2351 }
2352 return preservedAspectRatio();
2353}
2354
2355void QgsSvgMarkerSymbolLayer::setParameters( const QMap<QString, QgsProperty> &parameters )
2356{
2358}
2359
2360
2362{
2363 return u"SvgMarker"_s;
2364}
2365
2370
2372{
2373 QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
2374 Q_UNUSED( context )
2375}
2376
2378{
2379 Q_UNUSED( context )
2380}
2381
2383{
2384 QPainter *p = context.renderContext().painter();
2385 if ( !p )
2386 return;
2387
2388 bool hasDataDefinedSize = false;
2389 const double scaledWidth = calculateSize( context, hasDataDefinedSize );
2390 const double devicePixelRatio = context.renderContext().devicePixelRatio();
2391 const double width = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2392
2393 //don't render symbols with a width below one or above 10,000 pixels
2394 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2395 {
2396 return;
2397 }
2398
2399 const QgsScopedQPainterState painterState( p );
2400
2401 bool hasDataDefinedAspectRatio = false;
2402 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2403 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2404
2406
2407 double strokeWidth = mStrokeWidth;
2409 {
2412 }
2414
2415 QColor fillColor = mColor;
2416 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
2417 if ( useSelectedColor && mHasFillParam )
2418 {
2420 }
2422 {
2425 }
2426
2427 QColor strokeColor = mStrokeColor;
2429 {
2432 }
2433
2434 QString path = mPath;
2436 {
2439 context.renderContext().pathResolver() );
2440 if ( path != mPath && qgsDoubleNear( aspectRatio, 0.0 ) && !mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Height ) )
2441 {
2442 // adjust height of data defined path
2443 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2444 context.renderContext().scaleFactor(), aspectRatio,
2445 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2446 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2447 }
2448 }
2449
2450 QPointF outputOffset;
2451 double angle = 0.0;
2452 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2453
2454 p->translate( point + outputOffset );
2455
2456 const bool rotated = !qgsDoubleNear( angle, 0 );
2457 if ( rotated )
2458 p->rotate( angle );
2459
2460 bool fitsInCache = true;
2461 bool usePict = true;
2462 const bool rasterizeSelected = !mHasFillParam || mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Name );
2463 if ( ( !context.forceVectorRendering() && !rotated ) || ( useSelectedColor && rasterizeSelected ) )
2464 {
2465 QImage img = QgsApplication::svgCache()->svgAsImage( path, width * devicePixelRatio, fillColor, strokeColor, strokeWidth,
2466 context.renderContext().scaleFactor(), fitsInCache, aspectRatio,
2467 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2468 if ( fitsInCache && img.width() > 1 )
2469 {
2470 usePict = false;
2471
2472 if ( useSelectedColor )
2473 {
2475 }
2476
2477 //consider transparency
2478 if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2479 {
2480 QImage transparentImage = img.copy();
2481 QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2482 if ( devicePixelRatio == 1 )
2483 {
2484 p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
2485 }
2486 else
2487 {
2488 p->drawImage( QRectF( -transparentImage.width() / 2.0 / devicePixelRatio, -transparentImage.height() / 2.0 / devicePixelRatio,
2489 transparentImage.width() / devicePixelRatio, transparentImage.height() / devicePixelRatio
2490 ), transparentImage );
2491 }
2492 }
2493 else
2494 {
2495 if ( devicePixelRatio == 1 )
2496 {
2497 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2498 }
2499 else
2500 {
2501 p->drawImage( QRectF( -img.width() / 2.0 / devicePixelRatio, -img.height() / 2.0 / devicePixelRatio,
2502 img.width() / devicePixelRatio, img.height() / devicePixelRatio ), img );
2503 }
2504 }
2505 }
2506 }
2507
2508 if ( usePict || !fitsInCache )
2509 {
2510 p->setOpacity( context.opacity() );
2512 context.renderContext().scaleFactor(), context.forceVectorRendering(), aspectRatio,
2513 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2514 if ( pct.width() > 1 )
2515 {
2516 QgsPainting::drawPicture( p, QPointF( 0, 0 ), pct );
2517 }
2518 }
2519
2520 // workaround issue with nested QPictures forgetting antialiasing flag - see https://github.com/qgis/QGIS/issues/22909
2522}
2523
2524double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2525{
2526 double scaledSize = mSize;
2528
2529 bool ok = true;
2530 if ( hasDataDefinedSize )
2531 {
2534 }
2535 else
2536 {
2537 hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Width );
2538 if ( hasDataDefinedSize )
2539 {
2541 scaledSize = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::Width, context.renderContext().expressionContext(), mSize, &ok );
2542 }
2543 }
2544
2545 if ( hasDataDefinedSize && ok )
2546 {
2547 switch ( mScaleMethod )
2548 {
2550 scaledSize = std::sqrt( scaledSize );
2551 break;
2553 break;
2554 }
2555 }
2556
2557 return scaledSize;
2558}
2559
2560double QgsSvgMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2561{
2563 if ( !hasDataDefinedAspectRatio )
2564 return mFixedAspectRatio;
2565
2567 return 0.0;
2568
2569 double scaledAspectRatio = mDefaultAspectRatio;
2570 if ( mFixedAspectRatio > 0.0 )
2571 scaledAspectRatio = mFixedAspectRatio;
2572
2573 const double defaultHeight = mSize * scaledAspectRatio;
2574 scaledAspectRatio = defaultHeight / scaledSize;
2575
2576 bool ok = true;
2577 double scaledHeight = scaledSize * scaledAspectRatio;
2579 {
2580 context.setOriginalValueVariable( defaultHeight );
2581 scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::Height, context.renderContext().expressionContext(), defaultHeight, &ok );
2582 }
2583
2584 if ( hasDataDefinedAspectRatio && ok )
2585 {
2586 switch ( mScaleMethod )
2587 {
2589 scaledHeight = sqrt( scaledHeight );
2590 break;
2592 break;
2593 }
2594 }
2595
2596 scaledAspectRatio = scaledHeight / scaledSize;
2597
2598 return scaledAspectRatio;
2599}
2600
2601void QgsSvgMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
2602{
2603 //offset
2604 double offsetX = 0;
2605 double offsetY = 0;
2606 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
2607 offset = QPointF( offsetX, offsetY );
2608
2611 {
2614 }
2615
2616 const bool hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Angle );
2617 if ( hasDataDefinedRotation )
2618 {
2619 // For non-point markers, "dataDefinedRotation" means following the
2620 // shape (shape-data defined). For them, "field-data defined" does
2621 // not work at all. TODO: if "field-data defined" ever gets implemented
2622 // we'll need a way to distinguish here between the two, possibly
2623 // using another flag in renderHints()
2624 const QgsFeature *f = context.feature();
2625 if ( f )
2626 {
2627 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
2628 {
2629 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2630 angle += m2p.mapRotation();
2631 }
2632 }
2633 }
2634
2635 if ( angle )
2637}
2638
2639
2641{
2642 QVariantMap map;
2643 map[u"name"_s] = mPath;
2644 map[u"size"_s] = QString::number( mSize );
2645 map[u"size_unit"_s] = QgsUnitTypes::encodeUnit( mSizeUnit );
2646 map[u"size_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2647 map[u"fixedAspectRatio"_s] = QString::number( mFixedAspectRatio );
2648 map[u"angle"_s] = QString::number( mAngle );
2649 map[u"offset"_s] = QgsSymbolLayerUtils::encodePoint( mOffset );
2650 map[u"offset_unit"_s] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2651 map[u"offset_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2652 map[u"scale_method"_s] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2653 map[u"color"_s] = QgsColorUtils::colorToString( mColor );
2654 map[u"outline_color"_s] = QgsColorUtils::colorToString( mStrokeColor );
2655 map[u"outline_width"_s] = QString::number( mStrokeWidth );
2656 map[u"outline_width_unit"_s] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2657 map[u"outline_width_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2658 map[u"horizontal_anchor_point"_s] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
2659 map[u"vertical_anchor_point"_s] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
2660
2661 map[u"parameters"_s] = QgsProperty::propertyMapToVariantMap( mParameters );
2662
2663 return map;
2664}
2665
2672
2677
2678void QgsSvgMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
2679{
2680 QgsSldExportContext context;
2681 context.setExtraProperties( props );
2682 toSld( doc, element, context );
2683}
2684
2685bool QgsSvgMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
2686{
2687 return QgsMarkerSymbolLayer::toSld( doc, element, context );
2688}
2689
2695
2697{
2699 if ( unit != mStrokeWidthUnit )
2700 {
2702 }
2703 return unit;
2704}
2705
2711
2720
2721void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
2722{
2723 QgsSldExportContext context;
2724 context.setExtraProperties( props );
2725 writeSldMarker( doc, element, context );
2726}
2727
2728bool QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
2729{
2730 // <Graphic>
2731 QDomElement graphicElem = doc.createElement( u"se:Graphic"_s );
2732 element.appendChild( graphicElem );
2733
2734 const QVariantMap props = context.extraProperties();
2735 // encode a parametric SVG reference
2736 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
2739
2740 // <Rotation>
2741 QString angleFunc;
2742 bool ok;
2743 const double angle = props.value( u"angle"_s, u"0"_s ).toDouble( &ok );
2744 if ( !ok )
2745 {
2746 angleFunc = u"%1 + %2"_s.arg( props.value( u"angle"_s, u"0"_s ).toString() ).arg( mAngle );
2747 }
2748 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2749 {
2750 angleFunc = QString::number( angle + mAngle );
2751 }
2752
2753 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc, context );
2754
2755 // <Displacement>
2756 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2758 return true;
2759}
2760
2762{
2763 QgsDebugMsgLevel( u"Entered."_s, 4 );
2764
2765 QDomElement graphicElem = element.firstChildElement( u"Graphic"_s );
2766 if ( graphicElem.isNull() )
2767 return nullptr;
2768
2769 QString path, mimeType;
2770 // Unused and to be DEPRECATED in externalGraphicFromSld
2771 QColor fillColor_;
2772 double size;
2773
2774 if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor_, size ) )
2775 return nullptr;
2776
2777 double scaleFactor = 1.0;
2778 const QString uom = element.attribute( u"uom"_s );
2779 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
2780 size = size * scaleFactor;
2781
2782 if ( mimeType != "image/svg+xml"_L1 )
2783 return nullptr;
2784
2785 double angle = 0.0;
2786 QString angleFunc;
2787 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2788 {
2789 bool ok;
2790 const double d = angleFunc.toDouble( &ok );
2791 if ( ok )
2792 angle = d;
2793 }
2794
2795 QPointF offset;
2797
2798 // Extract parameters from URL
2799 QString realPath { path };
2800 QUrl svgUrl { path };
2801
2802 // Because color definition can start with '#', the url parsing won't recognize the query string entirely
2803 QUrlQuery queryString;
2804
2805 if ( svgUrl.hasQuery() && svgUrl.hasFragment() )
2806 {
2807 const QString queryPart { path.mid( path.indexOf( '?' ) + 1 ) };
2808 queryString.setQuery( queryPart );
2809 }
2810
2811 // Remove query for simple file paths
2812 if ( svgUrl.scheme().isEmpty() || svgUrl.isLocalFile() )
2813 {
2814 svgUrl.setQuery( QString() );
2815 realPath = svgUrl.path();
2816 }
2817
2819
2820 QMap<QString, QgsProperty> params;
2821
2822 bool ok;
2823
2824 if ( queryString.hasQueryItem( u"fill"_s ) )
2825 {
2826 const QColor fillColor { queryString.queryItemValue( u"fill"_s ) };
2827 m->setFillColor( fillColor );
2828 }
2829
2830 if ( queryString.hasQueryItem( u"fill-opacity"_s ) )
2831 {
2832 const double alpha { queryString.queryItemValue( u"fill-opacity"_s ).toDouble( &ok ) };
2833 if ( ok )
2834 {
2835 params.insert( u"fill-opacity"_s, QgsProperty::fromValue( alpha ) );
2836 }
2837 }
2838
2839 if ( queryString.hasQueryItem( u"outline"_s ) )
2840 {
2841 const QColor strokeColor { queryString.queryItemValue( u"outline"_s ) };
2843 }
2844
2845 if ( queryString.hasQueryItem( u"outline-opacity"_s ) )
2846 {
2847 const double alpha { queryString.queryItemValue( u"outline-opacity"_s ).toDouble( &ok ) };
2848 if ( ok )
2849 {
2850 params.insert( u"outline-opacity"_s, QgsProperty::fromValue( alpha ) );
2851 }
2852 }
2853
2854 if ( queryString.hasQueryItem( u"outline-width"_s ) )
2855 {
2856 const int width { queryString.queryItemValue( u"outline-width"_s ).toInt( &ok )};
2857 if ( ok )
2858 {
2859 m->setStrokeWidth( width );
2860 }
2861 }
2862
2863 if ( ! params.isEmpty() )
2864 {
2865 m->setParameters( params );
2866 }
2867
2868 m->setOutputUnit( sldUnitSize );
2869 m->setAngle( angle );
2870 m->setOffset( offset );
2871 return m;
2872}
2873
2874bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
2875{
2876 //size
2877 double size = mSize;
2878
2879 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
2880
2881 bool ok = true;
2882 if ( hasDataDefinedSize )
2883 {
2886 }
2887
2888 if ( hasDataDefinedSize && ok )
2889 {
2890 switch ( mScaleMethod )
2891 {
2893 size = std::sqrt( size );
2894 break;
2896 break;
2897 }
2898 }
2899
2901 {
2902 size *= mmMapUnitScaleFactor;
2903 }
2904
2905//offset, angle
2906 QPointF offset = mOffset;
2907
2909 {
2911 const QVariant val = mDataDefinedProperties.value( QgsSymbolLayer::Property::Offset, context.renderContext().expressionContext(), QString() );
2912 const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
2913 if ( ok )
2914 offset = res;
2915 }
2916 const double offsetX = offset.x();
2917 const double offsetY = offset.y();
2918
2919 QPointF outputOffset( offsetX, offsetY );
2920
2921 double angle = mAngle + mLineAngle;
2923 {
2926 }
2927
2928 if ( angle )
2929 outputOffset = _rotatedOffset( outputOffset, angle );
2930
2932
2933 QString path = mPath;
2935 {
2938 context.renderContext().pathResolver() );
2939 }
2940
2941 double strokeWidth = mStrokeWidth;
2943 {
2946 }
2948
2949 QColor fillColor = mColor;
2951 {
2954 }
2955
2956 QColor strokeColor = mStrokeColor;
2958 {
2961 }
2962
2964
2965 const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, size, fillColor, strokeColor, strokeWidth,
2967 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2968
2969 QSvgRenderer r( svgContent );
2970 if ( !r.isValid() )
2971 return false;
2972
2973 QgsDxfPaintDevice pd( &e );
2974 pd.setDrawingSize( QSizeF( r.defaultSize() ) );
2975
2976 QSizeF outSize( r.defaultSize() );
2977 outSize.scale( size, size, Qt::KeepAspectRatio );
2978
2979 QPainter p;
2980 p.begin( &pd );
2981 if ( !qgsDoubleNear( angle, 0.0 ) )
2982 {
2983 p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
2984 p.rotate( angle );
2985 p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
2986 }
2987 pd.setShift( shift + QPointF( outputOffset.x(), -outputOffset.y() ) );
2988 pd.setOutputSize( QRectF( -outSize.width() / 2.0, -outSize.height() / 2.0, outSize.width(), outSize.height() ) );
2989 pd.setLayer( layerName );
2990 r.render( &p );
2991 p.end();
2992 return true;
2993}
2994
2996{
2997 bool hasDataDefinedSize = false;
2998 double scaledWidth = calculateSize( context, hasDataDefinedSize );
2999
3000 bool hasDataDefinedAspectRatio = false;
3001 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
3002 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
3003
3004 scaledWidth = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
3005 scaledHeight = context.renderContext().convertToPainterUnits( scaledHeight, mSizeUnit, mSizeMapUnitScale );
3006
3007 //don't render symbols with size below one or above 10,000 pixels
3008 if ( static_cast< int >( scaledWidth ) < 1 || 10000.0 < scaledWidth )
3009 {
3010 return QRectF();
3011 }
3012
3013 QPointF outputOffset;
3014 double angle = 0.0;
3015 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
3016
3017 double strokeWidth = mStrokeWidth;
3019 {
3022 }
3024
3025 QString path = mPath;
3027 {
3030 context.renderContext().pathResolver() );
3031 if ( path != mPath && qgsDoubleNear( aspectRatio, 0.0 ) && !mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Height ) )
3032 {
3033 // need to get colors to take advantage of cached SVGs
3034 QColor fillColor = mColor;
3036 {
3039 }
3040
3041 const QColor strokeColor = mStrokeColor;
3043 {
3046 }
3047
3049
3050 // adjust height of data defined path
3051 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
3052 context.renderContext().scaleFactor(), aspectRatio,
3053 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
3054 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
3055 }
3056 }
3057
3058 QTransform transform;
3059 // move to the desired position
3060 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3061
3062 if ( !qgsDoubleNear( angle, 0.0 ) )
3063 transform.rotate( angle );
3064
3065 //antialiasing
3066 strokeWidth += 1.0 / 2.0;
3067
3068 QRectF symbolBounds = transform.mapRect( QRectF( -scaledWidth / 2.0,
3069 -scaledHeight / 2.0,
3070 scaledWidth,
3071 scaledHeight ) );
3072
3073 //extend bounds by pen width / 2.0
3074 symbolBounds.adjust( -strokeWidth / 2.0, -strokeWidth / 2.0,
3075 strokeWidth / 2.0, strokeWidth / 2.0 );
3076
3077 return symbolBounds;
3078}
3079
3081
3083 : mPath( path )
3084{
3085 mSize = size;
3086 mAngle = angle;
3087 mOffset = QPointF( 0, 0 );
3090}
3091
3093
3095{
3096 QString path;
3100
3101 if ( props.contains( u"imageFile"_s ) )
3102 path = props[u"imageFile"_s].toString();
3103 if ( props.contains( u"size"_s ) )
3104 size = props[u"size"_s].toDouble();
3105 if ( props.contains( u"angle"_s ) )
3106 angle = props[u"angle"_s].toDouble();
3107 if ( props.contains( u"scale_method"_s ) )
3108 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[u"scale_method"_s].toString() );
3109
3110 auto m = std::make_unique< QgsRasterMarkerSymbolLayer >( path, size, angle, scaleMethod );
3111 m->setCommonProperties( props );
3112 return m.release();
3113}
3114
3116{
3117 const QDomElement graphicElem = element.firstChildElement( u"Graphic"_s );
3118 if ( graphicElem.isNull() )
3119 return nullptr;
3120
3121 const QDomElement externalGraphicElem = graphicElem.firstChildElement( u"ExternalGraphic"_s );
3122 if ( externalGraphicElem.isNull() )
3123 return nullptr;
3124
3125 const QDomElement onlineResourceElem = externalGraphicElem.firstChildElement( u"OnlineResource"_s );
3126 const QDomElement inlineContentElem = externalGraphicElem.firstChildElement( u"InlineContent"_s );
3127
3128 QString url;
3129 if ( !onlineResourceElem.isNull() )
3130 {
3131 url = onlineResourceElem.attribute( u"href"_s );
3132 // no further processing to do, both base64 data urls and direct file/http urls are compatible with raster markers already
3133 }
3134 else if ( !inlineContentElem.isNull() && inlineContentElem.attribute( u"encoding"_s ) == "base64"_L1 )
3135 {
3136 url = u"base64:"_s + inlineContentElem.text();
3137 }
3138 else
3139 {
3140 return nullptr;
3141 }
3142
3144 // TODO: parse other attributes from the SLD spec (Opacity, Size, Rotation, AnchorPoint, Displacement)
3145 return m;
3146}
3147
3149{
3150 if ( properties.contains( u"alpha"_s ) )
3151 {
3152 setOpacity( properties[u"alpha"_s].toDouble() );
3153 }
3154
3155 if ( properties.contains( u"size_unit"_s ) )
3156 setSizeUnit( QgsUnitTypes::decodeRenderUnit( properties[u"size_unit"_s].toString() ) );
3157 if ( properties.contains( u"size_map_unit_scale"_s ) )
3158 setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[u"size_map_unit_scale"_s].toString() ) );
3159 if ( properties.contains( u"fixedAspectRatio"_s ) )
3160 setFixedAspectRatio( properties[u"fixedAspectRatio"_s].toDouble() );
3161
3162 if ( properties.contains( u"offset"_s ) )
3163 setOffset( QgsSymbolLayerUtils::decodePoint( properties[u"offset"_s].toString() ) );
3164 if ( properties.contains( u"offset_unit"_s ) )
3165 setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[u"offset_unit"_s].toString() ) );
3166 if ( properties.contains( u"offset_map_unit_scale"_s ) )
3167 setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[u"offset_map_unit_scale"_s].toString() ) );
3168
3169 if ( properties.contains( u"horizontal_anchor_point"_s ) )
3170 {
3171 setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( properties[ u"horizontal_anchor_point"_s].toInt() ) );
3172 }
3173 if ( properties.contains( u"vertical_anchor_point"_s ) )
3174 {
3175 setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( properties[ u"vertical_anchor_point"_s].toInt() ) );
3176 }
3177
3180}
3181
3182void QgsRasterMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
3183{
3184 const QVariantMap::iterator it = properties.find( u"name"_s );
3185 if ( it != properties.end() && it.value().userType() == QMetaType::Type::QString )
3186 {
3187 if ( saving )
3188 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
3189 else
3190 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
3191 }
3192}
3193
3195{
3196 mPath = path;
3198}
3199
3201{
3202 const bool aPreservedAspectRatio = preservedAspectRatio();
3203 if ( aPreservedAspectRatio && !par )
3204 {
3206 }
3207 else if ( !aPreservedAspectRatio && par )
3208 {
3209 mFixedAspectRatio = 0.0;
3210 }
3211 return preservedAspectRatio();
3212}
3213
3215{
3216 if ( mDefaultAspectRatio == 0.0 && !mPath.isEmpty() )
3217 {
3219 mDefaultAspectRatio = ( !size.isNull() && size.isValid() && size.width() > 0 ) ? static_cast< double >( size.height() ) / static_cast< double >( size.width() ) : 0.0;
3220 }
3221 return mDefaultAspectRatio;
3222}
3223
3225{
3226 return u"RasterMarker"_s;
3227}
3228
3233
3235{
3236 QPainter *p = context.renderContext().painter();
3237 if ( !p )
3238 return;
3239
3240 QString path = mPath;
3242 {
3245 }
3246
3247 if ( path.isEmpty() )
3248 return;
3249
3250 double width = 0.0;
3251 double height = 0.0;
3252
3253 bool hasDataDefinedSize = false;
3254 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3255
3256 bool hasDataDefinedAspectRatio = false;
3257 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3258
3259 QPointF outputOffset;
3260 double angle = 0.0;
3261
3262 // RenderPercentage Unit Type takes original image size
3264 {
3266 if ( size.isEmpty() )
3267 return;
3268
3269 width = ( scaledSize * static_cast< double >( size.width() ) ) / 100.0;
3270 height = ( scaledSize * static_cast< double >( size.height() ) ) / 100.0;
3271
3272 // don't render symbols with size below one or above 10,000 pixels
3273 if ( static_cast< int >( width ) < 1 || 10000.0 < width || static_cast< int >( height ) < 1 || 10000.0 < height )
3274 return;
3275
3276 calculateOffsetAndRotation( context, width, height, outputOffset, angle );
3277 }
3278 else
3279 {
3280 width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3281 height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3282
3283 if ( preservedAspectRatio() && path != mPath )
3284 {
3286 if ( !size.isNull() && size.isValid() && size.width() > 0 )
3287 {
3288 height = width * ( static_cast< double >( size.height() ) / static_cast< double >( size.width() ) );
3289 }
3290 }
3291
3292 // don't render symbols with size below one or above 10,000 pixels
3293 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
3294 return;
3295
3296 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3297 }
3298
3299 const QgsScopedQPainterState painterState( p );
3300 p->translate( point + outputOffset );
3301
3302 const bool rotated = !qgsDoubleNear( angle, 0 );
3303 if ( rotated )
3304 p->rotate( angle );
3305
3306 double opacity = mOpacity;
3308 {
3311 }
3312 opacity *= context.opacity();
3313
3314 QImage img = fetchImage( context.renderContext(), path, QSize( width, preservedAspectRatio() ? 0 : width * aspectRatio ), preservedAspectRatio(), opacity );
3315 if ( !img.isNull() )
3316 {
3317 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
3318 if ( useSelectedColor )
3319 {
3321 }
3322
3323 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
3324 }
3325}
3326
3327QImage QgsRasterMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
3328{
3329 bool cached = false;
3330 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking );
3331}
3332
3333double QgsRasterMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
3334{
3335 double scaledSize = mSize;
3337
3338 bool ok = true;
3339 if ( hasDataDefinedSize )
3340 {
3343 }
3344 else
3345 {
3346 hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Width );
3347 if ( hasDataDefinedSize )
3348 {
3350 scaledSize = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::Width, context.renderContext().expressionContext(), mSize, &ok );
3351 }
3352 }
3353
3354 if ( hasDataDefinedSize && ok )
3355 {
3356 switch ( mScaleMethod )
3357 {
3359 scaledSize = std::sqrt( scaledSize );
3360 break;
3362 break;
3363 }
3364 }
3365
3366 return scaledSize;
3367}
3368
3369double QgsRasterMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
3370{
3372 if ( !hasDataDefinedAspectRatio )
3373 return mFixedAspectRatio;
3374
3376 return 0.0;
3377
3378 double scaledAspectRatio = mDefaultAspectRatio;
3379 if ( mFixedAspectRatio > 0.0 )
3380 scaledAspectRatio = mFixedAspectRatio;
3381
3382 const double defaultHeight = mSize * scaledAspectRatio;
3383 scaledAspectRatio = defaultHeight / scaledSize;
3384
3385 bool ok = true;
3386 double scaledHeight = scaledSize * scaledAspectRatio;
3388 {
3389 context.setOriginalValueVariable( defaultHeight );
3390 scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::Height, context.renderContext().expressionContext(), defaultHeight, &ok );
3391 }
3392
3393 if ( hasDataDefinedAspectRatio && ok )
3394 {
3395 switch ( mScaleMethod )
3396 {
3398 scaledHeight = sqrt( scaledHeight );
3399 break;
3401 break;
3402 }
3403 }
3404
3405 scaledAspectRatio = scaledHeight / scaledSize;
3406
3407 return scaledAspectRatio;
3408}
3409
3410void QgsRasterMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
3411{
3412 //offset
3413 double offsetX = 0;
3414 double offsetY = 0;
3415 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
3416 offset = QPointF( offsetX, offsetY );
3417
3420 {
3423 }
3424
3425 const bool hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Angle );
3426 if ( hasDataDefinedRotation )
3427 {
3428 const QgsFeature *f = context.feature();
3429 if ( f )
3430 {
3431 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3432 {
3433 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3434 angle += m2p.mapRotation();
3435 }
3436 }
3437 }
3438
3439 if ( angle )
3441}
3442
3443
3445{
3446 QVariantMap map;
3447 map[u"imageFile"_s] = mPath;
3448 map[u"size"_s] = QString::number( mSize );
3449 map[u"size_unit"_s] = QgsUnitTypes::encodeUnit( mSizeUnit );
3450 map[u"size_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3451 map[u"fixedAspectRatio"_s] = QString::number( mFixedAspectRatio );
3452 map[u"angle"_s] = QString::number( mAngle );
3453 map[u"alpha"_s] = QString::number( mOpacity );
3454 map[u"offset"_s] = QgsSymbolLayerUtils::encodePoint( mOffset );
3455 map[u"offset_unit"_s] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3456 map[u"offset_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3457 map[u"scale_method"_s] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
3458 map[u"horizontal_anchor_point"_s] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
3459 map[u"vertical_anchor_point"_s] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
3460 return map;
3461}
3462
3464{
3465 auto m = std::make_unique< QgsRasterMarkerSymbolLayer >();
3466 m->mPath = mPath;
3467 m->mDefaultAspectRatio = mDefaultAspectRatio;
3468 m->mSize = mSize;
3469 m->mAngle = mAngle;
3470 // other members are copied by:
3471 copyCommonProperties( m.get() );
3472 return m.release();
3473}
3474
3475
3490
3496
3498{
3499 return QColor();
3500}
3501
3506
3511
3513{
3514 bool hasDataDefinedSize = false;
3515 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3516 const double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3517 bool hasDataDefinedAspectRatio = false;
3518 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3519 const double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3520
3521 //don't render symbols with size below one or above 10,000 pixels
3522 if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
3523 {
3524 return QRectF();
3525 }
3526
3527 QPointF outputOffset;
3528 double angle = 0.0;
3529 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3530
3531 QTransform transform;
3532
3533 // move to the desired position
3534 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3535
3536 if ( !qgsDoubleNear( angle, 0.0 ) )
3537 transform.rotate( angle );
3538
3539 QRectF symbolBounds = transform.mapRect( QRectF( -width / 2.0,
3540 -height / 2.0,
3541 width,
3542 height ) );
3543
3544 return symbolBounds;
3545}
3546
3547void QgsRasterMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
3548{
3549 QgsSldExportContext context;
3550 context.setExtraProperties( props );
3551 writeSldMarker( doc, element, context );
3552}
3553
3554bool QgsRasterMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
3555{
3556 Q_UNUSED( context )
3557
3558 // <Graphic>
3559 QDomElement graphicElem = doc.createElement( u"se:Graphic"_s );
3560 element.appendChild( graphicElem );
3561
3562 // <ExternalGraphic>
3563 QDomElement extGraphElem = doc.createElement( u"se:ExternalGraphic"_s );
3564 graphicElem.appendChild( extGraphElem );
3565
3566 QMimeDatabase mimeDB;
3567 QMimeType mimeType;
3568
3569 QString base64data;
3570 if ( mPath.startsWith( "base64:"_L1 ) )
3571 {
3572 base64data = mPath.mid( 7 );
3573 }
3574 else
3575 {
3576 QString mime;
3577 QString data;
3579 {
3580 base64data = data;
3581 }
3582 }
3583
3584 if ( !base64data.isEmpty() )
3585 {
3586 // <InlineContent>
3587 QDomElement inlineContEleme = doc.createElement( u"se:InlineContent"_s );
3588
3589 inlineContEleme.setAttribute( u"encoding"_s, u"base64"_s );
3590 inlineContEleme.appendChild( doc.createTextNode( base64data ) );
3591 extGraphElem.appendChild( inlineContEleme );
3592
3593 // determine mime type
3594 const QByteArray ba = QByteArray::fromBase64( base64data.toUtf8() );
3595 mimeType = mimeDB.mimeTypeForData( ba );
3596 }
3597 else
3598 {
3599 // <ExternalGraphic>
3600 QDomElement onlineResElem = doc.createElement( u"se:OnlineResource"_s );
3601 QString url = mPath;
3602
3603 onlineResElem.setAttribute( u"xlink:href"_s, url );
3604 onlineResElem.setAttribute( u"xlink:type"_s, u"simple"_s );
3605 extGraphElem.appendChild( onlineResElem );
3606
3607 // determine mime type
3608 if ( mPath.startsWith( "http://"_L1 ) || mPath.startsWith( "https://"_L1 ) )
3609 {
3610 // Qt can't guess mime type for remote URLs, and it seems geoserver can handle wrong image mime types
3611 // but not generic ones, so let's hardcode to png.
3612 mimeType = mimeDB.mimeTypeForName( "image/png" );
3613 }
3614 else
3615 {
3616 mimeType = mimeDB.mimeTypeForUrl( url );
3617 }
3618 }
3619
3620 QDomElement formatElem = doc.createElement( u"se:Format"_s );
3621 formatElem.appendChild( doc.createTextNode( mimeType.name() ) );
3622 extGraphElem.appendChild( formatElem );
3623
3624 // TODO: write other attributes from the SLD spec (Opacity, Size, Rotation, AnchorPoint, Displacement)
3625 return true;
3626}
3627
3629
3630QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QString chr, double pointSize, const QColor &color, double angle )
3631{
3632 mFontFamily = fontFamily;
3633 mString = chr;
3634 mColor = color;
3635 mAngle = angle;
3636 mSize = pointSize;
3637 mOrigSize = pointSize;
3639 mOffset = QPointF( 0, 0 );
3641 mStrokeColor = DEFAULT_FONTMARKER_BORDERCOLOR;
3642 mStrokeWidth = 0.0;
3643 mStrokeWidthUnit = Qgis::RenderUnit::Millimeters;
3644 mPenJoinStyle = DEFAULT_FONTMARKER_JOINSTYLE;
3645}
3646
3648
3650{
3652 QString string = DEFAULT_FONTMARKER_CHR;
3653 double pointSize = DEFAULT_FONTMARKER_SIZE;
3656
3657 if ( props.contains( u"font"_s ) )
3658 fontFamily = props[u"font"_s].toString();
3659 if ( props.contains( u"chr"_s ) && props[u"chr"_s].toString().length() > 0 )
3660 {
3661 string = props["chr"].toString();
3662 const thread_local QRegularExpression charRegExp( u"%1([0-9]+)%1"_s.arg( FONTMARKER_CHR_FIX ) );
3663 QRegularExpressionMatch match = charRegExp.match( string );
3664 while ( match.hasMatch() )
3665 {
3666 QChar replacement = QChar( match.captured( 1 ).toUShort() );
3667 string = string.mid( 0, match.capturedStart( 0 ) ) + replacement + string.mid( match.capturedEnd( 0 ) );
3668 match = charRegExp.match( string );
3669 }
3670 }
3671
3672 if ( props.contains( u"size"_s ) )
3673 pointSize = props[u"size"_s].toDouble();
3674 if ( props.contains( u"color"_s ) )
3675 color = QgsColorUtils::colorFromString( props[u"color"_s].toString() );
3676 if ( props.contains( u"angle"_s ) )
3677 angle = props[u"angle"_s].toDouble();
3678
3680
3681 if ( props.contains( u"font_style"_s ) )
3682 m->setFontStyle( props[u"font_style"_s].toString() );
3683 if ( props.contains( u"outline_color"_s ) )
3684 m->setStrokeColor( QgsColorUtils::colorFromString( props[u"outline_color"_s].toString() ) );
3685 if ( props.contains( u"outline_width"_s ) )
3686 m->setStrokeWidth( props[u"outline_width"_s].toDouble() );
3687 if ( props.contains( u"offset"_s ) )
3688 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[u"offset"_s].toString() ) );
3689 if ( props.contains( u"offset_unit"_s ) )
3690 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[u"offset_unit"_s].toString() ) );
3691 if ( props.contains( u"offset_map_unit_scale"_s ) )
3692 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"offset_map_unit_scale"_s].toString() ) );
3693 if ( props.contains( u"size_unit"_s ) )
3694 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[u"size_unit"_s].toString() ) );
3695 if ( props.contains( u"size_map_unit_scale"_s ) )
3696 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"size_map_unit_scale"_s].toString() ) );
3697 if ( props.contains( u"outline_width_unit"_s ) )
3698 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[u"outline_width_unit"_s].toString() ) );
3699 if ( props.contains( u"outline_width_map_unit_scale"_s ) )
3700 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"outline_width_map_unit_scale"_s].toString() ) );
3701 if ( props.contains( u"joinstyle"_s ) )
3702 m->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[u"joinstyle"_s].toString() ) );
3703 if ( props.contains( u"horizontal_anchor_point"_s ) )
3704 m->setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( props[ u"horizontal_anchor_point"_s].toInt() ) );
3705 if ( props.contains( u"vertical_anchor_point"_s ) )
3706 m->setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( props[ u"vertical_anchor_point"_s].toInt() ) );
3707
3709
3710 return m;
3711}
3712
3714{
3715 return u"FontMarker"_s;
3716}
3717
3722
3724{
3725 QColor brushColor = mColor;
3726 QColor penColor = mStrokeColor;
3727
3728 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
3729 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
3730
3731 mBrush = QBrush( brushColor );
3732 mPen = QPen( penColor );
3733 mPen.setJoinStyle( mPenJoinStyle );
3734 mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
3735
3736 mFont = QgsFontUtils::createFont( QgsApplication::fontManager()->processFontFamilyName( mFontFamily ) );
3737 if ( !mFontStyle.isEmpty() )
3738 {
3739 mFont.setStyleName( QgsFontUtils::translateNamedStyle( mFontStyle ) );
3740 }
3741
3742 double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3743 mNonZeroFontSize = !qgsDoubleNear( sizePixels, 0.0 );
3744
3745 if ( mNonZeroFontSize && sizePixels > MAX_FONT_CHARACTER_SIZE_IN_PIXELS )
3746 {
3747 // if font is too large (e.g using map units and map is very zoomed in), then we limit
3748 // the font size and instead scale up the painter.
3749 // this avoids issues with massive font sizes (eg https://github.com/qgis/QGIS/issues/42270)
3750 mFontSizeScale = sizePixels / MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3751 sizePixels = MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3752 }
3753 else
3754 mFontSizeScale = 1.0;
3755
3756 // if a non zero, but small pixel size results, round up to 2 pixels so that a "dot" is at least visible
3757 // (if we set a <=1 pixel size here Qt will reset the font to a default size, leading to much too large symbols)
3758 mFont.setPixelSize( std::max( 2, static_cast< int >( std::round( sizePixels ) ) ) );
3759 mFontMetrics = std::make_unique<QFontMetrics>( mFont );
3760 mChrWidth = mFontMetrics->horizontalAdvance( mString );
3761 switch ( mVerticalAnchorPoint )
3762 {
3764 {
3765 mChrOffset = QPointF( mChrWidth / 2.0, -sizePixels / 2.0 );
3766 break;
3767 }
3771 {
3772 mChrOffset = QPointF( mChrWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3773 break;
3774 }
3775 }
3776 mOrigSize = mSize; // save in case the size would be data defined
3777
3778 // use caching only when not using a data defined character
3779 mUseCachedPath = !mDataDefinedProperties.isActive( QgsSymbolLayer::Property::FontFamily ) &&
3782 if ( mUseCachedPath )
3783 {
3784 QPointF chrOffset = mChrOffset;
3785 double chrWidth;
3786 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3787 mCachedPath = QPainterPath();
3788 mCachedPath.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3789 }
3790}
3791
3793{
3794 Q_UNUSED( context )
3795}
3796
3797QString QgsFontMarkerSymbolLayer::characterToRender( QgsSymbolRenderContext &context, QPointF &charOffset, double &charWidth )
3798{
3799 charOffset = mChrOffset;
3800 QString stringToRender = mString;
3802 {
3803 context.setOriginalValueVariable( mString );
3805 if ( stringToRender != mString )
3806 {
3807 charWidth = mFontMetrics->horizontalAdvance( stringToRender );
3808 switch ( mVerticalAnchorPoint )
3809 {
3811 {
3812 const double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3813 charOffset = QPointF( charWidth / 2.0, -sizePixels / 2.0 );
3814 break;
3815 }
3819 {
3820 charOffset = QPointF( charWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3821 break;
3822 }
3823 }
3824 }
3825 }
3826 return stringToRender;
3827}
3828
3829void QgsFontMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
3830 double scaledSize,
3831 bool &hasDataDefinedRotation,
3832 QPointF &offset,
3833 double &angle ) const
3834{
3835 //offset
3836 double offsetX = 0;
3837 double offsetY = 0;
3838 markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
3839 offset = QPointF( offsetX, offsetY );
3840 hasDataDefinedRotation = false;
3841
3842 //angle
3843 bool ok = true;
3846 {
3849
3850 // If the expression evaluation was not successful, fallback to static value
3851 if ( !ok )
3853
3854 hasDataDefinedRotation = true;
3855 }
3856
3857 hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || hasDataDefinedRotation;
3858 if ( hasDataDefinedRotation )
3859 {
3860 // For non-point markers, "dataDefinedRotation" means following the
3861 // shape (shape-data defined). For them, "field-data defined" does
3862 // not work at all. TODO: if "field-data defined" ever gets implemented
3863 // we'll need a way to distinguish here between the two, possibly
3864 // using another flag in renderHints()
3865 const QgsFeature *f = context.feature();
3866 if ( f )
3867 {
3868 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3869 {
3870 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3871 angle += m2p.mapRotation();
3872 }
3873 }
3874 }
3875
3876 if ( angle )
3878}
3879
3880double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context )
3881{
3882 double scaledSize = mSize;
3883 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
3884
3885 bool ok = true;
3886 if ( hasDataDefinedSize )
3887 {
3889 scaledSize = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::Size, context.renderContext().expressionContext(), mSize, &ok );
3890 }
3891
3892 if ( hasDataDefinedSize && ok )
3893 {
3894 switch ( mScaleMethod )
3895 {
3897 scaledSize = std::sqrt( scaledSize );
3898 break;
3900 break;
3901 }
3902 }
3903 return scaledSize;
3904}
3905
3907{
3908 QPainter *p = context.renderContext().painter();
3909 if ( !p || !mNonZeroFontSize )
3910 return;
3911
3912 QTransform transform;
3913
3914 bool ok;
3915 QColor brushColor = mColor;
3917 {
3919 brushColor = mDataDefinedProperties.valueAsColor( QgsSymbolLayer::Property::FillColor, context.renderContext().expressionContext(), brushColor );
3920 }
3921 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
3922 brushColor = useSelectedColor ? context.renderContext().selectionColor() : brushColor;
3923 if ( !useSelectedColor || !SELECTION_IS_OPAQUE )
3924 {
3925 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
3926 }
3927 mBrush.setColor( brushColor );
3928
3929 QColor penColor = mStrokeColor;
3931 {
3933 penColor = mDataDefinedProperties.valueAsColor( QgsSymbolLayer::Property::StrokeColor, context.renderContext().expressionContext(), penColor );
3934 }
3935 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
3936
3937 double penWidth = context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3939 {
3940 context.setOriginalValueVariable( mStrokeWidth );
3941 const double strokeWidth = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::StrokeWidth, context.renderContext().expressionContext(), mStrokeWidth, &ok );
3942 if ( ok )
3943 {
3944 penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3945 }
3946 }
3947
3949 {
3951 const QString style = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::JoinStyle, context.renderContext().expressionContext(), QString(), &ok );
3952 if ( ok )
3953 {
3954 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
3955 }
3956 }
3957
3958 const QgsScopedQPainterState painterState( p );
3959 p->setBrush( mBrush );
3960 if ( !qgsDoubleNear( penWidth, 0.0 ) )
3961 {
3962 mPen.setColor( penColor );
3963 mPen.setWidthF( penWidth );
3964 p->setPen( mPen );
3965 }
3966 else
3967 {
3968 p->setPen( Qt::NoPen );
3969 }
3970
3972 {
3973 context.setOriginalValueVariable( mFontFamily );
3974 const QString fontFamily = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::FontFamily, context.renderContext().expressionContext(), mFontFamily, &ok );
3975 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( ok ? fontFamily : mFontFamily );
3976 QgsFontUtils::setFontFamily( mFont, processedFamily );
3977 }
3979 {
3980 context.setOriginalValueVariable( mFontStyle );
3981 const QString fontStyle = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::FontStyle, context.renderContext().expressionContext(), mFontStyle, &ok );
3983 }
3985 {
3986 mFontMetrics = std::make_unique<QFontMetrics>( mFont );
3987 }
3988
3989 QPointF chrOffset = mChrOffset;
3990 double chrWidth;
3991 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3992
3993 const double sizeToRender = calculateSize( context );
3994
3995 bool hasDataDefinedRotation = false;
3996 QPointF offset;
3997 double angle = 0;
3998 calculateOffsetAndRotation( context, sizeToRender, hasDataDefinedRotation, offset, angle );
3999
4000 p->translate( point.x() + offset.x(), point.y() + offset.y() );
4001
4002 if ( !qgsDoubleNear( angle, 0.0 ) )
4003 transform.rotate( angle );
4004
4005 if ( !qgsDoubleNear( sizeToRender, mOrigSize ) )
4006 {
4007 const double s = sizeToRender / mOrigSize;
4008 transform.scale( s, s );
4009 }
4010
4011 if ( !qgsDoubleNear( mFontSizeScale, 1.0 ) )
4012 transform.scale( mFontSizeScale, mFontSizeScale );
4013
4014 if ( mUseCachedPath )
4015 {
4016 p->drawPath( transform.map( mCachedPath ) );
4017 }
4018 else
4019 {
4020 QPainterPath path;
4021 path.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
4022 p->drawPath( transform.map( path ) );
4023 }
4024}
4025
4027{
4028 QVariantMap props;
4029 props[u"font"_s] = mFontFamily;
4030 props[u"font_style"_s] = mFontStyle;
4031 QString chr = mString;
4032 for ( int i = 0; i < 32; i++ )
4033 {
4034 if ( i == 9 || i == 10 || i == 13 )
4035 {
4036 continue;
4037 }
4038 chr.replace( QChar( i ), u"%1%2%1"_s.arg( FONTMARKER_CHR_FIX, QString::number( i ) ) );
4039 }
4040 props[u"chr"_s] = chr;
4041 props[u"size"_s] = QString::number( mSize );
4042 props[u"size_unit"_s] = QgsUnitTypes::encodeUnit( mSizeUnit );
4043 props[u"size_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
4044 props[u"color"_s] = QgsColorUtils::colorToString( mColor );
4045 props[u"outline_color"_s] = QgsColorUtils::colorToString( mStrokeColor );
4046 props[u"outline_width"_s] = QString::number( mStrokeWidth );
4047 props[u"outline_width_unit"_s] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
4048 props[u"outline_width_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
4049 props[u"joinstyle"_s] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
4050 props[u"angle"_s] = QString::number( mAngle );
4051 props[u"offset"_s] = QgsSymbolLayerUtils::encodePoint( mOffset );
4052 props[u"offset_unit"_s] = QgsUnitTypes::encodeUnit( mOffsetUnit );
4053 props[u"offset_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
4054 props[u"horizontal_anchor_point"_s] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
4055 props[u"vertical_anchor_point"_s] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
4056 return props;
4057}
4058
4060{
4061 QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( mFontFamily, mString, mSize, mColor, mAngle );
4062 m->setFontStyle( mFontStyle );
4063 m->setStrokeColor( mStrokeColor );
4064 m->setStrokeWidth( mStrokeWidth );
4065 m->setStrokeWidthUnit( mStrokeWidthUnit );
4066 m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
4067 m->setPenJoinStyle( mPenJoinStyle );
4068 m->setOffset( mOffset );
4071 m->setSizeUnit( mSizeUnit );
4076 copyPaintEffect( m );
4077 return m;
4078}
4079
4080void QgsFontMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
4081{
4082 QgsSldExportContext context;
4083 context.setExtraProperties( props );
4084 toSld( doc, element, context );
4085}
4086
4087bool QgsFontMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
4088{
4089 return QgsMarkerSymbolLayer::toSld( doc, element, context );
4090}
4091
4092void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
4093{
4094 QgsSldExportContext context;
4095 context.setExtraProperties( props );
4096 writeSldMarker( doc, element, context );
4097}
4098
4099bool QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
4100{
4101 // <Graphic>
4102 QDomElement graphicElem = doc.createElement( u"se:Graphic"_s );
4103 element.appendChild( graphicElem );
4104
4105 const QVariantMap props = context.extraProperties();
4106 const QString fontPath = u"ttf://%1"_s.arg( mFontFamily );
4107 int markIndex = !mString.isEmpty() ? mString.at( 0 ).unicode() : 0;
4108 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
4109 QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, u"ttf"_s, context, &markIndex, mColor, size );
4110
4111 // <Rotation>
4112 QString angleFunc;
4113 bool ok;
4114 const double angle = props.value( u"angle"_s, u"0"_s ).toDouble( &ok );
4115 if ( !ok )
4116 {
4117 angleFunc = u"%1 + %2"_s.arg( props.value( u"angle"_s, u"0"_s ).toString() ).arg( mAngle );
4118 }
4119 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
4120 {
4121 angleFunc = QString::number( angle + mAngle );
4122 }
4123 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc, context );
4124
4125 // <Displacement>
4126 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
4128 return true;
4129}
4130
4137
4139{
4141 mStrokeWidthUnit = unit;
4142}
4143
4145{
4146 QPointF chrOffset = mChrOffset;
4147 double chrWidth = mChrWidth;
4148 //calculate width of rendered character
4149 ( void )characterToRender( context, chrOffset, chrWidth );
4150
4151 if ( !mFontMetrics )
4152 mFontMetrics = std::make_unique<QFontMetrics>( mFont );
4153
4154 double scaledSize = calculateSize( context );
4155 if ( !qgsDoubleNear( scaledSize, mOrigSize ) )
4156 {
4157 chrWidth *= scaledSize / mOrigSize;
4158 }
4159 chrWidth *= mFontSizeScale;
4160
4161 bool hasDataDefinedRotation = false;
4162 QPointF offset;
4163 double angle = 0;
4164 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
4165 scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
4166
4167 QTransform transform;
4168
4169 // move to the desired position
4170 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
4171
4172 if ( !qgsDoubleNear( angle, 0.0 ) )
4173 transform.rotate( angle );
4174
4175 QRectF symbolBounds = transform.mapRect( QRectF( -chrWidth / 2.0,
4176 -scaledSize / 2.0,
4177 chrWidth,
4178 scaledSize ) );
4179 return symbolBounds;
4180}
4181
4183{
4184 QgsDebugMsgLevel( u"Entered."_s, 4 );
4185
4186 QDomElement graphicElem = element.firstChildElement( u"Graphic"_s );
4187 if ( graphicElem.isNull() )
4188 return nullptr;
4189
4190 QString name, format;
4191 QColor color;
4192 double size;
4193 int chr;
4194
4195 if ( !QgsSymbolLayerUtils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
4196 return nullptr;
4197
4198 if ( !name.startsWith( "ttf://"_L1 ) || format != "ttf"_L1 )
4199 return nullptr;
4200
4201 const QString fontFamily = name.mid( 6 );
4202
4203 double angle = 0.0;
4204 QString angleFunc;
4205 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
4206 {
4207 bool ok;
4208 const double d = angleFunc.toDouble( &ok );
4209 if ( ok )
4210 angle = d;
4211 }
4212
4213 QPointF offset;
4215
4216 double scaleFactor = 1.0;
4217 const QString uom = element.attribute( u"uom"_s );
4218 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
4219 offset.setX( offset.x() * scaleFactor );
4220 offset.setY( offset.y() * scaleFactor );
4221 size = size * scaleFactor;
4222
4224 m->setOutputUnit( sldUnitSize );
4225 m->setAngle( angle );
4226 m->setOffset( offset );
4227 return m;
4228}
4229
4231{
4232 const QString fontFamily = properties.value( u"font"_s, DEFAULT_FONTMARKER_FONT ).toString();
4233 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( fontFamily );
4234 QString matched;
4235 if ( !QgsFontUtils::fontFamilyMatchOnSystem( processedFamily )
4236 && !QgsApplication::fontManager()->tryToDownloadFontFamily( processedFamily, matched ) )
4237 {
4238 context.pushMessage( QObject::tr( "Font “%1” not available on system" ).arg( processedFamily ) );
4239 }
4240}
4241
4243{
4244 QMap<QString, QgsProperty>::iterator it = mParameters.begin();
4245 for ( ; it != mParameters.end(); ++it )
4246 it.value().prepare( context.renderContext().expressionContext() );
4247
4249}
4250
4251
4253{
4254 QSet<QString> attrs = QgsMarkerSymbolLayer::usedAttributes( context );
4255
4256 QMap<QString, QgsProperty>::const_iterator it = mParameters.constBegin();
4257 for ( ; it != mParameters.constEnd(); ++it )
4258 {
4259 attrs.unite( it.value().referencedFields( context.expressionContext(), true ) );
4260 }
4261
4262 return attrs;
4263}
4264
4265//
4266// QgsAnimatedMarkerSymbolLayer
4267//
4268
4274
4276
4277QgsSymbolLayer *QgsAnimatedMarkerSymbolLayer::create( const QVariantMap &properties ) // cppcheck-suppress duplInheritedMember
4278{
4279 QString path;
4282
4283 if ( properties.contains( u"imageFile"_s ) )
4284 path = properties[u"imageFile"_s].toString();
4285 if ( properties.contains( u"size"_s ) )
4286 size = properties[u"size"_s].toDouble();
4287 if ( properties.contains( u"angle"_s ) )
4288 angle = properties[u"angle"_s].toDouble();
4289
4290 auto m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( path, size, angle );
4291 m->setFrameRate( properties.value( u"frameRate"_s, u"10"_s ).toDouble() );
4292
4293 m->setCommonProperties( properties );
4294 return m.release();
4295}
4296
4298{
4299 return u"AnimatedMarker"_s;
4300}
4301
4303{
4304 QVariantMap res = QgsRasterMarkerSymbolLayer::properties();
4305 res.insert( u"frameRate"_s, mFrameRateFps );
4306 return res;
4307}
4308
4310{
4311 auto m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( mPath, mSize, mAngle );
4312 m->setFrameRate( mFrameRateFps );
4313 copyCommonProperties( m.get() );
4314 return m.release();
4315}
4316
4318{
4320
4321 mPreparedPaths.clear();
4322 if ( !mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Name ) && !mPath.isEmpty() )
4323 {
4325 mStaticPath = true;
4326 }
4327 else
4328 {
4329 mStaticPath = false;
4330 }
4331}
4332
4333QImage QgsAnimatedMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
4334{
4335 if ( !mStaticPath && !mPreparedPaths.contains( path ) )
4336 {
4338 mPreparedPaths.insert( path );
4339 }
4340
4341 const long long mapFrameNumber = context.currentFrame();
4343 const double markerAnimationDuration = totalFrameCount / mFrameRateFps;
4344
4345 double animationTimeSeconds = 0;
4346 if ( mapFrameNumber >= 0 && context.frameRate() > 0 )
4347 {
4348 // render is part of an animation, so we base the calculated frame on that
4349 animationTimeSeconds = mapFrameNumber / context.frameRate();
4350 }
4351 else
4352 {
4353 // render is outside of animation, so base the calculated frame on the current epoch
4354 animationTimeSeconds = QDateTime::currentMSecsSinceEpoch() / 1000.0;
4355 }
4356
4357 const double markerAnimationProgressSeconds = std::fmod( animationTimeSeconds, markerAnimationDuration );
4358 const int movieFrame = static_cast< int >( std::floor( markerAnimationProgressSeconds * mFrameRateFps ) );
4359
4360 bool cached = false;
4361 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking, 96, movieFrame );
4362}
@ DynamicRotation
Rotation of symbol may be changed during rendering and symbol should not be cached.
Definition qgis.h:789
@ IsSymbolLayerSubSymbol
Symbol is being rendered as a sub-symbol of a QgsSymbolLayer.
Definition qgis.h:790
@ CanCalculateMaskGeometryPerFeature
If present, indicates that mask geometry can safely be calculated per feature for the symbol layer....
Definition qgis.h:901
ScaleMethod
Scale methods.
Definition qgis.h:643
@ ScaleDiameter
Calculate scale by the diameter.
Definition qgis.h:645
@ ScaleArea
Calculate scale by the area.
Definition qgis.h:644
QFlags< SymbolLayerFlag > SymbolLayerFlags
Symbol layer flags.
Definition qgis.h:906
MarkerShape
Marker shapes.
Definition qgis.h:3139
@ Pentagon
Pentagon.
Definition qgis.h:3142
@ Arrow
Arrow.
Definition qgis.h:3147
@ Hexagon
Hexagon.
Definition qgis.h:3143
@ EquilateralTriangle
Equilateral triangle.
Definition qgis.h:3145
@ SemiCircle
Semi circle (top half).
Definition qgis.h:3155
@ QuarterCircle
Quarter circle (top left quarter).
Definition qgis.h:3157
@ Circle
Circle.
Definition qgis.h:3148
@ LeftHalfTriangle
Left half of triangle.
Definition qgis.h:3162
@ ArrowHead
Right facing arrow head (unfilled, lines only).
Definition qgis.h:3153
@ ParallelogramRight
Parallelogram that slants right.
Definition qgis.h:3169
@ AsteriskFill
A filled asterisk shape.
Definition qgis.h:3165
@ Octagon
Octagon.
Definition qgis.h:3163
@ HalfArc
A line-only half arc.
Definition qgis.h:3166
@ Line
Vertical line.
Definition qgis.h:3152
@ QuarterSquare
Quarter square (top left quarter).
Definition qgis.h:3158
@ Triangle
Triangle.
Definition qgis.h:3144
@ Cross2
Rotated cross (lines only), 'x' shape.
Definition qgis.h:3151
@ Trapezoid
Trapezoid.
Definition qgis.h:3171
@ ArrowHeadFilled
Right facing filled arrow head.
Definition qgis.h:3154
@ Diamond
Diamond.
Definition qgis.h:3141
@ Shield
A shape consisting of a triangle attached to a rectangle.
Definition qgis.h:3172
@ HalfSquare
Half square (left half).
Definition qgis.h:3159
@ CrossFill
Solid filled cross.
Definition qgis.h:3150
@ Decagon
Decagon.
Definition qgis.h:3175
@ RoundedSquare
A square with rounded corners.
Definition qgis.h:3176
@ RightHalfTriangle
Right half of triangle.
Definition qgis.h:3161
@ Square
Square.
Definition qgis.h:3140
@ ThirdCircle
One third circle (top left third).
Definition qgis.h:3156
@ ThirdArc
A line-only one third arc.
Definition qgis.h:3167
@ SquareWithCorners
A square with diagonal corners.
Definition qgis.h:3164
@ QuarterArc
A line-only one quarter arc.
Definition qgis.h:3168
@ DiamondStar
A 4-sided star.
Definition qgis.h:3173
@ Cross
Cross (lines only).
Definition qgis.h:3149
@ ParallelogramLeft
Parallelogram that slants left.
Definition qgis.h:3170
@ Heart
Heart.
Definition qgis.h:3174
@ DiagonalHalfSquare
Diagonal half square (bottom left half).
Definition qgis.h:3160
VerticalAnchorPoint
Marker symbol vertical anchor points.
Definition qgis.h:833
@ Bottom
Align to bottom of symbol.
Definition qgis.h:836
@ Center
Align to vertical center of symbol.
Definition qgis.h:835
@ Baseline
Align to baseline of symbol, e.g. font baseline for font marker symbol layers. Treated as Bottom if n...
Definition qgis.h:837
@ Top
Align to top of symbol.
Definition qgis.h:834
@ Point
Points.
Definition qgis.h:366
RenderUnit
Rendering size units.
Definition qgis.h:5255
@ Percentage
Percentage of another measurement (e.g., canvas size, feature size).
Definition qgis.h:5259
@ Millimeters
Millimeters.
Definition qgis.h:5256
@ Unknown
Mixed or unknown units.
Definition qgis.h:5262
@ MapUnits
Map units.
Definition qgis.h:5257
@ MetersInMapUnits
Meters value as Map units.
Definition qgis.h:5263
@ RenderingSubSymbol
Set whenever a sub-symbol of a parent symbol is currently being rendered. Can be used during symbol a...
Definition qgis.h:2823
@ RenderSymbolPreview
The render is for a symbol preview only and map based properties may not be available,...
Definition qgis.h:2818
@ RenderLayerTree
The render is for a layer tree display where map based properties are not available and where avoidan...
Definition qgis.h:2829
@ RenderBlocking
Render and load remote sources in the same thread to ensure rendering remote sources (svg and images)...
Definition qgis.h:2817
@ Fill
Fill symbol.
Definition qgis.h:632
HorizontalAnchorPoint
Marker symbol horizontal anchor points.
Definition qgis.h:819
static bool parseBase64DataUrl(const QString &path, QString *mimeType=nullptr, QString *data=nullptr)
Parses a path to determine if it represents a base 64 encoded HTML data URL, and if so,...
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.
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:60
QgsGeometry geometry
Definition qgsfeature.h:71
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.
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.
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.
double mSize
Marker size.
void setVerticalAnchorPoint(Qgis::VerticalAnchorPoint v)
Sets the vertical anchor point for positioning the symbol.
QgsMarkerSymbolLayer(const QgsMarkerSymbolLayer &other)
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:53
bool isActive(int key) const final
Returns true if the collection contains an active property with the specified key.
static QVariantMap propertyMapToVariantMap(const QMap< QString, QgsProperty > &propertyMap)
Convert a map of QgsProperty to a map of QVariant This is useful to save a map of properties.
static QMap< QString, QgsProperty > variantMapToPropertyMap(const QVariantMap &variantMap)
Convert a map of QVariant to a map of QgsProperty This is useful to restore a map of properties.
static QgsProperty fromValue(const QVariant &value, bool isActive=true)
Returns a new StaticProperty created from the specified value.
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.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsRasterMarkerSymbolLayer from an SLD XML element.
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.
Q_DECL_DEPRECATED void writeSldMarker(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Writes the symbol layer definition as a SLD XML element.
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.
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).
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.
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.
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.
@ CapStyle
Line cap style.
@ JoinStyle
Line join style.
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.
QgsSymbolLayer(const QgsSymbolLayer &other)
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:6900
QMap< QString, QString > QgsStringMap
Definition qgis.h:7413
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
#define DEG2RAD(x)
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
#define QgsDebugError(str)
Definition qgslogger.h:59
#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