QGIS API Documentation 3.99.0-Master (e9821da5c6b)
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
1412
1413void QgsSimpleMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1414{
1415 QgsSldExportContext context;
1416 context.setExtraProperties( props );
1417 toSld( doc, element, context );
1418}
1419
1420bool QgsSimpleMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
1421{
1422 return QgsSimpleMarkerSymbolLayerBase::toSld( doc, element, context );
1423}
1424
1425void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1426{
1427 QgsSldExportContext context;
1428 context.setExtraProperties( props );
1429 writeSldMarker( doc, element, context );
1430}
1431
1432bool QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
1433{
1434 // <Graphic>
1435 QDomElement graphicElem = doc.createElement( u"se:Graphic"_s );
1436 element.appendChild( graphicElem );
1437
1438 const QVariantMap props = context.extraProperties();
1440 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
1442
1443 // <Rotation>
1444 QString angleFunc;
1445
1447 {
1448 angleFunc = mDataDefinedProperties.property( QgsSymbolLayer::Property::Angle ).asExpression();
1449 }
1450 else
1451 {
1452 bool ok;
1453 const double angle = props.value( u"angle"_s, u"0"_s ).toDouble( &ok );
1454 if ( !ok )
1455 {
1456 angleFunc = u"%1 + %2"_s.arg( props.value( u"angle"_s, u"0"_s ).toString() ).arg( mAngle );
1457 }
1458 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
1459 {
1460 angleFunc = QString::number( angle + mAngle );
1461 }
1462 }
1463
1464 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc, context );
1465
1466 // <Displacement>
1467 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
1469 return true;
1470}
1471
1472QString QgsSimpleMarkerSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
1473{
1474 Q_UNUSED( mmScaleFactor )
1475 Q_UNUSED( mapUnitScaleFactor )
1476#if 0
1477 QString ogrType = "3"; //default is circle
1478 if ( mName == "square" )
1479 {
1480 ogrType = "5";
1481 }
1482 else if ( mName == "triangle" )
1483 {
1484 ogrType = "7";
1485 }
1486 else if ( mName == "star" )
1487 {
1488 ogrType = "9";
1489 }
1490 else if ( mName == "circle" )
1491 {
1492 ogrType = "3";
1493 }
1494 else if ( mName == "cross" )
1495 {
1496 ogrType = "0";
1497 }
1498 else if ( mName == "x" || mName == "cross2" )
1499 {
1500 ogrType = "1";
1501 }
1502 else if ( mName == "line" )
1503 {
1504 ogrType = "10";
1505 }
1506
1507 QString ogrString;
1508 ogrString.append( "SYMBOL(" );
1509 ogrString.append( "id:" );
1510 ogrString.append( '\"' );
1511 ogrString.append( "ogr-sym-" );
1512 ogrString.append( ogrType );
1513 ogrString.append( '\"' );
1514 ogrString.append( ",c:" );
1515 ogrString.append( mColor.name() );
1516 ogrString.append( ",o:" );
1517 ogrString.append( mStrokeColor.name() );
1518 ogrString.append( QString( ",s:%1mm" ).arg( mSize ) );
1519 ogrString.append( ')' );
1520 return ogrString;
1521#endif //0
1522
1523 QString ogrString;
1524 ogrString.append( "PEN(" );
1525 ogrString.append( "c:" );
1526 ogrString.append( mColor.name() );
1527 ogrString.append( ",w:" );
1528 ogrString.append( QString::number( mSize ) );
1529 ogrString.append( "mm" );
1530 ogrString.append( ")" );
1531 return ogrString;
1532}
1533
1535{
1536 QgsDebugMsgLevel( u"Entered."_s, 4 );
1537
1538 QDomElement graphicElem = element.firstChildElement( u"Graphic"_s );
1539 if ( graphicElem.isNull() )
1540 return nullptr;
1541
1542 QString name = u"square"_s;
1543 QColor color, strokeColor;
1544 double strokeWidth, size;
1545 Qt::PenStyle strokeStyle;
1546
1548 return nullptr;
1549
1550 double angle = 0.0;
1551 QString angleFunc;
1552 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
1553 {
1554 bool ok;
1555 const double d = angleFunc.toDouble( &ok );
1556 if ( ok )
1557 angle = d;
1558 }
1559
1560 QPointF offset;
1562
1563 const Qgis::MarkerShape shape = decodeShape( name );
1564
1565 double scaleFactor = 1.0;
1566 const QString uom = element.attribute( u"uom"_s );
1567 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
1568 size = size * scaleFactor;
1569 offset.setX( offset.x() * scaleFactor );
1570 offset.setY( offset.y() * scaleFactor );
1571
1573 m->setOutputUnit( sldUnitSize );
1574 m->setColor( color );
1576 m->setAngle( angle );
1577 m->setOffset( offset );
1580 return m;
1581}
1582
1584{
1585 Q_UNUSED( context )
1586
1587 if ( mPolygon.count() != 0 )
1588 {
1589 p->drawPolygon( mPolygon );
1590 }
1591 else
1592 {
1593 p->drawPath( mPath );
1594 }
1595}
1596
1597bool QgsSimpleMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
1598{
1599 //data defined size?
1600 double size = mSize;
1601
1602 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
1603
1604 //data defined size
1605 bool ok = true;
1606 if ( hasDataDefinedSize )
1607 {
1609
1610 if ( ok )
1611 {
1612 switch ( mScaleMethod )
1613 {
1615 size = std::sqrt( size );
1616 break;
1618 break;
1619 }
1620 }
1621 }
1622
1624 {
1625 size *= mmMapUnitScaleFactor;
1626 }
1627
1629 {
1631 }
1632 const double halfSize = size / 2.0;
1633
1634 //strokeWidth
1635 double strokeWidth = mStrokeWidth;
1636
1638 {
1641 }
1644 {
1646 }
1647
1648 //color
1649 QColor pc = mPen.color();
1650 QColor bc = mBrush.color();
1652 {
1655 }
1657 {
1660 }
1661
1662 //offset
1663 double offsetX = 0;
1664 double offsetY = 0;
1665 markerOffset( context, offsetX, offsetY );
1666 offsetX *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1667 offsetY *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1668
1669
1670 QPointF off( offsetX, offsetY );
1671
1672 //angle
1673 double angle = mAngle + mLineAngle;
1675 {
1678 }
1679
1682 {
1684 const QString shapeName = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::Name, context.renderContext().expressionContext(), QString(), &ok );
1685 if ( ok )
1686 {
1687 shape = decodeShape( shapeName, &ok );
1688 if ( !ok )
1689 shape = mShape;
1690 }
1691 }
1692
1693 if ( angle )
1694 off = _rotatedOffset( off, angle );
1695
1697
1698 QTransform t;
1699 t.translate( shift.x() + off.x(), shift.y() - off.y() );
1700
1701 if ( !qgsDoubleNear( angle, 0.0 ) )
1702 t.rotate( -angle );
1703
1704 QPolygonF polygon;
1705 if ( shapeToPolygon( shape, polygon ) )
1706 {
1707 t.scale( halfSize, -halfSize );
1708
1709 polygon = t.map( polygon );
1710
1712 p.reserve( polygon.size() );
1713 for ( int i = 0; i < polygon.size(); i++ )
1714 {
1715 p << QgsPoint( polygon[i] );
1716 }
1717
1718 if ( mBrush.style() != Qt::NoBrush )
1719 e.writePolygon( QgsRingSequence() << p, layerName, u"SOLID"_s, bc );
1720 if ( mPen.style() != Qt::NoPen )
1721 e.writePolyline( p, layerName, u"CONTINUOUS"_s, pc, strokeWidth );
1722 }
1723 else if ( shape == Qgis::MarkerShape::Circle )
1724 {
1725 shift += QPointF( off.x(), -off.y() );
1726 if ( mBrush.style() != Qt::NoBrush )
1727 e.writeFilledCircle( layerName, bc, QgsPoint( shift ), halfSize );
1728 if ( mPen.style() != Qt::NoPen )
1729 e.writeCircle( layerName, pc, QgsPoint( shift ), halfSize, u"CONTINUOUS"_s, strokeWidth );
1730 }
1731 else if ( shape == Qgis::MarkerShape::Line )
1732 {
1733 const QPointF pt1 = t.map( QPointF( 0, -halfSize ) );
1734 const QPointF pt2 = t.map( QPointF( 0, halfSize ) );
1735
1736 if ( mPen.style() != Qt::NoPen )
1737 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, u"CONTINUOUS"_s, pc, strokeWidth );
1738 }
1739 else if ( shape == Qgis::MarkerShape::Cross )
1740 {
1741 if ( mPen.style() != Qt::NoPen )
1742 {
1743 const QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
1744 const QPointF pt2 = t.map( QPointF( halfSize, 0 ) );
1745 const QPointF pt3 = t.map( QPointF( 0, -halfSize ) );
1746 const QPointF pt4 = t.map( QPointF( 0, halfSize ) );
1747
1748 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, u"CONTINUOUS"_s, pc, strokeWidth );
1749 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, u"CONTINUOUS"_s, pc, strokeWidth );
1750 }
1751 }
1752 else if ( shape == Qgis::MarkerShape::Cross2 )
1753 {
1754 if ( mPen.style() != Qt::NoPen )
1755 {
1756 const QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
1757 const QPointF pt2 = t.map( QPointF( halfSize, halfSize ) );
1758 const QPointF pt3 = t.map( QPointF( halfSize, -halfSize ) );
1759 const QPointF pt4 = t.map( QPointF( -halfSize, halfSize ) );
1760
1761 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, u"CONTINUOUS"_s, pc, strokeWidth );
1762 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, u"CONTINUOUS"_s, pc, strokeWidth );
1763 }
1764 }
1765 else if ( shape == Qgis::MarkerShape::ArrowHead )
1766 {
1767 if ( mPen.style() != Qt::NoPen )
1768 {
1769 const QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
1770 const QPointF pt2 = t.map( QPointF( 0, 0 ) );
1771 const QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
1772
1773 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, u"CONTINUOUS"_s, pc, strokeWidth );
1774 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt2 ), layerName, u"CONTINUOUS"_s, pc, strokeWidth );
1775 }
1776 }
1777 else
1778 {
1779 QgsDebugError( u"Unsupported dxf marker name %1"_s.arg( encodeShape( shape ) ) );
1780 return false;
1781 }
1782
1783 return true;
1784}
1785
1786
1792
1801
1807
1816
1823
1825{
1826 QRectF symbolBounds = QgsSimpleMarkerSymbolLayerBase::bounds( point, context );
1827
1828 // need to account for stroke width
1829 double penWidth = mStrokeWidth;
1830 bool ok = true;
1832 {
1835 if ( ok )
1836 {
1837 penWidth = strokeWidth;
1838 }
1839 }
1842 {
1844 const QString strokeStyle = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::StrokeStyle, context.renderContext().expressionContext(), QString(), &ok );
1845 if ( ok && strokeStyle == "no"_L1 )
1846 {
1847 penWidth = 0.0;
1848 }
1849 }
1850 else if ( mStrokeStyle == Qt::NoPen )
1851 penWidth = 0;
1852
1853 //antialiasing, add 1 pixel
1854 penWidth += 1;
1855
1856 //extend bounds by pen width / 2.0
1857 symbolBounds.adjust( -penWidth / 2.0, -penWidth / 2.0,
1858 penWidth / 2.0, penWidth / 2.0 );
1859
1860 return symbolBounds;
1861}
1862
1864{
1865 if ( shapeIsFilled( mShape ) )
1866 {
1868 }
1869 else
1870 {
1872 }
1873}
1874
1876{
1877 if ( shapeIsFilled( mShape ) )
1878 {
1879 return fillColor();
1880 }
1881 else
1882 {
1883 return strokeColor();
1884 }
1885}
1886
1887
1888
1889
1890//
1891// QgsFilledMarkerSymbolLayer
1892//
1893
1899
1901
1903{
1904 QString name = DEFAULT_SIMPLEMARKER_NAME;
1908
1909 if ( props.contains( u"name"_s ) )
1910 name = props[u"name"_s].toString();
1911 if ( props.contains( u"size"_s ) )
1912 size = props[u"size"_s].toDouble();
1913 if ( props.contains( u"angle"_s ) )
1914 angle = props[u"angle"_s].toDouble();
1915 if ( props.contains( u"scale_method"_s ) )
1916 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[u"scale_method"_s].toString() );
1917
1919 if ( props.contains( u"offset"_s ) )
1920 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[u"offset"_s].toString() ) );
1921 if ( props.contains( u"offset_unit"_s ) )
1922 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[u"offset_unit"_s].toString() ) );
1923 if ( props.contains( u"offset_map_unit_scale"_s ) )
1924 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"offset_map_unit_scale"_s].toString() ) );
1925 if ( props.contains( u"size_unit"_s ) )
1926 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[u"size_unit"_s].toString() ) );
1927 if ( props.contains( u"size_map_unit_scale"_s ) )
1928 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"size_map_unit_scale"_s].toString() ) );
1929 if ( props.contains( u"horizontal_anchor_point"_s ) )
1930 {
1931 m->setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( props[ u"horizontal_anchor_point"_s].toInt() ) );
1932 }
1933 if ( props.contains( u"vertical_anchor_point"_s ) )
1934 {
1935 m->setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( props[ u"vertical_anchor_point"_s].toInt() ) );
1936 }
1937
1938 m->setSubSymbol( QgsFillSymbol::createSimple( props ).release() );
1939
1941
1942 return m;
1943}
1944
1946{
1947 return u"FilledMarker"_s;
1948}
1949
1951{
1952 if ( mFill )
1953 {
1954 mFill->setRenderHints( mFill->renderHints() | Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol );
1955 mFill->startRender( context.renderContext(), context.fields() );
1956 }
1957
1959}
1960
1962{
1963 if ( mFill )
1964 {
1965 mFill->stopRender( context.renderContext() );
1966 }
1967}
1968
1970{
1971 installMasks( context, true );
1972
1973 // The base class version passes this on to the subsymbol, but we deliberately don't do that here.
1974}
1975
1977{
1978 removeMasks( context, true );
1979
1980 // The base class version passes this on to the subsymbol, but we deliberately don't do that here.
1981}
1982
1984{
1985 QVariantMap map;
1986 map[u"name"_s] = encodeShape( mShape );
1987 map[u"size"_s] = QString::number( mSize );
1988 map[u"size_unit"_s] = QgsUnitTypes::encodeUnit( mSizeUnit );
1989 map[u"size_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1990 map[u"angle"_s] = QString::number( mAngle );
1991 map[u"offset"_s] = QgsSymbolLayerUtils::encodePoint( mOffset );
1992 map[u"offset_unit"_s] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1993 map[u"offset_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1994 map[u"scale_method"_s] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1995 map[u"horizontal_anchor_point"_s] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
1996 map[u"vertical_anchor_point"_s] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
1997
1998 if ( mFill )
1999 {
2000 map[u"color"_s] = QgsColorUtils::colorToString( mFill->color() );
2001 }
2002 return map;
2003}
2004
2012
2014{
2015 return mFill.get();
2016}
2017
2019{
2020 if ( symbol && symbol->type() == Qgis::SymbolType::Fill )
2021 {
2022 mFill.reset( static_cast<QgsFillSymbol *>( symbol ) );
2023 return true;
2024 }
2025 else
2026 {
2027 delete symbol;
2028 return false;
2029 }
2030}
2031
2033{
2034 if ( mFill )
2035 {
2036 return QgsSymbolLayerUtils::estimateMaxSymbolBleed( mFill.get(), context );
2037 }
2038 return 0;
2039}
2040
2042{
2043 QSet<QString> attr = QgsSimpleMarkerSymbolLayerBase::usedAttributes( context );
2044 if ( mFill )
2045 attr.unite( mFill->usedAttributes( context ) );
2046 return attr;
2047}
2048
2050{
2052 return true;
2053 if ( mFill && mFill->hasDataDefinedProperties() )
2054 return true;
2055 return false;
2056}
2057
2059{
2060 mColor = c;
2061 if ( mFill )
2062 mFill->setColor( c );
2063}
2064
2066{
2067 return mFill ? mFill->color() : mColor;
2068}
2069
2076
2078{
2080 if ( mFill )
2081 mFill->setOutputUnit( unit );
2082}
2083
2084void QgsFilledMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
2085{
2086 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
2087 //of the rendered point!
2088
2089 QPainter *p = context.renderContext().painter();
2090 if ( !p )
2091 {
2092 return;
2093 }
2094
2095 const double prevOpacity = mFill->opacity();
2096 mFill->setOpacity( mFill->opacity() * context.opacity() );
2097
2098 if ( shapeIsFilled( shape ) )
2099 {
2100 p->setBrush( Qt::red );
2101 }
2102 else
2103 {
2104 p->setBrush( Qt::NoBrush );
2105 }
2106 p->setPen( Qt::black );
2107
2108 const bool prevIsSubsymbol = context.renderContext().flags() & Qgis::RenderContextFlag::RenderingSubSymbol;
2110
2111 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
2112 if ( !polygon.isEmpty() )
2113 {
2114 mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
2115 }
2116 else
2117 {
2118 const QPolygonF poly = path.toFillPolygon();
2119 mFill->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
2120 }
2121
2123
2124 mFill->setOpacity( prevOpacity );
2125}
2126
2127
2129
2130
2132{
2133 mSize = size;
2134 mAngle = angle;
2135 mOffset = QPointF( 0, 0 );
2137 mStrokeWidth = 0.2;
2139 mColor = QColor( 35, 35, 35 );
2140 mStrokeColor = QColor( 35, 35, 35 );
2141 setPath( path );
2142}
2143
2157
2159
2161{
2162 QString name;
2166
2167 if ( props.contains( u"name"_s ) )
2168 name = props[u"name"_s].toString();
2169 if ( props.contains( u"size"_s ) )
2170 size = props[u"size"_s].toDouble();
2171 if ( props.contains( u"angle"_s ) )
2172 angle = props[u"angle"_s].toDouble();
2173 if ( props.contains( u"scale_method"_s ) )
2174 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[u"scale_method"_s].toString() );
2175
2177
2178 if ( props.contains( u"size_unit"_s ) )
2179 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[u"size_unit"_s].toString() ) );
2180 if ( props.contains( u"size_map_unit_scale"_s ) )
2181 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"size_map_unit_scale"_s].toString() ) );
2182 if ( props.contains( u"fixedAspectRatio"_s ) )
2183 m->setFixedAspectRatio( props[u"fixedAspectRatio"_s].toDouble() );
2184 if ( props.contains( u"offset"_s ) )
2185 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[u"offset"_s].toString() ) );
2186 if ( props.contains( u"offset_unit"_s ) )
2187 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[u"offset_unit"_s].toString() ) );
2188 if ( props.contains( u"offset_map_unit_scale"_s ) )
2189 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"offset_map_unit_scale"_s].toString() ) );
2190 if ( props.contains( u"fill"_s ) )
2191 {
2192 //pre 2.5 projects used "fill"
2193 m->setFillColor( QgsColorUtils::colorFromString( props[u"fill"_s].toString() ) );
2194 }
2195 else if ( props.contains( u"color"_s ) )
2196 {
2197 m->setFillColor( QgsColorUtils::colorFromString( props[u"color"_s].toString() ) );
2198 }
2199 if ( props.contains( u"outline"_s ) )
2200 {
2201 //pre 2.5 projects used "outline"
2202 m->setStrokeColor( QgsColorUtils::colorFromString( props[u"outline"_s].toString() ) );
2203 }
2204 else if ( props.contains( u"outline_color"_s ) )
2205 {
2206 m->setStrokeColor( QgsColorUtils::colorFromString( props[u"outline_color"_s].toString() ) );
2207 }
2208 else if ( props.contains( u"line_color"_s ) )
2209 {
2210 m->setStrokeColor( QgsColorUtils::colorFromString( props[u"line_color"_s].toString() ) );
2211 }
2212
2213 if ( props.contains( u"outline-width"_s ) )
2214 {
2215 //pre 2.5 projects used "outline-width"
2216 m->setStrokeWidth( props[u"outline-width"_s].toDouble() );
2217 }
2218 else if ( props.contains( u"outline_width"_s ) )
2219 {
2220 m->setStrokeWidth( props[u"outline_width"_s].toDouble() );
2221 }
2222 else if ( props.contains( u"line_width"_s ) )
2223 {
2224 m->setStrokeWidth( props[u"line_width"_s].toDouble() );
2225 }
2226
2227 if ( props.contains( u"outline_width_unit"_s ) )
2228 {
2229 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[u"outline_width_unit"_s].toString() ) );
2230 }
2231 else if ( props.contains( u"line_width_unit"_s ) )
2232 {
2233 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[u"line_width_unit"_s].toString() ) );
2234 }
2235 if ( props.contains( u"outline_width_map_unit_scale"_s ) )
2236 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"outline_width_map_unit_scale"_s].toString() ) );
2237
2238 if ( props.contains( u"horizontal_anchor_point"_s ) )
2239 {
2240 m->setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( props[ u"horizontal_anchor_point"_s].toInt() ) );
2241 }
2242 if ( props.contains( u"vertical_anchor_point"_s ) )
2243 {
2244 m->setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( props[ u"vertical_anchor_point"_s].toInt() ) );
2245 }
2246
2248
2250
2251 if ( props.contains( u"parameters"_s ) )
2252 {
2253 const QVariantMap parameters = props[u"parameters"_s].toMap();
2255 }
2256
2257 return m;
2258}
2259
2260void QgsSvgMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
2261{
2262 const QVariantMap::iterator it = properties.find( u"name"_s );
2263 if ( it != properties.end() )
2264 {
2265 if ( saving )
2266 {
2267 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
2268 }
2269 else
2270 {
2271 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
2272 }
2273 }
2274}
2275
2277{
2279 mHasFillParam = false;
2280 mPath = path;
2281 QColor defaultFillColor, defaultStrokeColor;
2282 double strokeWidth, fillOpacity, strokeOpacity;
2283 bool hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
2284 bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
2285 QgsApplication::svgCache()->containsParams( path, mHasFillParam, hasDefaultFillColor, defaultFillColor,
2286 hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
2287 hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2288 hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
2289 hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
2290
2291 const double newFillOpacity = hasFillOpacityParam ? fillColor().alphaF() : 1.0;
2292 const double newStrokeOpacity = hasStrokeOpacityParam ? strokeColor().alphaF() : 1.0;
2293
2294 if ( hasDefaultFillColor )
2295 {
2296 defaultFillColor.setAlphaF( newFillOpacity );
2297 setFillColor( defaultFillColor );
2298 }
2299 if ( hasDefaultFillOpacity )
2300 {
2301 QColor c = fillColor();
2302 c.setAlphaF( fillOpacity );
2303 setFillColor( c );
2304 }
2305 if ( hasDefaultStrokeColor )
2306 {
2307 defaultStrokeColor.setAlphaF( newStrokeOpacity );
2308 setStrokeColor( defaultStrokeColor );
2309 }
2310 if ( hasDefaultStrokeWidth )
2311 {
2313 }
2314 if ( hasDefaultStrokeOpacity )
2315 {
2316 QColor c = strokeColor();
2317 c.setAlphaF( strokeOpacity );
2318 setStrokeColor( c );
2319 }
2320
2322}
2323
2325{
2326 if ( mDefaultAspectRatio == 0.0 )
2327 {
2328 //size
2329 const double size = mSize;
2330 //assume 88 dpi as standard value
2331 const double widthScaleFactor = 3.465;
2332 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( mPath, size, mColor, mStrokeColor, mStrokeWidth, widthScaleFactor );
2333 // set default aspect ratio
2334 mDefaultAspectRatio = svgViewbox.isValid() ? svgViewbox.height() / svgViewbox.width() : 0.0;
2335 }
2336 return mDefaultAspectRatio;
2337}
2338
2340{
2341 const bool aPreservedAspectRatio = preservedAspectRatio();
2342 if ( aPreservedAspectRatio && !par )
2343 {
2345 }
2346 else if ( !aPreservedAspectRatio && par )
2347 {
2348 mFixedAspectRatio = 0.0;
2349 }
2350 return preservedAspectRatio();
2351}
2352
2353void QgsSvgMarkerSymbolLayer::setParameters( const QMap<QString, QgsProperty> &parameters )
2354{
2356}
2357
2358
2360{
2361 return u"SvgMarker"_s;
2362}
2363
2368
2370{
2371 QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
2372 Q_UNUSED( context )
2373}
2374
2376{
2377 Q_UNUSED( context )
2378}
2379
2381{
2382 QPainter *p = context.renderContext().painter();
2383 if ( !p )
2384 return;
2385
2386 bool hasDataDefinedSize = false;
2387 const double scaledWidth = calculateSize( context, hasDataDefinedSize );
2388 const double devicePixelRatio = context.renderContext().devicePixelRatio();
2389 const double width = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2390
2391 //don't render symbols with a width below one or above 10,000 pixels
2392 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2393 {
2394 return;
2395 }
2396
2397 const QgsScopedQPainterState painterState( p );
2398
2399 bool hasDataDefinedAspectRatio = false;
2400 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2401 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2402
2404
2405 double strokeWidth = mStrokeWidth;
2407 {
2410 }
2412
2413 QColor fillColor = mColor;
2414 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
2415 if ( useSelectedColor && mHasFillParam )
2416 {
2418 }
2420 {
2423 }
2424
2425 QColor strokeColor = mStrokeColor;
2427 {
2430 }
2431
2432 QString path = mPath;
2434 {
2437 context.renderContext().pathResolver() );
2438 if ( path != mPath && qgsDoubleNear( aspectRatio, 0.0 ) && !mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Height ) )
2439 {
2440 // adjust height of data defined path
2441 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2442 context.renderContext().scaleFactor(), aspectRatio,
2443 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2444 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2445 }
2446 }
2447
2448 QPointF outputOffset;
2449 double angle = 0.0;
2450 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2451
2452 p->translate( point + outputOffset );
2453
2454 const bool rotated = !qgsDoubleNear( angle, 0 );
2455 if ( rotated )
2456 p->rotate( angle );
2457
2458 bool fitsInCache = true;
2459 bool usePict = true;
2460 const bool rasterizeSelected = !mHasFillParam || mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Name );
2461 if ( ( !context.forceVectorRendering() && !rotated ) || ( useSelectedColor && rasterizeSelected ) )
2462 {
2463 QImage img = QgsApplication::svgCache()->svgAsImage( path, width * devicePixelRatio, fillColor, strokeColor, strokeWidth,
2464 context.renderContext().scaleFactor(), fitsInCache, aspectRatio,
2465 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2466 if ( fitsInCache && img.width() > 1 )
2467 {
2468 usePict = false;
2469
2470 if ( useSelectedColor )
2471 {
2473 }
2474
2475 //consider transparency
2476 if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2477 {
2478 QImage transparentImage = img.copy();
2479 QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2480 if ( devicePixelRatio == 1 )
2481 {
2482 p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
2483 }
2484 else
2485 {
2486 p->drawImage( QRectF( -transparentImage.width() / 2.0 / devicePixelRatio, -transparentImage.height() / 2.0 / devicePixelRatio,
2487 transparentImage.width() / devicePixelRatio, transparentImage.height() / devicePixelRatio
2488 ), transparentImage );
2489 }
2490 }
2491 else
2492 {
2493 if ( devicePixelRatio == 1 )
2494 {
2495 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2496 }
2497 else
2498 {
2499 p->drawImage( QRectF( -img.width() / 2.0 / devicePixelRatio, -img.height() / 2.0 / devicePixelRatio,
2500 img.width() / devicePixelRatio, img.height() / devicePixelRatio ), img );
2501 }
2502 }
2503 }
2504 }
2505
2506 if ( usePict || !fitsInCache )
2507 {
2508 p->setOpacity( context.opacity() );
2510 context.renderContext().scaleFactor(), context.forceVectorRendering(), aspectRatio,
2511 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2512 if ( pct.width() > 1 )
2513 {
2514 QgsPainting::drawPicture( p, QPointF( 0, 0 ), pct );
2515 }
2516 }
2517
2518 // workaround issue with nested QPictures forgetting antialiasing flag - see https://github.com/qgis/QGIS/issues/22909
2520}
2521
2522double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2523{
2524 double scaledSize = mSize;
2526
2527 bool ok = true;
2528 if ( hasDataDefinedSize )
2529 {
2532 }
2533 else
2534 {
2535 hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Width );
2536 if ( hasDataDefinedSize )
2537 {
2539 scaledSize = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::Width, context.renderContext().expressionContext(), mSize, &ok );
2540 }
2541 }
2542
2543 if ( hasDataDefinedSize && ok )
2544 {
2545 switch ( mScaleMethod )
2546 {
2548 scaledSize = std::sqrt( scaledSize );
2549 break;
2551 break;
2552 }
2553 }
2554
2555 return scaledSize;
2556}
2557
2558double QgsSvgMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2559{
2561 if ( !hasDataDefinedAspectRatio )
2562 return mFixedAspectRatio;
2563
2565 return 0.0;
2566
2567 double scaledAspectRatio = mDefaultAspectRatio;
2568 if ( mFixedAspectRatio > 0.0 )
2569 scaledAspectRatio = mFixedAspectRatio;
2570
2571 const double defaultHeight = mSize * scaledAspectRatio;
2572 scaledAspectRatio = defaultHeight / scaledSize;
2573
2574 bool ok = true;
2575 double scaledHeight = scaledSize * scaledAspectRatio;
2577 {
2578 context.setOriginalValueVariable( defaultHeight );
2579 scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::Height, context.renderContext().expressionContext(), defaultHeight, &ok );
2580 }
2581
2582 if ( hasDataDefinedAspectRatio && ok )
2583 {
2584 switch ( mScaleMethod )
2585 {
2587 scaledHeight = sqrt( scaledHeight );
2588 break;
2590 break;
2591 }
2592 }
2593
2594 scaledAspectRatio = scaledHeight / scaledSize;
2595
2596 return scaledAspectRatio;
2597}
2598
2599void QgsSvgMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
2600{
2601 //offset
2602 double offsetX = 0;
2603 double offsetY = 0;
2604 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
2605 offset = QPointF( offsetX, offsetY );
2606
2609 {
2612 }
2613
2614 const bool hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Angle );
2615 if ( hasDataDefinedRotation )
2616 {
2617 // For non-point markers, "dataDefinedRotation" means following the
2618 // shape (shape-data defined). For them, "field-data defined" does
2619 // not work at all. TODO: if "field-data defined" ever gets implemented
2620 // we'll need a way to distinguish here between the two, possibly
2621 // using another flag in renderHints()
2622 const QgsFeature *f = context.feature();
2623 if ( f )
2624 {
2625 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
2626 {
2627 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2628 angle += m2p.mapRotation();
2629 }
2630 }
2631 }
2632
2633 if ( angle )
2635}
2636
2637
2639{
2640 QVariantMap map;
2641 map[u"name"_s] = mPath;
2642 map[u"size"_s] = QString::number( mSize );
2643 map[u"size_unit"_s] = QgsUnitTypes::encodeUnit( mSizeUnit );
2644 map[u"size_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2645 map[u"fixedAspectRatio"_s] = QString::number( mFixedAspectRatio );
2646 map[u"angle"_s] = QString::number( mAngle );
2647 map[u"offset"_s] = QgsSymbolLayerUtils::encodePoint( mOffset );
2648 map[u"offset_unit"_s] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2649 map[u"offset_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2650 map[u"scale_method"_s] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2651 map[u"color"_s] = QgsColorUtils::colorToString( mColor );
2652 map[u"outline_color"_s] = QgsColorUtils::colorToString( mStrokeColor );
2653 map[u"outline_width"_s] = QString::number( mStrokeWidth );
2654 map[u"outline_width_unit"_s] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2655 map[u"outline_width_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2656 map[u"horizontal_anchor_point"_s] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
2657 map[u"vertical_anchor_point"_s] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
2658
2659 map[u"parameters"_s] = QgsProperty::propertyMapToVariantMap( mParameters );
2660
2661 return map;
2662}
2663
2670
2675
2676void QgsSvgMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
2677{
2678 QgsSldExportContext context;
2679 context.setExtraProperties( props );
2680 toSld( doc, element, context );
2681}
2682
2683bool QgsSvgMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
2684{
2685 return QgsMarkerSymbolLayer::toSld( doc, element, context );
2686}
2687
2693
2695{
2697 if ( unit != mStrokeWidthUnit )
2698 {
2700 }
2701 return unit;
2702}
2703
2709
2718
2719void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
2720{
2721 QgsSldExportContext context;
2722 context.setExtraProperties( props );
2723 writeSldMarker( doc, element, context );
2724}
2725
2726bool QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
2727{
2728 // <Graphic>
2729 QDomElement graphicElem = doc.createElement( u"se:Graphic"_s );
2730 element.appendChild( graphicElem );
2731
2732 const QVariantMap props = context.extraProperties();
2733 // encode a parametric SVG reference
2734 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
2737
2738 // <Rotation>
2739 QString angleFunc;
2740 bool ok;
2741 const double angle = props.value( u"angle"_s, u"0"_s ).toDouble( &ok );
2742 if ( !ok )
2743 {
2744 angleFunc = u"%1 + %2"_s.arg( props.value( u"angle"_s, u"0"_s ).toString() ).arg( mAngle );
2745 }
2746 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2747 {
2748 angleFunc = QString::number( angle + mAngle );
2749 }
2750
2751 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc, context );
2752
2753 // <Displacement>
2754 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2756 return true;
2757}
2758
2760{
2761 QgsDebugMsgLevel( u"Entered."_s, 4 );
2762
2763 QDomElement graphicElem = element.firstChildElement( u"Graphic"_s );
2764 if ( graphicElem.isNull() )
2765 return nullptr;
2766
2767 QString path, mimeType;
2768 // Unused and to be DEPRECATED in externalGraphicFromSld
2769 QColor fillColor_;
2770 double size;
2771
2772 if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor_, size ) )
2773 return nullptr;
2774
2775 double scaleFactor = 1.0;
2776 const QString uom = element.attribute( u"uom"_s );
2777 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
2778 size = size * scaleFactor;
2779
2780 if ( mimeType != "image/svg+xml"_L1 )
2781 return nullptr;
2782
2783 double angle = 0.0;
2784 QString angleFunc;
2785 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2786 {
2787 bool ok;
2788 const double d = angleFunc.toDouble( &ok );
2789 if ( ok )
2790 angle = d;
2791 }
2792
2793 QPointF offset;
2795
2796 // Extract parameters from URL
2797 QString realPath { path };
2798 QUrl svgUrl { path };
2799
2800 // Because color definition can start with '#', the url parsing won't recognize the query string entirely
2801 QUrlQuery queryString;
2802
2803 if ( svgUrl.hasQuery() && svgUrl.hasFragment() )
2804 {
2805 const QString queryPart { path.mid( path.indexOf( '?' ) + 1 ) };
2806 queryString.setQuery( queryPart );
2807 }
2808
2809 // Remove query for simple file paths
2810 if ( svgUrl.scheme().isEmpty() || svgUrl.isLocalFile() )
2811 {
2812 svgUrl.setQuery( QString() );
2813 realPath = svgUrl.path();
2814 }
2815
2817
2818 QMap<QString, QgsProperty> params;
2819
2820 bool ok;
2821
2822 if ( queryString.hasQueryItem( u"fill"_s ) )
2823 {
2824 const QColor fillColor { queryString.queryItemValue( u"fill"_s ) };
2825 m->setFillColor( fillColor );
2826 }
2827
2828 if ( queryString.hasQueryItem( u"fill-opacity"_s ) )
2829 {
2830 const double alpha { queryString.queryItemValue( u"fill-opacity"_s ).toDouble( &ok ) };
2831 if ( ok )
2832 {
2833 params.insert( u"fill-opacity"_s, QgsProperty::fromValue( alpha ) );
2834 }
2835 }
2836
2837 if ( queryString.hasQueryItem( u"outline"_s ) )
2838 {
2839 const QColor strokeColor { queryString.queryItemValue( u"outline"_s ) };
2841 }
2842
2843 if ( queryString.hasQueryItem( u"outline-opacity"_s ) )
2844 {
2845 const double alpha { queryString.queryItemValue( u"outline-opacity"_s ).toDouble( &ok ) };
2846 if ( ok )
2847 {
2848 params.insert( u"outline-opacity"_s, QgsProperty::fromValue( alpha ) );
2849 }
2850 }
2851
2852 if ( queryString.hasQueryItem( u"outline-width"_s ) )
2853 {
2854 const int width { queryString.queryItemValue( u"outline-width"_s ).toInt( &ok )};
2855 if ( ok )
2856 {
2857 m->setStrokeWidth( width );
2858 }
2859 }
2860
2861 if ( ! params.isEmpty() )
2862 {
2863 m->setParameters( params );
2864 }
2865
2866 m->setOutputUnit( sldUnitSize );
2867 m->setAngle( angle );
2868 m->setOffset( offset );
2869 return m;
2870}
2871
2872bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
2873{
2874 //size
2875 double size = mSize;
2876
2877 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
2878
2879 bool ok = true;
2880 if ( hasDataDefinedSize )
2881 {
2884 }
2885
2886 if ( hasDataDefinedSize && ok )
2887 {
2888 switch ( mScaleMethod )
2889 {
2891 size = std::sqrt( size );
2892 break;
2894 break;
2895 }
2896 }
2897
2899 {
2900 size *= mmMapUnitScaleFactor;
2901 }
2902
2903//offset, angle
2904 QPointF offset = mOffset;
2905
2907 {
2909 const QVariant val = mDataDefinedProperties.value( QgsSymbolLayer::Property::Offset, context.renderContext().expressionContext(), QString() );
2910 const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
2911 if ( ok )
2912 offset = res;
2913 }
2914 const double offsetX = offset.x();
2915 const double offsetY = offset.y();
2916
2917 QPointF outputOffset( offsetX, offsetY );
2918
2919 double angle = mAngle + mLineAngle;
2921 {
2924 }
2925
2926 if ( angle )
2927 outputOffset = _rotatedOffset( outputOffset, angle );
2928
2930
2931 QString path = mPath;
2933 {
2936 context.renderContext().pathResolver() );
2937 }
2938
2939 double strokeWidth = mStrokeWidth;
2941 {
2944 }
2946
2947 QColor fillColor = mColor;
2949 {
2952 }
2953
2954 QColor strokeColor = mStrokeColor;
2956 {
2959 }
2960
2962
2963 const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, size, fillColor, strokeColor, strokeWidth,
2965 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2966
2967 QSvgRenderer r( svgContent );
2968 if ( !r.isValid() )
2969 return false;
2970
2971 QgsDxfPaintDevice pd( &e );
2972 pd.setDrawingSize( QSizeF( r.defaultSize() ) );
2973
2974 QSizeF outSize( r.defaultSize() );
2975 outSize.scale( size, size, Qt::KeepAspectRatio );
2976
2977 QPainter p;
2978 p.begin( &pd );
2979 if ( !qgsDoubleNear( angle, 0.0 ) )
2980 {
2981 p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
2982 p.rotate( angle );
2983 p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
2984 }
2985 pd.setShift( shift + QPointF( outputOffset.x(), -outputOffset.y() ) );
2986 pd.setOutputSize( QRectF( -outSize.width() / 2.0, -outSize.height() / 2.0, outSize.width(), outSize.height() ) );
2987 pd.setLayer( layerName );
2988 r.render( &p );
2989 p.end();
2990 return true;
2991}
2992
2994{
2995 bool hasDataDefinedSize = false;
2996 double scaledWidth = calculateSize( context, hasDataDefinedSize );
2997
2998 bool hasDataDefinedAspectRatio = false;
2999 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
3000 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
3001
3002 scaledWidth = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
3003 scaledHeight = context.renderContext().convertToPainterUnits( scaledHeight, mSizeUnit, mSizeMapUnitScale );
3004
3005 //don't render symbols with size below one or above 10,000 pixels
3006 if ( static_cast< int >( scaledWidth ) < 1 || 10000.0 < scaledWidth )
3007 {
3008 return QRectF();
3009 }
3010
3011 QPointF outputOffset;
3012 double angle = 0.0;
3013 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
3014
3015 double strokeWidth = mStrokeWidth;
3017 {
3020 }
3022
3023 QString path = mPath;
3025 {
3028 context.renderContext().pathResolver() );
3029 if ( path != mPath && qgsDoubleNear( aspectRatio, 0.0 ) && !mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Height ) )
3030 {
3031 // need to get colors to take advantage of cached SVGs
3032 QColor fillColor = mColor;
3034 {
3037 }
3038
3039 const QColor strokeColor = mStrokeColor;
3041 {
3044 }
3045
3047
3048 // adjust height of data defined path
3049 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
3050 context.renderContext().scaleFactor(), aspectRatio,
3051 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
3052 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
3053 }
3054 }
3055
3056 QTransform transform;
3057 // move to the desired position
3058 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3059
3060 if ( !qgsDoubleNear( angle, 0.0 ) )
3061 transform.rotate( angle );
3062
3063 //antialiasing
3064 strokeWidth += 1.0 / 2.0;
3065
3066 QRectF symbolBounds = transform.mapRect( QRectF( -scaledWidth / 2.0,
3067 -scaledHeight / 2.0,
3068 scaledWidth,
3069 scaledHeight ) );
3070
3071 //extend bounds by pen width / 2.0
3072 symbolBounds.adjust( -strokeWidth / 2.0, -strokeWidth / 2.0,
3073 strokeWidth / 2.0, strokeWidth / 2.0 );
3074
3075 return symbolBounds;
3076}
3077
3079
3081 : mPath( path )
3082{
3083 mSize = size;
3084 mAngle = angle;
3085 mOffset = QPointF( 0, 0 );
3088}
3089
3091
3093{
3094 QString path;
3098
3099 if ( props.contains( u"imageFile"_s ) )
3100 path = props[u"imageFile"_s].toString();
3101 if ( props.contains( u"size"_s ) )
3102 size = props[u"size"_s].toDouble();
3103 if ( props.contains( u"angle"_s ) )
3104 angle = props[u"angle"_s].toDouble();
3105 if ( props.contains( u"scale_method"_s ) )
3106 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[u"scale_method"_s].toString() );
3107
3108 auto m = std::make_unique< QgsRasterMarkerSymbolLayer >( path, size, angle, scaleMethod );
3109 m->setCommonProperties( props );
3110 return m.release();
3111}
3112
3114{
3115 const QDomElement graphicElem = element.firstChildElement( u"Graphic"_s );
3116 if ( graphicElem.isNull() )
3117 return nullptr;
3118
3119 const QDomElement externalGraphicElem = graphicElem.firstChildElement( u"ExternalGraphic"_s );
3120 if ( externalGraphicElem.isNull() )
3121 return nullptr;
3122
3123 const QDomElement onlineResourceElem = externalGraphicElem.firstChildElement( u"OnlineResource"_s );
3124 const QDomElement inlineContentElem = externalGraphicElem.firstChildElement( u"InlineContent"_s );
3125
3126 QString url;
3127 if ( !onlineResourceElem.isNull() )
3128 {
3129 url = onlineResourceElem.attribute( u"href"_s );
3130 // no further processing to do, both base64 data urls and direct file/http urls are compatible with raster markers already
3131 }
3132 else if ( !inlineContentElem.isNull() && inlineContentElem.attribute( u"encoding"_s ) == "base64"_L1 )
3133 {
3134 url = u"base64:"_s + inlineContentElem.text();
3135 }
3136 else
3137 {
3138 return nullptr;
3139 }
3140
3142 // TODO: parse other attributes from the SLD spec (Opacity, Size, Rotation, AnchorPoint, Displacement)
3143 return m;
3144}
3145
3147{
3148 if ( properties.contains( u"alpha"_s ) )
3149 {
3150 setOpacity( properties[u"alpha"_s].toDouble() );
3151 }
3152
3153 if ( properties.contains( u"size_unit"_s ) )
3154 setSizeUnit( QgsUnitTypes::decodeRenderUnit( properties[u"size_unit"_s].toString() ) );
3155 if ( properties.contains( u"size_map_unit_scale"_s ) )
3156 setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[u"size_map_unit_scale"_s].toString() ) );
3157 if ( properties.contains( u"fixedAspectRatio"_s ) )
3158 setFixedAspectRatio( properties[u"fixedAspectRatio"_s].toDouble() );
3159
3160 if ( properties.contains( u"offset"_s ) )
3161 setOffset( QgsSymbolLayerUtils::decodePoint( properties[u"offset"_s].toString() ) );
3162 if ( properties.contains( u"offset_unit"_s ) )
3163 setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[u"offset_unit"_s].toString() ) );
3164 if ( properties.contains( u"offset_map_unit_scale"_s ) )
3165 setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[u"offset_map_unit_scale"_s].toString() ) );
3166
3167 if ( properties.contains( u"horizontal_anchor_point"_s ) )
3168 {
3169 setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( properties[ u"horizontal_anchor_point"_s].toInt() ) );
3170 }
3171 if ( properties.contains( u"vertical_anchor_point"_s ) )
3172 {
3173 setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( properties[ u"vertical_anchor_point"_s].toInt() ) );
3174 }
3175
3178}
3179
3180void QgsRasterMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
3181{
3182 const QVariantMap::iterator it = properties.find( u"name"_s );
3183 if ( it != properties.end() && it.value().userType() == QMetaType::Type::QString )
3184 {
3185 if ( saving )
3186 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
3187 else
3188 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
3189 }
3190}
3191
3193{
3194 mPath = path;
3196}
3197
3199{
3200 const bool aPreservedAspectRatio = preservedAspectRatio();
3201 if ( aPreservedAspectRatio && !par )
3202 {
3204 }
3205 else if ( !aPreservedAspectRatio && par )
3206 {
3207 mFixedAspectRatio = 0.0;
3208 }
3209 return preservedAspectRatio();
3210}
3211
3213{
3214 if ( mDefaultAspectRatio == 0.0 && !mPath.isEmpty() )
3215 {
3217 mDefaultAspectRatio = ( !size.isNull() && size.isValid() && size.width() > 0 ) ? static_cast< double >( size.height() ) / static_cast< double >( size.width() ) : 0.0;
3218 }
3219 return mDefaultAspectRatio;
3220}
3221
3223{
3224 return u"RasterMarker"_s;
3225}
3226
3231
3233{
3234 QPainter *p = context.renderContext().painter();
3235 if ( !p )
3236 return;
3237
3238 QString path = mPath;
3240 {
3243 }
3244
3245 if ( path.isEmpty() )
3246 return;
3247
3248 double width = 0.0;
3249 double height = 0.0;
3250
3251 bool hasDataDefinedSize = false;
3252 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3253
3254 bool hasDataDefinedAspectRatio = false;
3255 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3256
3257 QPointF outputOffset;
3258 double angle = 0.0;
3259
3260 // RenderPercentage Unit Type takes original image size
3262 {
3264 if ( size.isEmpty() )
3265 return;
3266
3267 width = ( scaledSize * static_cast< double >( size.width() ) ) / 100.0;
3268 height = ( scaledSize * static_cast< double >( size.height() ) ) / 100.0;
3269
3270 // don't render symbols with size below one or above 10,000 pixels
3271 if ( static_cast< int >( width ) < 1 || 10000.0 < width || static_cast< int >( height ) < 1 || 10000.0 < height )
3272 return;
3273
3274 calculateOffsetAndRotation( context, width, height, outputOffset, angle );
3275 }
3276 else
3277 {
3278 width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3279 height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3280
3281 if ( preservedAspectRatio() && path != mPath )
3282 {
3284 if ( !size.isNull() && size.isValid() && size.width() > 0 )
3285 {
3286 height = width * ( static_cast< double >( size.height() ) / static_cast< double >( size.width() ) );
3287 }
3288 }
3289
3290 // don't render symbols with size below one or above 10,000 pixels
3291 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
3292 return;
3293
3294 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3295 }
3296
3297 const QgsScopedQPainterState painterState( p );
3298 p->translate( point + outputOffset );
3299
3300 const bool rotated = !qgsDoubleNear( angle, 0 );
3301 if ( rotated )
3302 p->rotate( angle );
3303
3304 double opacity = mOpacity;
3306 {
3309 }
3310 opacity *= context.opacity();
3311
3312 QImage img = fetchImage( context.renderContext(), path, QSize( width, preservedAspectRatio() ? 0 : width * aspectRatio ), preservedAspectRatio(), opacity );
3313 if ( !img.isNull() )
3314 {
3315 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
3316 if ( useSelectedColor )
3317 {
3319 }
3320
3321 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
3322 }
3323}
3324
3325QImage QgsRasterMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
3326{
3327 bool cached = false;
3328 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking );
3329}
3330
3331double QgsRasterMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
3332{
3333 double scaledSize = mSize;
3335
3336 bool ok = true;
3337 if ( hasDataDefinedSize )
3338 {
3341 }
3342 else
3343 {
3344 hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Width );
3345 if ( hasDataDefinedSize )
3346 {
3348 scaledSize = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::Width, context.renderContext().expressionContext(), mSize, &ok );
3349 }
3350 }
3351
3352 if ( hasDataDefinedSize && ok )
3353 {
3354 switch ( mScaleMethod )
3355 {
3357 scaledSize = std::sqrt( scaledSize );
3358 break;
3360 break;
3361 }
3362 }
3363
3364 return scaledSize;
3365}
3366
3367double QgsRasterMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
3368{
3370 if ( !hasDataDefinedAspectRatio )
3371 return mFixedAspectRatio;
3372
3374 return 0.0;
3375
3376 double scaledAspectRatio = mDefaultAspectRatio;
3377 if ( mFixedAspectRatio > 0.0 )
3378 scaledAspectRatio = mFixedAspectRatio;
3379
3380 const double defaultHeight = mSize * scaledAspectRatio;
3381 scaledAspectRatio = defaultHeight / scaledSize;
3382
3383 bool ok = true;
3384 double scaledHeight = scaledSize * scaledAspectRatio;
3386 {
3387 context.setOriginalValueVariable( defaultHeight );
3388 scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::Height, context.renderContext().expressionContext(), defaultHeight, &ok );
3389 }
3390
3391 if ( hasDataDefinedAspectRatio && ok )
3392 {
3393 switch ( mScaleMethod )
3394 {
3396 scaledHeight = sqrt( scaledHeight );
3397 break;
3399 break;
3400 }
3401 }
3402
3403 scaledAspectRatio = scaledHeight / scaledSize;
3404
3405 return scaledAspectRatio;
3406}
3407
3408void QgsRasterMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
3409{
3410 //offset
3411 double offsetX = 0;
3412 double offsetY = 0;
3413 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
3414 offset = QPointF( offsetX, offsetY );
3415
3418 {
3421 }
3422
3423 const bool hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Angle );
3424 if ( hasDataDefinedRotation )
3425 {
3426 const QgsFeature *f = context.feature();
3427 if ( f )
3428 {
3429 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3430 {
3431 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3432 angle += m2p.mapRotation();
3433 }
3434 }
3435 }
3436
3437 if ( angle )
3439}
3440
3441
3443{
3444 QVariantMap map;
3445 map[u"imageFile"_s] = mPath;
3446 map[u"size"_s] = QString::number( mSize );
3447 map[u"size_unit"_s] = QgsUnitTypes::encodeUnit( mSizeUnit );
3448 map[u"size_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3449 map[u"fixedAspectRatio"_s] = QString::number( mFixedAspectRatio );
3450 map[u"angle"_s] = QString::number( mAngle );
3451 map[u"alpha"_s] = QString::number( mOpacity );
3452 map[u"offset"_s] = QgsSymbolLayerUtils::encodePoint( mOffset );
3453 map[u"offset_unit"_s] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3454 map[u"offset_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3455 map[u"scale_method"_s] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
3456 map[u"horizontal_anchor_point"_s] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
3457 map[u"vertical_anchor_point"_s] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
3458 return map;
3459}
3460
3462{
3463 auto m = std::make_unique< QgsRasterMarkerSymbolLayer >();
3464 m->mPath = mPath;
3465 m->mDefaultAspectRatio = mDefaultAspectRatio;
3466 m->mSize = mSize;
3467 m->mAngle = mAngle;
3468 // other members are copied by:
3469 copyCommonProperties( m.get() );
3470 return m.release();
3471}
3472
3473
3487
3493
3495{
3496 return QColor();
3497}
3498
3503
3508
3510{
3511 bool hasDataDefinedSize = false;
3512 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3513 const double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3514 bool hasDataDefinedAspectRatio = false;
3515 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3516 const double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3517
3518 //don't render symbols with size below one or above 10,000 pixels
3519 if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
3520 {
3521 return QRectF();
3522 }
3523
3524 QPointF outputOffset;
3525 double angle = 0.0;
3526 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3527
3528 QTransform transform;
3529
3530 // move to the desired position
3531 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3532
3533 if ( !qgsDoubleNear( angle, 0.0 ) )
3534 transform.rotate( angle );
3535
3536 QRectF symbolBounds = transform.mapRect( QRectF( -width / 2.0,
3537 -height / 2.0,
3538 width,
3539 height ) );
3540
3541 return symbolBounds;
3542}
3543
3544void QgsRasterMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
3545{
3546 QgsSldExportContext context;
3547 context.setExtraProperties( props );
3548 writeSldMarker( doc, element, context );
3549}
3550
3551bool QgsRasterMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
3552{
3553 Q_UNUSED( context )
3554
3555 // <Graphic>
3556 QDomElement graphicElem = doc.createElement( u"se:Graphic"_s );
3557 element.appendChild( graphicElem );
3558
3559 // <ExternalGraphic>
3560 QDomElement extGraphElem = doc.createElement( u"se:ExternalGraphic"_s );
3561 graphicElem.appendChild( extGraphElem );
3562
3563 QMimeDatabase mimeDB;
3564 QMimeType mimeType;
3565
3566 QString base64data;
3567 if ( mPath.startsWith( "base64:"_L1 ) )
3568 {
3569 base64data = mPath.mid( 7 );
3570 }
3571 else
3572 {
3573 QString mime;
3574 QString data;
3576 {
3577 base64data = data;
3578 }
3579 }
3580
3581 if ( !base64data.isEmpty() )
3582 {
3583 // <InlineContent>
3584 QDomElement inlineContEleme = doc.createElement( u"se:InlineContent"_s );
3585
3586 inlineContEleme.setAttribute( u"encoding"_s, u"base64"_s );
3587 inlineContEleme.appendChild( doc.createTextNode( base64data ) );
3588 extGraphElem.appendChild( inlineContEleme );
3589
3590 // determine mime type
3591 const QByteArray ba = QByteArray::fromBase64( base64data.toUtf8() );
3592 mimeType = mimeDB.mimeTypeForData( ba );
3593 }
3594 else
3595 {
3596 // <ExternalGraphic>
3597 QDomElement onlineResElem = doc.createElement( u"se:OnlineResource"_s );
3598 QString url = mPath;
3599
3600 onlineResElem.setAttribute( u"xlink:href"_s, url );
3601 onlineResElem.setAttribute( u"xlink:type"_s, u"simple"_s );
3602 extGraphElem.appendChild( onlineResElem );
3603
3604 // determine mime type
3605 if ( mPath.startsWith( "http://"_L1 ) || mPath.startsWith( "https://"_L1 ) )
3606 {
3607 // Qt can't guess mime type for remote URLs, and it seems geoserver can handle wrong image mime types
3608 // but not generic ones, so let's hardcode to png.
3609 mimeType = mimeDB.mimeTypeForName( "image/png" );
3610 }
3611 else
3612 {
3613 mimeType = mimeDB.mimeTypeForUrl( url );
3614 }
3615 }
3616
3617 QDomElement formatElem = doc.createElement( u"se:Format"_s );
3618 formatElem.appendChild( doc.createTextNode( mimeType.name() ) );
3619 extGraphElem.appendChild( formatElem );
3620
3621 // TODO: write other attributes from the SLD spec (Opacity, Size, Rotation, AnchorPoint, Displacement)
3622 return true;
3623}
3624
3626
3627QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QString chr, double pointSize, const QColor &color, double angle )
3628{
3629 mFontFamily = fontFamily;
3630 mString = chr;
3631 mColor = color;
3632 mAngle = angle;
3633 mSize = pointSize;
3634 mOrigSize = pointSize;
3636 mOffset = QPointF( 0, 0 );
3638 mStrokeColor = DEFAULT_FONTMARKER_BORDERCOLOR;
3639 mStrokeWidth = 0.0;
3640 mStrokeWidthUnit = Qgis::RenderUnit::Millimeters;
3641 mPenJoinStyle = DEFAULT_FONTMARKER_JOINSTYLE;
3642}
3643
3645
3647{
3649 QString string = DEFAULT_FONTMARKER_CHR;
3650 double pointSize = DEFAULT_FONTMARKER_SIZE;
3653
3654 if ( props.contains( u"font"_s ) )
3655 fontFamily = props[u"font"_s].toString();
3656 if ( props.contains( u"chr"_s ) && props[u"chr"_s].toString().length() > 0 )
3657 {
3658 string = props["chr"].toString();
3659 const thread_local QRegularExpression charRegExp( u"%1([0-9]+)%1"_s.arg( FONTMARKER_CHR_FIX ) );
3660 QRegularExpressionMatch match = charRegExp.match( string );
3661 while ( match.hasMatch() )
3662 {
3663 QChar replacement = QChar( match.captured( 1 ).toUShort() );
3664 string = string.mid( 0, match.capturedStart( 0 ) ) + replacement + string.mid( match.capturedEnd( 0 ) );
3665 match = charRegExp.match( string );
3666 }
3667 }
3668
3669 if ( props.contains( u"size"_s ) )
3670 pointSize = props[u"size"_s].toDouble();
3671 if ( props.contains( u"color"_s ) )
3672 color = QgsColorUtils::colorFromString( props[u"color"_s].toString() );
3673 if ( props.contains( u"angle"_s ) )
3674 angle = props[u"angle"_s].toDouble();
3675
3677
3678 if ( props.contains( u"font_style"_s ) )
3679 m->setFontStyle( props[u"font_style"_s].toString() );
3680 if ( props.contains( u"outline_color"_s ) )
3681 m->setStrokeColor( QgsColorUtils::colorFromString( props[u"outline_color"_s].toString() ) );
3682 if ( props.contains( u"outline_width"_s ) )
3683 m->setStrokeWidth( props[u"outline_width"_s].toDouble() );
3684 if ( props.contains( u"offset"_s ) )
3685 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[u"offset"_s].toString() ) );
3686 if ( props.contains( u"offset_unit"_s ) )
3687 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[u"offset_unit"_s].toString() ) );
3688 if ( props.contains( u"offset_map_unit_scale"_s ) )
3689 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"offset_map_unit_scale"_s].toString() ) );
3690 if ( props.contains( u"size_unit"_s ) )
3691 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[u"size_unit"_s].toString() ) );
3692 if ( props.contains( u"size_map_unit_scale"_s ) )
3693 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"size_map_unit_scale"_s].toString() ) );
3694 if ( props.contains( u"outline_width_unit"_s ) )
3695 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[u"outline_width_unit"_s].toString() ) );
3696 if ( props.contains( u"outline_width_map_unit_scale"_s ) )
3697 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"outline_width_map_unit_scale"_s].toString() ) );
3698 if ( props.contains( u"joinstyle"_s ) )
3699 m->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[u"joinstyle"_s].toString() ) );
3700 if ( props.contains( u"horizontal_anchor_point"_s ) )
3701 m->setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( props[ u"horizontal_anchor_point"_s].toInt() ) );
3702 if ( props.contains( u"vertical_anchor_point"_s ) )
3703 m->setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( props[ u"vertical_anchor_point"_s].toInt() ) );
3704
3706
3707 return m;
3708}
3709
3711{
3712 return u"FontMarker"_s;
3713}
3714
3719
3721{
3722 QColor brushColor = mColor;
3723 QColor penColor = mStrokeColor;
3724
3725 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
3726 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
3727
3728 mBrush = QBrush( brushColor );
3729 mPen = QPen( penColor );
3730 mPen.setJoinStyle( mPenJoinStyle );
3731 mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
3732
3733 mFont = QgsFontUtils::createFont( QgsApplication::fontManager()->processFontFamilyName( mFontFamily ) );
3734 if ( !mFontStyle.isEmpty() )
3735 {
3736 mFont.setStyleName( QgsFontUtils::translateNamedStyle( mFontStyle ) );
3737 }
3738
3739 double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3740 mNonZeroFontSize = !qgsDoubleNear( sizePixels, 0.0 );
3741
3742 if ( mNonZeroFontSize && sizePixels > MAX_FONT_CHARACTER_SIZE_IN_PIXELS )
3743 {
3744 // if font is too large (e.g using map units and map is very zoomed in), then we limit
3745 // the font size and instead scale up the painter.
3746 // this avoids issues with massive font sizes (eg https://github.com/qgis/QGIS/issues/42270)
3747 mFontSizeScale = sizePixels / MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3748 sizePixels = MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3749 }
3750 else
3751 mFontSizeScale = 1.0;
3752
3753 // if a non zero, but small pixel size results, round up to 2 pixels so that a "dot" is at least visible
3754 // (if we set a <=1 pixel size here Qt will reset the font to a default size, leading to much too large symbols)
3755 mFont.setPixelSize( std::max( 2, static_cast< int >( std::round( sizePixels ) ) ) );
3756 mFontMetrics = std::make_unique<QFontMetrics>( mFont );
3757 mChrWidth = mFontMetrics->horizontalAdvance( mString );
3758 switch ( mVerticalAnchorPoint )
3759 {
3761 {
3762 mChrOffset = QPointF( mChrWidth / 2.0, -sizePixels / 2.0 );
3763 break;
3764 }
3768 {
3769 mChrOffset = QPointF( mChrWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3770 break;
3771 }
3772 }
3773 mOrigSize = mSize; // save in case the size would be data defined
3774
3775 // use caching only when not using a data defined character
3776 mUseCachedPath = !mDataDefinedProperties.isActive( QgsSymbolLayer::Property::FontFamily ) &&
3779 if ( mUseCachedPath )
3780 {
3781 QPointF chrOffset = mChrOffset;
3782 double chrWidth;
3783 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3784 mCachedPath = QPainterPath();
3785 mCachedPath.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3786 }
3787}
3788
3790{
3791 Q_UNUSED( context )
3792}
3793
3794QString QgsFontMarkerSymbolLayer::characterToRender( QgsSymbolRenderContext &context, QPointF &charOffset, double &charWidth )
3795{
3796 charOffset = mChrOffset;
3797 QString stringToRender = mString;
3799 {
3800 context.setOriginalValueVariable( mString );
3802 if ( stringToRender != mString )
3803 {
3804 charWidth = mFontMetrics->horizontalAdvance( stringToRender );
3805 switch ( mVerticalAnchorPoint )
3806 {
3808 {
3809 const double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3810 charOffset = QPointF( charWidth / 2.0, -sizePixels / 2.0 );
3811 break;
3812 }
3816 {
3817 charOffset = QPointF( charWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3818 break;
3819 }
3820 }
3821 }
3822 }
3823 return stringToRender;
3824}
3825
3826void QgsFontMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
3827 double scaledSize,
3828 bool &hasDataDefinedRotation,
3829 QPointF &offset,
3830 double &angle ) const
3831{
3832 //offset
3833 double offsetX = 0;
3834 double offsetY = 0;
3835 markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
3836 offset = QPointF( offsetX, offsetY );
3837 hasDataDefinedRotation = false;
3838
3839 //angle
3840 bool ok = true;
3843 {
3846
3847 // If the expression evaluation was not successful, fallback to static value
3848 if ( !ok )
3850
3851 hasDataDefinedRotation = true;
3852 }
3853
3854 hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || hasDataDefinedRotation;
3855 if ( hasDataDefinedRotation )
3856 {
3857 // For non-point markers, "dataDefinedRotation" means following the
3858 // shape (shape-data defined). For them, "field-data defined" does
3859 // not work at all. TODO: if "field-data defined" ever gets implemented
3860 // we'll need a way to distinguish here between the two, possibly
3861 // using another flag in renderHints()
3862 const QgsFeature *f = context.feature();
3863 if ( f )
3864 {
3865 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3866 {
3867 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3868 angle += m2p.mapRotation();
3869 }
3870 }
3871 }
3872
3873 if ( angle )
3875}
3876
3877double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context )
3878{
3879 double scaledSize = mSize;
3880 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
3881
3882 bool ok = true;
3883 if ( hasDataDefinedSize )
3884 {
3886 scaledSize = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::Size, context.renderContext().expressionContext(), mSize, &ok );
3887 }
3888
3889 if ( hasDataDefinedSize && ok )
3890 {
3891 switch ( mScaleMethod )
3892 {
3894 scaledSize = std::sqrt( scaledSize );
3895 break;
3897 break;
3898 }
3899 }
3900 return scaledSize;
3901}
3902
3904{
3905 QPainter *p = context.renderContext().painter();
3906 if ( !p || !mNonZeroFontSize )
3907 return;
3908
3909 QTransform transform;
3910
3911 bool ok;
3912 QColor brushColor = mColor;
3914 {
3916 brushColor = mDataDefinedProperties.valueAsColor( QgsSymbolLayer::Property::FillColor, context.renderContext().expressionContext(), brushColor );
3917 }
3918 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
3919 brushColor = useSelectedColor ? context.renderContext().selectionColor() : brushColor;
3920 if ( !useSelectedColor || !SELECTION_IS_OPAQUE )
3921 {
3922 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
3923 }
3924 mBrush.setColor( brushColor );
3925
3926 QColor penColor = mStrokeColor;
3928 {
3930 penColor = mDataDefinedProperties.valueAsColor( QgsSymbolLayer::Property::StrokeColor, context.renderContext().expressionContext(), penColor );
3931 }
3932 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
3933
3934 double penWidth = context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3936 {
3937 context.setOriginalValueVariable( mStrokeWidth );
3938 const double strokeWidth = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::StrokeWidth, context.renderContext().expressionContext(), mStrokeWidth, &ok );
3939 if ( ok )
3940 {
3941 penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3942 }
3943 }
3944
3946 {
3948 const QString style = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::JoinStyle, context.renderContext().expressionContext(), QString(), &ok );
3949 if ( ok )
3950 {
3951 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
3952 }
3953 }
3954
3955 const QgsScopedQPainterState painterState( p );
3956 p->setBrush( mBrush );
3957 if ( !qgsDoubleNear( penWidth, 0.0 ) )
3958 {
3959 mPen.setColor( penColor );
3960 mPen.setWidthF( penWidth );
3961 p->setPen( mPen );
3962 }
3963 else
3964 {
3965 p->setPen( Qt::NoPen );
3966 }
3967
3969 {
3970 context.setOriginalValueVariable( mFontFamily );
3971 const QString fontFamily = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::FontFamily, context.renderContext().expressionContext(), mFontFamily, &ok );
3972 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( ok ? fontFamily : mFontFamily );
3973 QgsFontUtils::setFontFamily( mFont, processedFamily );
3974 }
3976 {
3977 context.setOriginalValueVariable( mFontStyle );
3978 const QString fontStyle = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::FontStyle, context.renderContext().expressionContext(), mFontStyle, &ok );
3980 }
3982 {
3983 mFontMetrics = std::make_unique<QFontMetrics>( mFont );
3984 }
3985
3986 QPointF chrOffset = mChrOffset;
3987 double chrWidth;
3988 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3989
3990 const double sizeToRender = calculateSize( context );
3991
3992 bool hasDataDefinedRotation = false;
3993 QPointF offset;
3994 double angle = 0;
3995 calculateOffsetAndRotation( context, sizeToRender, hasDataDefinedRotation, offset, angle );
3996
3997 p->translate( point.x() + offset.x(), point.y() + offset.y() );
3998
3999 if ( !qgsDoubleNear( angle, 0.0 ) )
4000 transform.rotate( angle );
4001
4002 if ( !qgsDoubleNear( sizeToRender, mOrigSize ) )
4003 {
4004 const double s = sizeToRender / mOrigSize;
4005 transform.scale( s, s );
4006 }
4007
4008 if ( !qgsDoubleNear( mFontSizeScale, 1.0 ) )
4009 transform.scale( mFontSizeScale, mFontSizeScale );
4010
4011 if ( mUseCachedPath )
4012 {
4013 p->drawPath( transform.map( mCachedPath ) );
4014 }
4015 else
4016 {
4017 QPainterPath path;
4018 path.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
4019 p->drawPath( transform.map( path ) );
4020 }
4021}
4022
4024{
4025 QVariantMap props;
4026 props[u"font"_s] = mFontFamily;
4027 props[u"font_style"_s] = mFontStyle;
4028 QString chr = mString;
4029 for ( int i = 0; i < 32; i++ )
4030 {
4031 if ( i == 9 || i == 10 || i == 13 )
4032 {
4033 continue;
4034 }
4035 chr.replace( QChar( i ), u"%1%2%1"_s.arg( FONTMARKER_CHR_FIX, QString::number( i ) ) );
4036 }
4037 props[u"chr"_s] = chr;
4038 props[u"size"_s] = QString::number( mSize );
4039 props[u"size_unit"_s] = QgsUnitTypes::encodeUnit( mSizeUnit );
4040 props[u"size_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
4041 props[u"color"_s] = QgsColorUtils::colorToString( mColor );
4042 props[u"outline_color"_s] = QgsColorUtils::colorToString( mStrokeColor );
4043 props[u"outline_width"_s] = QString::number( mStrokeWidth );
4044 props[u"outline_width_unit"_s] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
4045 props[u"outline_width_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
4046 props[u"joinstyle"_s] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
4047 props[u"angle"_s] = QString::number( mAngle );
4048 props[u"offset"_s] = QgsSymbolLayerUtils::encodePoint( mOffset );
4049 props[u"offset_unit"_s] = QgsUnitTypes::encodeUnit( mOffsetUnit );
4050 props[u"offset_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
4051 props[u"horizontal_anchor_point"_s] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
4052 props[u"vertical_anchor_point"_s] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
4053 return props;
4054}
4055
4057{
4058 QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( mFontFamily, mString, mSize, mColor, mAngle );
4059 m->setFontStyle( mFontStyle );
4060 m->setStrokeColor( mStrokeColor );
4061 m->setStrokeWidth( mStrokeWidth );
4062 m->setStrokeWidthUnit( mStrokeWidthUnit );
4063 m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
4064 m->setPenJoinStyle( mPenJoinStyle );
4065 m->setOffset( mOffset );
4068 m->setSizeUnit( mSizeUnit );
4073 return m;
4074}
4075
4076void QgsFontMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
4077{
4078 QgsSldExportContext context;
4079 context.setExtraProperties( props );
4080 toSld( doc, element, context );
4081}
4082
4083bool QgsFontMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
4084{
4085 return QgsMarkerSymbolLayer::toSld( doc, element, context );
4086}
4087
4088void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
4089{
4090 QgsSldExportContext context;
4091 context.setExtraProperties( props );
4092 writeSldMarker( doc, element, context );
4093}
4094
4095bool QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
4096{
4097 // <Graphic>
4098 QDomElement graphicElem = doc.createElement( u"se:Graphic"_s );
4099 element.appendChild( graphicElem );
4100
4101 const QVariantMap props = context.extraProperties();
4102 const QString fontPath = u"ttf://%1"_s.arg( mFontFamily );
4103 int markIndex = !mString.isEmpty() ? mString.at( 0 ).unicode() : 0;
4104 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
4105 QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, u"ttf"_s, context, &markIndex, mColor, size );
4106
4107 // <Rotation>
4108 QString angleFunc;
4109 bool ok;
4110 const double angle = props.value( u"angle"_s, u"0"_s ).toDouble( &ok );
4111 if ( !ok )
4112 {
4113 angleFunc = u"%1 + %2"_s.arg( props.value( u"angle"_s, u"0"_s ).toString() ).arg( mAngle );
4114 }
4115 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
4116 {
4117 angleFunc = QString::number( angle + mAngle );
4118 }
4119 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc, context );
4120
4121 // <Displacement>
4122 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
4124 return true;
4125}
4126
4133
4135{
4137 mStrokeWidthUnit = unit;
4138}
4139
4141{
4142 QPointF chrOffset = mChrOffset;
4143 double chrWidth = mChrWidth;
4144 //calculate width of rendered character
4145 ( void )characterToRender( context, chrOffset, chrWidth );
4146
4147 if ( !mFontMetrics )
4148 mFontMetrics = std::make_unique<QFontMetrics>( mFont );
4149
4150 double scaledSize = calculateSize( context );
4151 if ( !qgsDoubleNear( scaledSize, mOrigSize ) )
4152 {
4153 chrWidth *= scaledSize / mOrigSize;
4154 }
4155 chrWidth *= mFontSizeScale;
4156
4157 bool hasDataDefinedRotation = false;
4158 QPointF offset;
4159 double angle = 0;
4160 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
4161 scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
4162
4163 QTransform transform;
4164
4165 // move to the desired position
4166 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
4167
4168 if ( !qgsDoubleNear( angle, 0.0 ) )
4169 transform.rotate( angle );
4170
4171 QRectF symbolBounds = transform.mapRect( QRectF( -chrWidth / 2.0,
4172 -scaledSize / 2.0,
4173 chrWidth,
4174 scaledSize ) );
4175 return symbolBounds;
4176}
4177
4179{
4180 QgsDebugMsgLevel( u"Entered."_s, 4 );
4181
4182 QDomElement graphicElem = element.firstChildElement( u"Graphic"_s );
4183 if ( graphicElem.isNull() )
4184 return nullptr;
4185
4186 QString name, format;
4187 QColor color;
4188 double size;
4189 int chr;
4190
4191 if ( !QgsSymbolLayerUtils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
4192 return nullptr;
4193
4194 if ( !name.startsWith( "ttf://"_L1 ) || format != "ttf"_L1 )
4195 return nullptr;
4196
4197 const QString fontFamily = name.mid( 6 );
4198
4199 double angle = 0.0;
4200 QString angleFunc;
4201 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
4202 {
4203 bool ok;
4204 const double d = angleFunc.toDouble( &ok );
4205 if ( ok )
4206 angle = d;
4207 }
4208
4209 QPointF offset;
4211
4212 double scaleFactor = 1.0;
4213 const QString uom = element.attribute( u"uom"_s );
4214 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
4215 offset.setX( offset.x() * scaleFactor );
4216 offset.setY( offset.y() * scaleFactor );
4217 size = size * scaleFactor;
4218
4220 m->setOutputUnit( sldUnitSize );
4221 m->setAngle( angle );
4222 m->setOffset( offset );
4223 return m;
4224}
4225
4227{
4228 const QString fontFamily = properties.value( u"font"_s, DEFAULT_FONTMARKER_FONT ).toString();
4229 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( fontFamily );
4230 QString matched;
4231 if ( !QgsFontUtils::fontFamilyMatchOnSystem( processedFamily )
4232 && !QgsApplication::fontManager()->tryToDownloadFontFamily( processedFamily, matched ) )
4233 {
4234 context.pushMessage( QObject::tr( "Font “%1” not available on system" ).arg( processedFamily ) );
4235 }
4236}
4237
4239{
4240 QMap<QString, QgsProperty>::iterator it = mParameters.begin();
4241 for ( ; it != mParameters.end(); ++it )
4242 it.value().prepare( context.renderContext().expressionContext() );
4243
4245}
4246
4247
4249{
4250 QSet<QString> attrs = QgsMarkerSymbolLayer::usedAttributes( context );
4251
4252 QMap<QString, QgsProperty>::const_iterator it = mParameters.constBegin();
4253 for ( ; it != mParameters.constEnd(); ++it )
4254 {
4255 attrs.unite( it.value().referencedFields( context.expressionContext(), true ) );
4256 }
4257
4258 return attrs;
4259}
4260
4261//
4262// QgsAnimatedMarkerSymbolLayer
4263//
4264
4270
4272
4273QgsSymbolLayer *QgsAnimatedMarkerSymbolLayer::create( const QVariantMap &properties ) // cppcheck-suppress duplInheritedMember
4274{
4275 QString path;
4278
4279 if ( properties.contains( u"imageFile"_s ) )
4280 path = properties[u"imageFile"_s].toString();
4281 if ( properties.contains( u"size"_s ) )
4282 size = properties[u"size"_s].toDouble();
4283 if ( properties.contains( u"angle"_s ) )
4284 angle = properties[u"angle"_s].toDouble();
4285
4286 auto m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( path, size, angle );
4287 m->setFrameRate( properties.value( u"frameRate"_s, u"10"_s ).toDouble() );
4288
4289 m->setCommonProperties( properties );
4290 return m.release();
4291}
4292
4294{
4295 return u"AnimatedMarker"_s;
4296}
4297
4299{
4300 QVariantMap res = QgsRasterMarkerSymbolLayer::properties();
4301 res.insert( u"frameRate"_s, mFrameRateFps );
4302 return res;
4303}
4304
4306{
4307 auto m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( mPath, mSize, mAngle );
4308 m->setFrameRate( mFrameRateFps );
4309 copyCommonProperties( m.get() );
4310 return m.release();
4311}
4312
4314{
4316
4317 mPreparedPaths.clear();
4318 if ( !mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Name ) && !mPath.isEmpty() )
4319 {
4321 mStaticPath = true;
4322 }
4323 else
4324 {
4325 mStaticPath = false;
4326 }
4327}
4328
4329QImage QgsAnimatedMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
4330{
4331 if ( !mStaticPath && !mPreparedPaths.contains( path ) )
4332 {
4334 mPreparedPaths.insert( path );
4335 }
4336
4337 const long long mapFrameNumber = context.currentFrame();
4339 const double markerAnimationDuration = totalFrameCount / mFrameRateFps;
4340
4341 double animationTimeSeconds = 0;
4342 if ( mapFrameNumber >= 0 && context.frameRate() > 0 )
4343 {
4344 // render is part of an animation, so we base the calculated frame on that
4345 animationTimeSeconds = mapFrameNumber / context.frameRate();
4346 }
4347 else
4348 {
4349 // render is outside of animation, so base the calculated frame on the current epoch
4350 animationTimeSeconds = QDateTime::currentMSecsSinceEpoch() / 1000.0;
4351 }
4352
4353 const double markerAnimationProgressSeconds = std::fmod( animationTimeSeconds, markerAnimationDuration );
4354 const int movieFrame = static_cast< int >( std::floor( markerAnimationProgressSeconds * mFrameRateFps ) );
4355
4356 bool cached = false;
4357 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking, 96, movieFrame );
4358}
@ 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:3151
@ Pentagon
Pentagon.
Definition qgis.h:3154
@ Arrow
Arrow.
Definition qgis.h:3159
@ Hexagon
Hexagon.
Definition qgis.h:3155
@ EquilateralTriangle
Equilateral triangle.
Definition qgis.h:3157
@ SemiCircle
Semi circle (top half).
Definition qgis.h:3167
@ QuarterCircle
Quarter circle (top left quarter).
Definition qgis.h:3169
@ Circle
Circle.
Definition qgis.h:3160
@ LeftHalfTriangle
Left half of triangle.
Definition qgis.h:3174
@ ArrowHead
Right facing arrow head (unfilled, lines only).
Definition qgis.h:3165
@ ParallelogramRight
Parallelogram that slants right.
Definition qgis.h:3181
@ AsteriskFill
A filled asterisk shape.
Definition qgis.h:3177
@ Octagon
Octagon.
Definition qgis.h:3175
@ HalfArc
A line-only half arc.
Definition qgis.h:3178
@ Line
Vertical line.
Definition qgis.h:3164
@ QuarterSquare
Quarter square (top left quarter).
Definition qgis.h:3170
@ Triangle
Triangle.
Definition qgis.h:3156
@ Cross2
Rotated cross (lines only), 'x' shape.
Definition qgis.h:3163
@ Trapezoid
Trapezoid.
Definition qgis.h:3183
@ ArrowHeadFilled
Right facing filled arrow head.
Definition qgis.h:3166
@ Diamond
Diamond.
Definition qgis.h:3153
@ Shield
A shape consisting of a triangle attached to a rectangle.
Definition qgis.h:3184
@ HalfSquare
Half square (left half).
Definition qgis.h:3171
@ CrossFill
Solid filled cross.
Definition qgis.h:3162
@ Decagon
Decagon.
Definition qgis.h:3187
@ RoundedSquare
A square with rounded corners.
Definition qgis.h:3188
@ RightHalfTriangle
Right half of triangle.
Definition qgis.h:3173
@ Square
Square.
Definition qgis.h:3152
@ ThirdCircle
One third circle (top left third).
Definition qgis.h:3168
@ ThirdArc
A line-only one third arc.
Definition qgis.h:3179
@ SquareWithCorners
A square with diagonal corners.
Definition qgis.h:3176
@ QuarterArc
A line-only one quarter arc.
Definition qgis.h:3180
@ DiamondStar
A 4-sided star.
Definition qgis.h:3185
@ Cross
Cross (lines only).
Definition qgis.h:3161
@ ParallelogramLeft
Parallelogram that slants left.
Definition qgis.h:3182
@ Heart
Heart.
Definition qgis.h:3186
@ DiagonalHalfSquare
Diagonal half square (bottom left half).
Definition qgis.h:3172
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:5279
@ Percentage
Percentage of another measurement (e.g., canvas size, feature size).
Definition qgis.h:5283
@ Millimeters
Millimeters.
Definition qgis.h:5280
@ Unknown
Mixed or unknown units.
Definition qgis.h:5286
@ MapUnits
Map units.
Definition qgis.h:5281
@ MetersInMapUnits
Meters value as Map units.
Definition qgis.h:5287
@ 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.
void copyCommonProperties(QgsSymbolLayer *destLayer) const
Copies all common base class properties from this layer to another symbol layer.
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.
@ 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.
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:6924
QMap< QString, QString > QgsStringMap
Definition qgis.h:7437
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