QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
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
104
115
117
119{
120 switch ( shape )
121 {
152 return true;
153
161 return false;
162 }
163 return true;
164}
165
167{
168 const bool hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Angle );
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 );
185 {
186 // rendering for symbol previews -- an size in meters in map units can't be calculated, so treat the size as millimeters
187 // and clamp it to a reasonable range. It's the best we can do in this situation!
188 scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
189 }
190
191 const double half = scaledSize / 2.0;
192 transform.scale( half, half );
193 }
194
195 // rotate if the rotation is not going to be changed during the rendering
196 if ( !hasDataDefinedRotation && !qgsDoubleNear( mAngle, 0.0 ) )
197 {
198 transform.rotate( mAngle );
199 }
200
201 if ( !mPolygon.isEmpty() )
202 mPolygon = transform.map( mPolygon );
203 else
204 mPath = transform.map( mPath );
205
207}
208
210{
211 Q_UNUSED( context )
212}
213
215{
216 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
217 //of the rendered point!
218
219 QPainter *p = context.renderContext().painter();
220 if ( !p )
221 {
222 return;
223 }
224
225 bool hasDataDefinedSize = false;
226 const double scaledSize = calculateSize( context, hasDataDefinedSize );
227
228 bool hasDataDefinedRotation = false;
229 QPointF offset;
230 double angle = 0;
231 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
232
233 //data defined shape?
234 bool createdNewPath = false;
235 bool ok = true;
236 Qgis::MarkerShape symbol = mShape;
238 {
239 context.setOriginalValueVariable( encodeShape( symbol ) );
240 const QVariant exprVal = mDataDefinedProperties.value( QgsSymbolLayer::Property::Name, context.renderContext().expressionContext() );
241 if ( !QgsVariantUtils::isNull( exprVal ) )
242 {
243 const Qgis::MarkerShape decoded = decodeShape( exprVal.toString(), &ok );
244 if ( ok )
245 {
246 symbol = decoded;
247
248 if ( !prepareMarkerShape( symbol ) ) // drawing as a polygon
249 {
250 prepareMarkerPath( symbol ); // drawing as a painter path
251 }
252 createdNewPath = true;
253 }
254 }
255 else
256 {
257 symbol = mShape;
258 }
259 }
260
261 QTransform transform;
262
263 // move to the desired position
264 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
265
266 // resize if necessary
267 if ( hasDataDefinedSize || createdNewPath )
268 {
269 double s = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
272 {
273 // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
274 // and clamp it to a reasonable range. It's the best we can do in this situation!
275 s = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
276 }
277 const double half = s / 2.0;
278 transform.scale( half, half );
279 }
280
281 if ( !qgsDoubleNear( angle, 0.0 ) && ( hasDataDefinedRotation || createdNewPath ) )
282 {
283 transform.rotate( angle );
284 }
285
286 //need to pass: symbol, polygon, path
287
288 QPolygonF polygon;
289 QPainterPath path;
290 if ( !mPolygon.isEmpty() )
291 {
292 polygon = transform.map( mPolygon );
293 }
294 else
295 {
296 path = transform.map( mPath );
297 }
298 draw( context, symbol, polygon, path );
299}
300
302{
303 bool hasDataDefinedSize = false;
304 double scaledSize = calculateSize( context, hasDataDefinedSize );
305
306 bool hasDataDefinedRotation = false;
307 QPointF offset;
308 double angle = 0;
309 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
310
311 scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
312
313 QTransform transform;
314
315 // move to the desired position
316 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
317
318 if ( !qgsDoubleNear( angle, 0.0 ) )
319 transform.rotate( angle );
320
321 return transform.mapRect( QRectF( -scaledSize / 2.0, -scaledSize / 2.0, scaledSize, scaledSize ) );
322}
323
325{
326 if ( ok )
327 *ok = true;
328 const QString cleaned = name.toLower().trimmed();
329
330 if ( cleaned == "square"_L1 || cleaned == "rectangle"_L1 )
332 else if ( cleaned == "trapezoid"_L1 )
334 else if ( cleaned == "parallelogram_right"_L1 )
336 else if ( cleaned == "parallelogram_left"_L1 )
338 else if ( cleaned == "square_with_corners"_L1 )
340 else if ( cleaned == "rounded_square"_L1 )
342 else if ( cleaned == "diamond"_L1 )
344 else if ( cleaned == "shield"_L1 )
346 else if ( cleaned == "pentagon"_L1 )
348 else if ( cleaned == "hexagon"_L1 )
350 else if ( cleaned == "octagon"_L1 )
352 else if ( cleaned == "decagon"_L1 )
354 else if ( cleaned == "triangle"_L1 )
356 else if ( cleaned == "equilateral_triangle"_L1 )
358 else if ( cleaned == "star_diamond"_L1 )
360 else if ( cleaned == "star"_L1 || cleaned == "regular_star"_L1 )
362 else if ( cleaned == "heart"_L1 )
364 else if ( cleaned == "arrow"_L1 )
366 else if ( cleaned == "circle"_L1 )
368 else if ( cleaned == "cross"_L1 )
370 else if ( cleaned == "cross_fill"_L1 )
372 else if ( cleaned == "cross2"_L1 || cleaned == "x"_L1 )
374 else if ( cleaned == "line"_L1 )
376 else if ( cleaned == "arrowhead"_L1 )
378 else if ( cleaned == "filled_arrowhead"_L1 )
380 else if ( cleaned == "semi_circle"_L1 )
382 else if ( cleaned == "third_circle"_L1 )
384 else if ( cleaned == "quarter_circle"_L1 )
386 else if ( cleaned == "quarter_square"_L1 )
388 else if ( cleaned == "half_square"_L1 )
390 else if ( cleaned == "diagonal_half_square"_L1 )
392 else if ( cleaned == "right_half_triangle"_L1 )
394 else if ( cleaned == "left_half_triangle"_L1 )
396 else if ( cleaned == "asterisk_fill"_L1 )
398 else if ( cleaned == "half_arc"_L1 )
400 else if ( cleaned == "third_arc"_L1 )
402 else if ( cleaned == "quarter_arc"_L1 )
404
405 if ( ok )
406 *ok = false;
408}
409
411{
412 switch ( shape )
413 {
415 return u"square"_s;
417 return u"quarter_square"_s;
419 return u"half_square"_s;
421 return u"diagonal_half_square"_s;
423 return u"parallelogram_right"_s;
425 return u"parallelogram_left"_s;
427 return u"trapezoid"_s;
429 return u"shield"_s;
431 return u"diamond"_s;
433 return u"pentagon"_s;
435 return u"hexagon"_s;
437 return u"octagon"_s;
439 return u"decagon"_s;
441 return u"square_with_corners"_s;
443 return u"rounded_square"_s;
445 return u"triangle"_s;
447 return u"equilateral_triangle"_s;
449 return u"left_half_triangle"_s;
451 return u"right_half_triangle"_s;
453 return u"star_diamond"_s;
455 return u"star"_s;
457 return u"heart"_s;
459 return u"arrow"_s;
461 return u"filled_arrowhead"_s;
463 return u"cross_fill"_s;
465 return u"circle"_s;
467 return u"cross"_s;
469 return u"cross2"_s;
471 return u"line"_s;
473 return u"arrowhead"_s;
475 return u"semi_circle"_s;
477 return u"third_circle"_s;
479 return u"quarter_circle"_s;
481 return u"asterisk_fill"_s;
483 return u"half_arc"_s;
485 return u"third_arc"_s;
487 return u"quarter_arc"_s;
488 }
489 return QString();
490}
491
496
498{
499 polygon.clear();
500
501 switch ( shape )
502 {
504 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 1, 1 ) ) );
505 return true;
506
508 {
509 static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 0.6072;
510
511 polygon
512 << 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 ) << QPointF( 1, 0.5 ) << QPointF( -1, 0.5 ) << QPointF( -0.5, -0.5 ) << QPointF( 0.5, -0.5 );
538 return true;
539
541 polygon << QPointF( 0.5, 0.5 ) << QPointF( 1, -0.5 ) << QPointF( -0.5, -0.5 ) << QPointF( -1, 0.5 ) << QPointF( 0.5, 0.5 );
542 return true;
543
545 polygon << QPointF( 1, 0.5 ) << QPointF( 0.5, -0.5 ) << QPointF( -1, -0.5 ) << QPointF( -0.5, 0.5 ) << QPointF( 1, 0.5 );
546 return true;
547
549 polygon << QPointF( -1, 0 ) << QPointF( 0, 1 ) << QPointF( 1, 0 ) << QPointF( 0, -1 ) << QPointF( -1, 0 );
550 return true;
551
553 polygon << QPointF( 1, 0.5 ) << QPointF( 1, -1 ) << QPointF( -1, -1 ) << QPointF( -1, 0.5 ) << QPointF( 0, 1 ) << QPointF( 1, 0.5 );
554 return true;
555
557 /* angular-representation of hardcoded values used
558 polygon << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288.0 ) ) )
559 << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) )
560 << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) )
561 << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) )
562 << QPointF( 0, -1 ); */
563 polygon << QPointF( -0.9511, -0.3090 ) << QPointF( -0.5878, 0.8090 ) << QPointF( 0.5878, 0.8090 ) << QPointF( 0.9511, -0.3090 ) << QPointF( 0, -1 ) << QPointF( -0.9511, -0.3090 );
564 return true;
565
567 /* angular-representation of hardcoded values used
568 polygon << QPointF( std::sin( DEG2RAD( 300.0 ) ), - std::cos( DEG2RAD( 300.0 ) ) )
569 << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
570 << QPointF( std::sin( DEG2RAD( 180.0 ) ), - std::cos( DEG2RAD( 180.0 ) ) )
571 << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
572 << QPointF( std::sin( DEG2RAD( 60.0 ) ), - std::cos( DEG2RAD( 60.0 ) ) )
573 << QPointF( 0, -1 ); */
574 polygon << QPointF( -0.8660, -0.5 ) << QPointF( -0.8660, 0.5 ) << QPointF( 0, 1 ) << QPointF( 0.8660, 0.5 ) << QPointF( 0.8660, -0.5 ) << QPointF( 0, -1 ) << QPointF( -0.8660, -0.5 );
575 return true;
576
578 {
579 static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 1.0 / ( 1 + M_SQRT2 );
580
581 polygon
582 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 )
583 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, 1 )
584 << QPointF( 1, VERTEX_OFFSET_FROM_ORIGIN )
585 << QPointF( 1, -VERTEX_OFFSET_FROM_ORIGIN )
586 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, -1 )
587 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, -1 )
588 << QPointF( -1, -VERTEX_OFFSET_FROM_ORIGIN )
589 << QPointF( -1, VERTEX_OFFSET_FROM_ORIGIN )
590 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 );
591 return true;
592 }
593
595 {
596 polygon
597 << QPointF( 0.587785252, 0.809016994 )
598 << QPointF( 0.951056516, 0.309016994 )
599 << QPointF( 0.951056516, -0.309016994 )
600 << QPointF( 0.587785252, -0.809016994 )
601 << QPointF( 0, -1 )
602 << QPointF( -0.587785252, -0.809016994 )
603 << QPointF( -0.951056516, -0.309016994 )
604 << QPointF( -0.951056516, 0.309016994 )
605 << QPointF( -0.587785252, 0.809016994 )
606 << QPointF( 0, 1 )
607 << QPointF( 0.587785252, 0.809016994 );
608 return true;
609 }
610
612 polygon << QPointF( -1, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
613 return true;
614
616 /* angular-representation of hardcoded values used
617 polygon << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
618 << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
619 << QPointF( 0, -1 ); */
620 polygon << QPointF( -0.8660, 0.5 ) << QPointF( 0.8660, 0.5 ) << QPointF( 0, -1 ) << QPointF( -0.8660, 0.5 );
621 return true;
622
624 polygon << QPointF( 0, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( 0, 1 );
625 return true;
626
628 polygon << QPointF( -1, 1 ) << QPointF( 0, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
629 return true;
630
632 {
633 const double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
634
635 polygon
636 << QPointF( inner_r * std::sin( DEG2RAD( 315.0 ) ), -inner_r * std::cos( DEG2RAD( 315.0 ) ) )
637 << QPointF( std::sin( DEG2RAD( 270 ) ), -std::cos( DEG2RAD( 270 ) ) )
638 << QPointF( inner_r * std::sin( DEG2RAD( 225.0 ) ), -inner_r * std::cos( DEG2RAD( 225.0 ) ) )
639 << QPointF( std::sin( DEG2RAD( 180 ) ), -std::cos( DEG2RAD( 180 ) ) )
640 << QPointF( inner_r * std::sin( DEG2RAD( 135.0 ) ), -inner_r * std::cos( DEG2RAD( 135.0 ) ) )
641 << QPointF( std::sin( DEG2RAD( 90 ) ), -std::cos( DEG2RAD( 90 ) ) )
642 << QPointF( inner_r * std::sin( DEG2RAD( 45.0 ) ), -inner_r * std::cos( DEG2RAD( 45.0 ) ) )
643 << QPointF( std::sin( DEG2RAD( 0 ) ), -std::cos( DEG2RAD( 0 ) ) )
644 << QPointF( inner_r * std::sin( DEG2RAD( 315.0 ) ), -inner_r * std::cos( DEG2RAD( 315.0 ) ) );
645 return true;
646 }
647
649 {
650 const double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
651
652 polygon
653 << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), -inner_r * std::cos( DEG2RAD( 324.0 ) ) ) // 324
654 << QPointF( std::sin( DEG2RAD( 288.0 ) ), -std::cos( DEG2RAD( 288 ) ) ) // 288
655 << QPointF( inner_r * std::sin( DEG2RAD( 252.0 ) ), -inner_r * std::cos( DEG2RAD( 252.0 ) ) ) // 252
656 << QPointF( std::sin( DEG2RAD( 216.0 ) ), -std::cos( DEG2RAD( 216.0 ) ) ) // 216
657 << QPointF( 0, inner_r ) // 180
658 << QPointF( std::sin( DEG2RAD( 144.0 ) ), -std::cos( DEG2RAD( 144.0 ) ) ) // 144
659 << QPointF( inner_r * std::sin( DEG2RAD( 108.0 ) ), -inner_r * std::cos( DEG2RAD( 108.0 ) ) ) // 108
660 << QPointF( std::sin( DEG2RAD( 72.0 ) ), -std::cos( DEG2RAD( 72.0 ) ) ) // 72
661 << QPointF( inner_r * std::sin( DEG2RAD( 36.0 ) ), -inner_r * std::cos( DEG2RAD( 36.0 ) ) ) // 36
662 << QPointF( 0, -1 )
663 << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), -inner_r * std::cos( DEG2RAD( 324.0 ) ) ); // 324; // 0
664 return true;
665 }
666
668 polygon << QPointF( 0, -1 ) << QPointF( 0.5, -0.5 ) << QPointF( 0.25, -0.5 ) << QPointF( 0.25, 1 ) << QPointF( -0.25, 1 ) << QPointF( -0.25, -0.5 ) << QPointF( -0.5, -0.5 ) << QPointF( 0, -1 );
669 return true;
670
672 polygon << QPointF( 0, 0 ) << QPointF( -1, 1 ) << QPointF( -1, -1 ) << QPointF( 0, 0 );
673 return true;
674
676 polygon
677 << QPointF( -1, -0.2 )
678 << QPointF( -1, -0.2 )
679 << QPointF( -1, 0.2 )
680 << QPointF( -0.2, 0.2 )
681 << QPointF( -0.2, 1 )
682 << QPointF( 0.2, 1 )
683 << QPointF( 0.2, 0.2 )
684 << QPointF( 1, 0.2 )
685 << QPointF( 1, -0.2 )
686 << QPointF( 0.2, -0.2 )
687 << QPointF( 0.2, -1 )
688 << QPointF( -0.2, -1 )
689 << QPointF( -0.2, -0.2 )
690 << QPointF( -1, -0.2 );
691 return true;
692
694 {
695 static constexpr double THICKNESS = 0.3;
696 static constexpr double HALF_THICKNESS = THICKNESS / 2.0;
697 static constexpr double INTERSECTION_POINT = THICKNESS / M_SQRT2;
698 static constexpr double DIAGONAL1 = M_SQRT1_2 - INTERSECTION_POINT * 0.5;
699 static constexpr double DIAGONAL2 = M_SQRT1_2 + INTERSECTION_POINT * 0.5;
700
701 polygon
702 << QPointF( -HALF_THICKNESS, -1 )
703 << QPointF( HALF_THICKNESS, -1 )
704 << QPointF( HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
705 << QPointF( DIAGONAL1, -DIAGONAL2 )
706 << QPointF( DIAGONAL2, -DIAGONAL1 )
707 << QPointF( HALF_THICKNESS + INTERSECTION_POINT, -HALF_THICKNESS )
708 << QPointF( 1, -HALF_THICKNESS )
709 << QPointF( 1, HALF_THICKNESS )
710 << QPointF( HALF_THICKNESS + INTERSECTION_POINT, HALF_THICKNESS )
711 << QPointF( DIAGONAL2, DIAGONAL1 )
712 << QPointF( DIAGONAL1, DIAGONAL2 )
713 << QPointF( HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
714 << QPointF( HALF_THICKNESS, 1 )
715 << QPointF( -HALF_THICKNESS, 1 )
716 << QPointF( -HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
717 << QPointF( -DIAGONAL1, DIAGONAL2 )
718 << QPointF( -DIAGONAL2, DIAGONAL1 )
719 << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, HALF_THICKNESS )
720 << QPointF( -1, HALF_THICKNESS )
721 << QPointF( -1, -HALF_THICKNESS )
722 << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, -HALF_THICKNESS )
723 << QPointF( -DIAGONAL2, -DIAGONAL1 )
724 << QPointF( -DIAGONAL1, -DIAGONAL2 )
725 << QPointF( -HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
726 << QPointF( -HALF_THICKNESS, -1 );
727 return true;
728 }
729
743 return false;
744 }
745
746 return false;
747}
748
750{
751 mPath = QPainterPath();
752
753 switch ( symbol )
754 {
756
757 mPath.addEllipse( QRectF( -1, -1, 2, 2 ) ); // x,y,w,h
758 return true;
759
761 mPath.moveTo( -1, -1 );
762 mPath.addRoundedRect( -1, -1, 2, 2, 0.25, 0.25 );
763 return true;
764
766 mPath.arcTo( -1, -1, 2, 2, 0, 180 );
767 mPath.lineTo( 0, 0 );
768 return true;
769
771 mPath.arcTo( -1, -1, 2, 2, 90, 120 );
772 mPath.lineTo( 0, 0 );
773 return true;
774
776 mPath.arcTo( -1, -1, 2, 2, 90, 90 );
777 mPath.lineTo( 0, 0 );
778 return true;
779
781 mPath.moveTo( 1, 0 );
782 mPath.arcTo( -1, -1, 2, 2, 0, 180 );
783 return true;
784
786 mPath.moveTo( 0, -1 );
787 mPath.arcTo( -1, -1, 2, 2, 90, 120 );
788 return true;
789
791 mPath.moveTo( 0, -1 );
792 mPath.arcTo( -1, -1, 2, 2, 90, 90 );
793 return true;
794
796 mPath.moveTo( -1, 0 );
797 mPath.lineTo( 1, 0 ); // horizontal
798 mPath.moveTo( 0, -1 );
799 mPath.lineTo( 0, 1 ); // vertical
800 return true;
801
803 mPath.moveTo( -1, -1 );
804 mPath.lineTo( 1, 1 );
805 mPath.moveTo( 1, -1 );
806 mPath.lineTo( -1, 1 );
807 return true;
808
810 mPath.moveTo( 0, -1 );
811 mPath.lineTo( 0, 1 ); // vertical line
812 return true;
813
815 mPath.moveTo( -1, -1 );
816 mPath.lineTo( 0, 0 );
817 mPath.lineTo( -1, 1 );
818 return true;
819
821 mPath.moveTo( 0, 0.75 );
822 mPath.arcTo( 0, -1, 1, 1, -45, 210 );
823 mPath.arcTo( -1, -1, 1, 1, 15, 210 );
824 mPath.lineTo( 0, 0.75 );
825 return true;
826
851 return false;
852 }
853 return false;
854}
855
856double QgsSimpleMarkerSymbolLayerBase::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
857{
858 double scaledSize = mSize;
859
860 hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
861 bool ok = true;
862 if ( hasDataDefinedSize )
863 {
865 scaledSize = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::Size, context.renderContext().expressionContext(), mSize, &ok );
866 }
867
868 if ( hasDataDefinedSize && ok )
869 {
870 switch ( mScaleMethod )
871 {
873 scaledSize = std::sqrt( scaledSize );
874 break;
876 break;
877 }
878 }
879
880 return scaledSize;
881}
882
883void QgsSimpleMarkerSymbolLayerBase::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle ) const
884{
885 //offset
886 double offsetX = 0;
887 double offsetY = 0;
888 markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
889 offset = QPointF( offsetX, offsetY );
890
891 hasDataDefinedRotation = false;
892 //angle
893 bool ok = true;
896 {
899
900 // If the expression evaluation was not successful, fallback to static value
901 if ( !ok )
903
904 hasDataDefinedRotation = true;
905 }
906
907 hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || hasDataDefinedRotation;
908
909 if ( hasDataDefinedRotation )
910 {
911 // For non-point markers, "dataDefinedRotation" means following the
912 // shape (shape-data defined). For them, "field-data defined" does
913 // not work at all. TODO: if "field-data defined" ever gets implemented
914 // we'll need a way to distinguish here between the two, possibly
915 // using another flag in renderHints()
916 const QgsFeature *f = context.feature();
917 if ( f )
918 {
919 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
920 {
921 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
922 angle += m2p.mapRotation();
923 }
924 }
925 }
926
927 if ( angle )
929}
930
931
932//
933// QgsSimpleMarkerSymbolLayer
934//
935
945
947
949{
957
958 if ( props.contains( u"name"_s ) )
959 {
960 shape = decodeShape( props[u"name"_s].toString() );
961 }
962 if ( props.contains( u"color"_s ) )
963 color = QgsColorUtils::colorFromString( props[u"color"_s].toString() );
964 if ( props.contains( u"color_border"_s ) )
965 {
966 //pre 2.5 projects use "color_border"
967 strokeColor = QgsColorUtils::colorFromString( props[u"color_border"_s].toString() );
968 }
969 else if ( props.contains( u"outline_color"_s ) )
970 {
971 strokeColor = QgsColorUtils::colorFromString( props[u"outline_color"_s].toString() );
972 }
973 else if ( props.contains( u"line_color"_s ) )
974 {
975 strokeColor = QgsColorUtils::colorFromString( props[u"line_color"_s].toString() );
976 }
977 if ( props.contains( u"joinstyle"_s ) )
978 {
979 penJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( props[u"joinstyle"_s].toString() );
980 }
981 if ( props.contains( u"size"_s ) )
982 size = props[u"size"_s].toDouble();
983 if ( props.contains( u"angle"_s ) )
984 angle = props[u"angle"_s].toDouble();
985 if ( props.contains( u"scale_method"_s ) )
986 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[u"scale_method"_s].toString() );
987
989 if ( props.contains( u"offset"_s ) )
990 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[u"offset"_s].toString() ) );
991 if ( props.contains( u"offset_unit"_s ) )
992 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[u"offset_unit"_s].toString() ) );
993 if ( props.contains( u"offset_map_unit_scale"_s ) )
994 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"offset_map_unit_scale"_s].toString() ) );
995 if ( props.contains( u"size_unit"_s ) )
996 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[u"size_unit"_s].toString() ) );
997 if ( props.contains( u"size_map_unit_scale"_s ) )
998 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"size_map_unit_scale"_s].toString() ) );
999
1000 if ( props.contains( u"outline_style"_s ) )
1001 {
1002 m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[u"outline_style"_s].toString() ) );
1003 }
1004 else if ( props.contains( u"line_style"_s ) )
1005 {
1006 m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[u"line_style"_s].toString() ) );
1007 }
1008 if ( props.contains( u"outline_width"_s ) )
1009 {
1010 m->setStrokeWidth( props[u"outline_width"_s].toDouble() );
1011 }
1012 else if ( props.contains( u"line_width"_s ) )
1013 {
1014 m->setStrokeWidth( props[u"line_width"_s].toDouble() );
1015 }
1016 if ( props.contains( u"outline_width_unit"_s ) )
1017 {
1018 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[u"outline_width_unit"_s].toString() ) );
1019 }
1020 if ( props.contains( u"line_width_unit"_s ) )
1021 {
1022 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[u"line_width_unit"_s].toString() ) );
1023 }
1024 if ( props.contains( u"outline_width_map_unit_scale"_s ) )
1025 {
1026 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"outline_width_map_unit_scale"_s].toString() ) );
1027 }
1028
1029 if ( props.contains( u"horizontal_anchor_point"_s ) )
1030 {
1031 m->setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( props[u"horizontal_anchor_point"_s].toInt() ) );
1032 }
1033 if ( props.contains( u"vertical_anchor_point"_s ) )
1034 {
1035 m->setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( props[u"vertical_anchor_point"_s].toInt() ) );
1036 }
1037
1038 if ( props.contains( u"cap_style"_s ) )
1039 {
1040 m->setPenCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( props[u"cap_style"_s].toString() ) );
1041 }
1042
1044
1045 return m;
1046}
1047
1048
1050{
1051 return u"SimpleMarker"_s;
1052}
1053
1058
1060{
1062
1063 QColor brushColor = mColor;
1064 QColor penColor = mStrokeColor;
1065
1066 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
1067 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
1068
1069 mBrush = QBrush( brushColor );
1070 mPen = QPen( penColor );
1071 mPen.setStyle( mStrokeStyle );
1072 mPen.setCapStyle( mPenCapStyle );
1073 mPen.setJoinStyle( mPenJoinStyle );
1075
1076 QColor selBrushColor = context.renderContext().selectionColor();
1077 QColor selPenColor = selBrushColor == mColor ? selBrushColor : mStrokeColor;
1078 if ( context.opacity() < 1 && !SELECTION_IS_OPAQUE )
1079 {
1080 selBrushColor.setAlphaF( context.opacity() );
1081 selPenColor.setAlphaF( context.opacity() );
1082 }
1083 mSelBrush = QBrush( selBrushColor );
1084 mSelPen = QPen( selPenColor );
1085 mSelPen.setStyle( mStrokeStyle );
1087
1088 const bool hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Angle );
1089 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
1090
1091 // use caching only when:
1092 // - size, rotation, shape, color, stroke color is not data-defined
1093 // - drawing to screen (not printer)
1094 mUsingCache = !hasDataDefinedRotation
1095 && !hasDataDefinedSize
1096 && !context.forceVectorRendering()
1103
1104 if ( mUsingCache )
1105 mCachedOpacity = context.opacity();
1106
1107 if ( !shapeIsFilled( mShape ) )
1108 {
1109 // some markers can't be drawn as a polygon (circle, cross)
1110 // For these set the selected stroke color to the selected color
1111 mSelPen.setColor( selBrushColor );
1112 }
1113
1114
1115 if ( mUsingCache )
1116 {
1117 if ( !prepareCache( context ) )
1118 {
1119 mUsingCache = false;
1120 }
1121 }
1122 else
1123 {
1124 mCache = QImage();
1125 mSelCache = QImage();
1126 }
1127}
1128
1129
1131{
1132 double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
1133 const double deviceRatio = context.renderContext().devicePixelRatio();
1136 {
1137 // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
1138 // and clamp it to a reasonable range. It's the best we can do in this situation!
1139 scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
1140 }
1141
1142 // take into account angle (which is not data-defined otherwise cache wouldn't be used)
1143 if ( !qgsDoubleNear( mAngle, 0.0 ) )
1144 {
1145 scaledSize = ( std::abs( std::sin( mAngle * M_PI / 180 ) ) + std::abs( std::cos( mAngle * M_PI / 180 ) ) ) * scaledSize;
1146 }
1147 // calculate necessary image size for the cache
1148 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
1149 const int imageSize = ( static_cast< int >( scaledSize ) + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width
1150 const double center = imageSize / 2.0;
1151 if ( imageSize * deviceRatio > MAXIMUM_CACHE_WIDTH )
1152 {
1153 return false;
1154 }
1155
1156 mCache = QImage( QSize( imageSize * deviceRatio, imageSize * deviceRatio ), QImage::Format_ARGB32_Premultiplied );
1157 mCache.setDevicePixelRatio( context.renderContext().devicePixelRatio() );
1158 mCache.setDotsPerMeterX( std::round( context.renderContext().scaleFactor() * 1000 ) );
1159 mCache.setDotsPerMeterY( std::round( context.renderContext().scaleFactor() * 1000 ) );
1160 mCache.fill( 0 );
1161
1162 const bool needsBrush = shapeIsFilled( mShape );
1163
1164 QPainter p;
1165 p.begin( &mCache );
1166 p.setRenderHint( QPainter::Antialiasing );
1167 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1168 p.setPen( mPen );
1169 p.translate( QPointF( center, center ) );
1170 drawMarker( &p, context );
1171 p.end();
1172
1173 // Construct the selected version of the Cache
1174
1175 const QColor selColor = context.renderContext().selectionColor();
1176
1177 mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
1178 mSelCache.fill( 0 );
1179
1180 p.begin( &mSelCache );
1181 p.setRenderHint( QPainter::Antialiasing );
1182 p.setBrush( needsBrush ? mSelBrush : Qt::NoBrush );
1183 p.setPen( mSelPen );
1184 p.translate( QPointF( center, center ) );
1185 drawMarker( &p, context );
1186 p.end();
1187
1188 // Check that the selected version is different. If not, then re-render,
1189 // filling the background with the selection color and using the normal
1190 // colors for the symbol .. could be ugly!
1191
1192 if ( mSelCache == mCache )
1193 {
1194 p.begin( &mSelCache );
1195 p.setRenderHint( QPainter::Antialiasing );
1196 p.fillRect( 0, 0, imageSize, imageSize, selColor );
1197 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1198 p.setPen( mPen );
1199 p.translate( QPointF( center, center ) );
1200 drawMarker( &p, context );
1201 p.end();
1202 }
1203
1204 return true;
1205}
1206
1207void QgsSimpleMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
1208{
1209 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1210 //of the rendered point!
1211
1212 QPainter *p = context.renderContext().painter();
1213 if ( !p )
1214 {
1215 return;
1216 }
1217
1218 QColor brushColor = mColor;
1219 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
1220 mBrush.setColor( brushColor );
1221
1222 QColor penColor = mStrokeColor;
1223 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
1224 mPen.setColor( penColor );
1225
1226 bool ok = true;
1228 {
1231 if ( ok )
1232 {
1233 c.setAlphaF( c.alphaF() * context.opacity() );
1234 mBrush.setColor( c );
1235 }
1236 }
1238 {
1241 if ( ok )
1242 {
1243 c.setAlphaF( c.alphaF() * context.opacity() );
1244 mPen.setColor( c );
1245 mSelPen.setColor( c );
1246 }
1247 }
1249 {
1252 if ( ok )
1253 {
1256 }
1257 }
1259 {
1261 const QString strokeStyle = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::StrokeStyle, context.renderContext().expressionContext(), QString(), &ok );
1262 if ( ok )
1263 {
1266 }
1267 }
1269 {
1271 const QString style = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::JoinStyle, context.renderContext().expressionContext(), QString(), &ok );
1272 if ( ok )
1273 {
1274 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1275 mSelPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1276 }
1277 }
1279 {
1281 const QString style = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::CapStyle, context.renderContext().expressionContext(), QString(), &ok );
1282 if ( ok )
1283 {
1284 mPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1285 mSelPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1286 }
1287 }
1288
1289 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
1290 if ( shapeIsFilled( shape ) )
1291 {
1292 p->setBrush( useSelectedColor ? mSelBrush : mBrush );
1293 }
1294 else
1295 {
1296 p->setBrush( Qt::NoBrush );
1297 }
1298 p->setPen( useSelectedColor ? mSelPen : mPen );
1299
1300 if ( !polygon.isEmpty() )
1301 p->drawPolygon( polygon );
1302 else
1303 p->drawPath( path );
1304}
1305
1307{
1308 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1309 //of the rendered point!
1310
1311 QPainter *p = context.renderContext().painter();
1312 if ( !p )
1313 {
1314 return;
1315 }
1316
1317 if ( mUsingCache && qgsDoubleNear( mCachedOpacity, context.opacity() ) )
1318 {
1319 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
1320 const QImage &img = useSelectedColor ? mSelCache : mCache;
1321 const double s = img.width() / img.devicePixelRatioF();
1322
1323 bool hasDataDefinedSize = false;
1324 const double scaledSize = calculateSize( context, hasDataDefinedSize );
1325
1326 bool hasDataDefinedRotation = false;
1327 QPointF offset;
1328 double angle = 0;
1329 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
1330
1331 p->drawImage( QRectF( point.x() - s / 2.0 + offset.x(), point.y() - s / 2.0 + offset.y(), s, s ), img );
1332 }
1333 else
1334 {
1336 }
1337}
1338
1340{
1341 QVariantMap map;
1342 map[u"name"_s] = encodeShape( mShape );
1343 map[u"color"_s] = QgsColorUtils::colorToString( mColor );
1344 map[u"outline_color"_s] = QgsColorUtils::colorToString( mStrokeColor );
1345 map[u"size"_s] = QString::number( mSize );
1346 map[u"size_unit"_s] = QgsUnitTypes::encodeUnit( mSizeUnit );
1347 map[u"size_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1348 map[u"angle"_s] = QString::number( mAngle );
1349 map[u"offset"_s] = QgsSymbolLayerUtils::encodePoint( mOffset );
1350 map[u"offset_unit"_s] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1351 map[u"offset_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1352 map[u"scale_method"_s] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1353 map[u"outline_style"_s] = QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle );
1354 map[u"outline_width"_s] = QString::number( mStrokeWidth );
1355 map[u"outline_width_unit"_s] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
1356 map[u"outline_width_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
1359 map[u"horizontal_anchor_point"_s] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
1360 map[u"vertical_anchor_point"_s] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
1361 return map;
1362}
1363
1382
1383void QgsSimpleMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1384{
1385 QgsSldExportContext context;
1386 context.setExtraProperties( props );
1387 toSld( doc, element, context );
1388}
1389
1390bool QgsSimpleMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
1391{
1392 return QgsSimpleMarkerSymbolLayerBase::toSld( doc, element, context );
1393}
1394
1395void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1396{
1397 QgsSldExportContext context;
1398 context.setExtraProperties( props );
1399 writeSldMarker( doc, element, context );
1400}
1401
1402bool QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
1403{
1404 // <Graphic>
1405 QDomElement graphicElem = doc.createElement( u"se:Graphic"_s );
1406 element.appendChild( graphicElem );
1407
1408 const QVariantMap props = context.extraProperties();
1410 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
1412
1413 // <Rotation>
1414 QString angleFunc;
1415
1417 {
1418 angleFunc = mDataDefinedProperties.property( QgsSymbolLayer::Property::Angle ).asExpression();
1419 }
1420 else
1421 {
1422 bool ok;
1423 const double angle = props.value( u"angle"_s, u"0"_s ).toDouble( &ok );
1424 if ( !ok )
1425 {
1426 angleFunc = u"%1 + %2"_s.arg( props.value( u"angle"_s, u"0"_s ).toString() ).arg( mAngle );
1427 }
1428 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
1429 {
1430 angleFunc = QString::number( angle + mAngle );
1431 }
1432 }
1433
1434 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc, context );
1435
1436 // <Displacement>
1437 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
1439 return true;
1440}
1441
1442QString QgsSimpleMarkerSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
1443{
1444 Q_UNUSED( mmScaleFactor )
1445 Q_UNUSED( mapUnitScaleFactor )
1446#if 0
1447 QString ogrType = "3"; //default is circle
1448 if ( mName == "square" )
1449 {
1450 ogrType = "5";
1451 }
1452 else if ( mName == "triangle" )
1453 {
1454 ogrType = "7";
1455 }
1456 else if ( mName == "star" )
1457 {
1458 ogrType = "9";
1459 }
1460 else if ( mName == "circle" )
1461 {
1462 ogrType = "3";
1463 }
1464 else if ( mName == "cross" )
1465 {
1466 ogrType = "0";
1467 }
1468 else if ( mName == "x" || mName == "cross2" )
1469 {
1470 ogrType = "1";
1471 }
1472 else if ( mName == "line" )
1473 {
1474 ogrType = "10";
1475 }
1476
1477 QString ogrString;
1478 ogrString.append( "SYMBOL(" );
1479 ogrString.append( "id:" );
1480 ogrString.append( '\"' );
1481 ogrString.append( "ogr-sym-" );
1482 ogrString.append( ogrType );
1483 ogrString.append( '\"' );
1484 ogrString.append( ",c:" );
1485 ogrString.append( mColor.name() );
1486 ogrString.append( ",o:" );
1487 ogrString.append( mStrokeColor.name() );
1488 ogrString.append( QString( ",s:%1mm" ).arg( mSize ) );
1489 ogrString.append( ')' );
1490 return ogrString;
1491#endif //0
1492
1493 QString ogrString;
1494 ogrString.append( "PEN(" );
1495 ogrString.append( "c:" );
1496 ogrString.append( mColor.name() );
1497 ogrString.append( ",w:" );
1498 ogrString.append( QString::number( mSize ) );
1499 ogrString.append( "mm" );
1500 ogrString.append( ")" );
1501 return ogrString;
1502}
1503
1505{
1506 QgsDebugMsgLevel( u"Entered."_s, 4 );
1507
1508 QDomElement graphicElem = element.firstChildElement( u"Graphic"_s );
1509 if ( graphicElem.isNull() )
1510 return nullptr;
1511
1512 QString name = u"square"_s;
1513 QColor color, strokeColor;
1514 double strokeWidth, size;
1515 Qt::PenStyle strokeStyle;
1516
1518 return nullptr;
1519
1520 double angle = 0.0;
1521 QString angleFunc;
1522 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
1523 {
1524 bool ok;
1525 const double d = angleFunc.toDouble( &ok );
1526 if ( ok )
1527 angle = d;
1528 }
1529
1530 QPointF offset;
1532
1533 const Qgis::MarkerShape shape = decodeShape( name );
1534
1535 double scaleFactor = 1.0;
1536 const QString uom = element.attribute( u"uom"_s );
1537 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
1538 size = size * scaleFactor;
1539 offset.setX( offset.x() * scaleFactor );
1540 offset.setY( offset.y() * scaleFactor );
1541
1543 m->setOutputUnit( sldUnitSize );
1544 m->setColor( color );
1546 m->setAngle( angle );
1547 m->setOffset( offset );
1550 return m;
1551}
1552
1554{
1555 Q_UNUSED( context )
1556
1557 if ( mPolygon.count() != 0 )
1558 {
1559 p->drawPolygon( mPolygon );
1560 }
1561 else
1562 {
1563 p->drawPath( mPath );
1564 }
1565}
1566
1567bool QgsSimpleMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
1568{
1569 //data defined size?
1570 double size = mSize;
1571
1572 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
1573
1574 //data defined size
1575 bool ok = true;
1576 if ( hasDataDefinedSize )
1577 {
1579
1580 if ( ok )
1581 {
1582 switch ( mScaleMethod )
1583 {
1585 size = std::sqrt( size );
1586 break;
1588 break;
1589 }
1590 }
1591 }
1592
1594 {
1595 size *= mmMapUnitScaleFactor;
1596 }
1597
1599 {
1601 }
1602 const double halfSize = size / 2.0;
1603
1604 //strokeWidth
1605 double strokeWidth = mStrokeWidth;
1606
1608 {
1611 }
1614 {
1616 }
1617
1618 //color
1619 QColor pc = mPen.color();
1620 QColor bc = mBrush.color();
1622 {
1625 }
1627 {
1630 }
1631
1632 //offset
1633 double offsetX = 0;
1634 double offsetY = 0;
1635 markerOffset( context, offsetX, offsetY );
1636 offsetX *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1637 offsetY *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1638
1639
1640 QPointF off( offsetX, offsetY );
1641
1642 //angle
1643 double angle = mAngle + mLineAngle;
1645 {
1648 }
1649
1652 {
1654 const QString shapeName = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::Name, context.renderContext().expressionContext(), QString(), &ok );
1655 if ( ok )
1656 {
1657 shape = decodeShape( shapeName, &ok );
1658 if ( !ok )
1659 shape = mShape;
1660 }
1661 }
1662
1663 if ( angle )
1664 off = _rotatedOffset( off, angle );
1665
1667
1668 QTransform t;
1669 t.translate( shift.x() + off.x(), shift.y() - off.y() );
1670
1671 if ( !qgsDoubleNear( angle, 0.0 ) )
1672 t.rotate( -angle );
1673
1674 QPolygonF polygon;
1675 if ( shapeToPolygon( shape, polygon ) )
1676 {
1677 t.scale( halfSize, -halfSize );
1678
1679 polygon = t.map( polygon );
1680
1682 p.reserve( polygon.size() );
1683 for ( int i = 0; i < polygon.size(); i++ )
1684 {
1685 p << QgsPoint( polygon[i] );
1686 }
1687
1688 if ( mBrush.style() != Qt::NoBrush )
1689 e.writePolygon( QgsRingSequence() << p, layerName, u"SOLID"_s, bc );
1690 if ( mPen.style() != Qt::NoPen )
1691 e.writePolyline( p, layerName, u"CONTINUOUS"_s, pc, strokeWidth );
1692 }
1693 else if ( shape == Qgis::MarkerShape::Circle )
1694 {
1695 shift += QPointF( off.x(), -off.y() );
1696 if ( mBrush.style() != Qt::NoBrush )
1697 e.writeFilledCircle( layerName, bc, QgsPoint( shift ), halfSize );
1698 if ( mPen.style() != Qt::NoPen )
1699 e.writeCircle( layerName, pc, QgsPoint( shift ), halfSize, u"CONTINUOUS"_s, strokeWidth );
1700 }
1701 else if ( shape == Qgis::MarkerShape::Line )
1702 {
1703 const QPointF pt1 = t.map( QPointF( 0, -halfSize ) );
1704 const QPointF pt2 = t.map( QPointF( 0, halfSize ) );
1705
1706 if ( mPen.style() != Qt::NoPen )
1707 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, u"CONTINUOUS"_s, pc, strokeWidth );
1708 }
1709 else if ( shape == Qgis::MarkerShape::Cross )
1710 {
1711 if ( mPen.style() != Qt::NoPen )
1712 {
1713 const QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
1714 const QPointF pt2 = t.map( QPointF( halfSize, 0 ) );
1715 const QPointF pt3 = t.map( QPointF( 0, -halfSize ) );
1716 const QPointF pt4 = t.map( QPointF( 0, halfSize ) );
1717
1718 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, u"CONTINUOUS"_s, pc, strokeWidth );
1719 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, u"CONTINUOUS"_s, pc, strokeWidth );
1720 }
1721 }
1722 else if ( shape == Qgis::MarkerShape::Cross2 )
1723 {
1724 if ( mPen.style() != Qt::NoPen )
1725 {
1726 const QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
1727 const QPointF pt2 = t.map( QPointF( halfSize, halfSize ) );
1728 const QPointF pt3 = t.map( QPointF( halfSize, -halfSize ) );
1729 const QPointF pt4 = t.map( QPointF( -halfSize, halfSize ) );
1730
1731 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, u"CONTINUOUS"_s, pc, strokeWidth );
1732 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, u"CONTINUOUS"_s, pc, strokeWidth );
1733 }
1734 }
1735 else if ( shape == Qgis::MarkerShape::ArrowHead )
1736 {
1737 if ( mPen.style() != Qt::NoPen )
1738 {
1739 const QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
1740 const QPointF pt2 = t.map( QPointF( 0, 0 ) );
1741 const QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
1742
1743 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, u"CONTINUOUS"_s, pc, strokeWidth );
1744 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt2 ), layerName, u"CONTINUOUS"_s, pc, strokeWidth );
1745 }
1746 }
1747 else
1748 {
1749 QgsDebugError( u"Unsupported dxf marker name %1"_s.arg( encodeShape( shape ) ) );
1750 return false;
1751 }
1752
1753 return true;
1754}
1755
1756
1762
1771
1777
1786
1796
1798{
1799 QRectF symbolBounds = QgsSimpleMarkerSymbolLayerBase::bounds( point, context );
1800
1801 // need to account for stroke width
1802 double penWidth = mStrokeWidth;
1803 bool ok = true;
1805 {
1808 if ( ok )
1809 {
1810 penWidth = strokeWidth;
1811 }
1812 }
1815 {
1817 const QString strokeStyle = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::StrokeStyle, context.renderContext().expressionContext(), QString(), &ok );
1818 if ( ok && strokeStyle == "no"_L1 )
1819 {
1820 penWidth = 0.0;
1821 }
1822 }
1823 else if ( mStrokeStyle == Qt::NoPen )
1824 penWidth = 0;
1825
1826 //antialiasing, add 1 pixel
1827 penWidth += 1;
1828
1829 //extend bounds by pen width / 2.0
1830 symbolBounds.adjust( -penWidth / 2.0, -penWidth / 2.0, penWidth / 2.0, penWidth / 2.0 );
1831
1832 return symbolBounds;
1833}
1834
1836{
1837 if ( shapeIsFilled( mShape ) )
1838 {
1840 }
1841 else
1842 {
1844 }
1845}
1846
1848{
1849 if ( shapeIsFilled( mShape ) )
1850 {
1851 return fillColor();
1852 }
1853 else
1854 {
1855 return strokeColor();
1856 }
1857}
1858
1859
1860//
1861// QgsFilledMarkerSymbolLayer
1862//
1863
1869
1871
1873{
1874 QString name = DEFAULT_SIMPLEMARKER_NAME;
1878
1879 if ( props.contains( u"name"_s ) )
1880 name = props[u"name"_s].toString();
1881 if ( props.contains( u"size"_s ) )
1882 size = props[u"size"_s].toDouble();
1883 if ( props.contains( u"angle"_s ) )
1884 angle = props[u"angle"_s].toDouble();
1885 if ( props.contains( u"scale_method"_s ) )
1886 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[u"scale_method"_s].toString() );
1887
1889 if ( props.contains( u"offset"_s ) )
1890 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[u"offset"_s].toString() ) );
1891 if ( props.contains( u"offset_unit"_s ) )
1892 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[u"offset_unit"_s].toString() ) );
1893 if ( props.contains( u"offset_map_unit_scale"_s ) )
1894 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"offset_map_unit_scale"_s].toString() ) );
1895 if ( props.contains( u"size_unit"_s ) )
1896 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[u"size_unit"_s].toString() ) );
1897 if ( props.contains( u"size_map_unit_scale"_s ) )
1898 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"size_map_unit_scale"_s].toString() ) );
1899 if ( props.contains( u"horizontal_anchor_point"_s ) )
1900 {
1901 m->setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( props[u"horizontal_anchor_point"_s].toInt() ) );
1902 }
1903 if ( props.contains( u"vertical_anchor_point"_s ) )
1904 {
1905 m->setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( props[u"vertical_anchor_point"_s].toInt() ) );
1906 }
1907
1908 m->setSubSymbol( QgsFillSymbol::createSimple( props ).release() );
1909
1911
1912 return m;
1913}
1914
1916{
1917 return u"FilledMarker"_s;
1918}
1919
1921{
1922 if ( mFill )
1923 {
1924 mFill->setRenderHints( mFill->renderHints() | Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol );
1925 mFill->startRender( context.renderContext(), context.fields() );
1926 }
1927
1929}
1930
1932{
1933 if ( mFill )
1934 {
1935 mFill->stopRender( context.renderContext() );
1936 }
1937}
1938
1940{
1941 installMasks( context, true );
1942
1943 // The base class version passes this on to the subsymbol, but we deliberately don't do that here.
1944}
1945
1947{
1948 removeMasks( context, true );
1949
1950 // The base class version passes this on to the subsymbol, but we deliberately don't do that here.
1951}
1952
1954{
1955 QVariantMap map;
1956 map[u"name"_s] = encodeShape( mShape );
1957 map[u"size"_s] = QString::number( mSize );
1958 map[u"size_unit"_s] = QgsUnitTypes::encodeUnit( mSizeUnit );
1959 map[u"size_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1960 map[u"angle"_s] = QString::number( mAngle );
1961 map[u"offset"_s] = QgsSymbolLayerUtils::encodePoint( mOffset );
1962 map[u"offset_unit"_s] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1963 map[u"offset_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1964 map[u"scale_method"_s] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1965 map[u"horizontal_anchor_point"_s] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
1966 map[u"vertical_anchor_point"_s] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
1967
1968 if ( mFill )
1969 {
1970 map[u"color"_s] = QgsColorUtils::colorToString( mFill->color() );
1971 }
1972 return map;
1973}
1974
1982
1984{
1985 return mFill.get();
1986}
1987
1989{
1990 if ( symbol && symbol->type() == Qgis::SymbolType::Fill )
1991 {
1992 mFill.reset( static_cast<QgsFillSymbol *>( symbol ) );
1993 return true;
1994 }
1995 else
1996 {
1997 delete symbol;
1998 return false;
1999 }
2000}
2001
2003{
2004 if ( mFill )
2005 {
2006 return QgsSymbolLayerUtils::estimateMaxSymbolBleed( mFill.get(), context );
2007 }
2008 return 0;
2009}
2010
2012{
2013 QSet<QString> attr = QgsSimpleMarkerSymbolLayerBase::usedAttributes( context );
2014 if ( mFill )
2015 attr.unite( mFill->usedAttributes( context ) );
2016 return attr;
2017}
2018
2020{
2022 return true;
2023 if ( mFill && mFill->hasDataDefinedProperties() )
2024 return true;
2025 return false;
2026}
2027
2029{
2030 mColor = c;
2031 if ( mFill )
2032 mFill->setColor( c );
2033}
2034
2036{
2037 return mFill ? mFill->color() : mColor;
2038}
2039
2048
2050{
2052 if ( mFill )
2053 mFill->setOutputUnit( unit );
2054}
2055
2056void QgsFilledMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
2057{
2058 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
2059 //of the rendered point!
2060
2061 QPainter *p = context.renderContext().painter();
2062 if ( !p )
2063 {
2064 return;
2065 }
2066
2067 const double prevOpacity = mFill->opacity();
2068 mFill->setOpacity( mFill->opacity() * context.opacity() );
2069
2070 if ( shapeIsFilled( shape ) )
2071 {
2072 p->setBrush( Qt::red );
2073 }
2074 else
2075 {
2076 p->setBrush( Qt::NoBrush );
2077 }
2078 p->setPen( Qt::black );
2079
2080 const bool prevIsSubsymbol = context.renderContext().flags() & Qgis::RenderContextFlag::RenderingSubSymbol;
2082
2083 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
2084 if ( !polygon.isEmpty() )
2085 {
2086 mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
2087 }
2088 else
2089 {
2090 const QPolygonF poly = path.toFillPolygon();
2091 mFill->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
2092 }
2093
2095
2096 mFill->setOpacity( prevOpacity );
2097}
2098
2099
2101
2102
2104{
2105 mSize = size;
2106 mAngle = angle;
2107 mOffset = QPointF( 0, 0 );
2109 mStrokeWidth = 0.2;
2111 mColor = QColor( 35, 35, 35 );
2112 mStrokeColor = QColor( 35, 35, 35 );
2113 setPath( path );
2114}
2115
2128
2130
2132{
2133 QString name;
2137
2138 if ( props.contains( u"name"_s ) )
2139 name = props[u"name"_s].toString();
2140 if ( props.contains( u"size"_s ) )
2141 size = props[u"size"_s].toDouble();
2142 if ( props.contains( u"angle"_s ) )
2143 angle = props[u"angle"_s].toDouble();
2144 if ( props.contains( u"scale_method"_s ) )
2145 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[u"scale_method"_s].toString() );
2146
2148
2149 if ( props.contains( u"size_unit"_s ) )
2150 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[u"size_unit"_s].toString() ) );
2151 if ( props.contains( u"size_map_unit_scale"_s ) )
2152 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"size_map_unit_scale"_s].toString() ) );
2153 if ( props.contains( u"fixedAspectRatio"_s ) )
2154 m->setFixedAspectRatio( props[u"fixedAspectRatio"_s].toDouble() );
2155 if ( props.contains( u"offset"_s ) )
2156 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[u"offset"_s].toString() ) );
2157 if ( props.contains( u"offset_unit"_s ) )
2158 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[u"offset_unit"_s].toString() ) );
2159 if ( props.contains( u"offset_map_unit_scale"_s ) )
2160 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"offset_map_unit_scale"_s].toString() ) );
2161 if ( props.contains( u"fill"_s ) )
2162 {
2163 //pre 2.5 projects used "fill"
2164 m->setFillColor( QgsColorUtils::colorFromString( props[u"fill"_s].toString() ) );
2165 }
2166 else if ( props.contains( u"color"_s ) )
2167 {
2168 m->setFillColor( QgsColorUtils::colorFromString( props[u"color"_s].toString() ) );
2169 }
2170 if ( props.contains( u"outline"_s ) )
2171 {
2172 //pre 2.5 projects used "outline"
2173 m->setStrokeColor( QgsColorUtils::colorFromString( props[u"outline"_s].toString() ) );
2174 }
2175 else if ( props.contains( u"outline_color"_s ) )
2176 {
2177 m->setStrokeColor( QgsColorUtils::colorFromString( props[u"outline_color"_s].toString() ) );
2178 }
2179 else if ( props.contains( u"line_color"_s ) )
2180 {
2181 m->setStrokeColor( QgsColorUtils::colorFromString( props[u"line_color"_s].toString() ) );
2182 }
2183
2184 if ( props.contains( u"outline-width"_s ) )
2185 {
2186 //pre 2.5 projects used "outline-width"
2187 m->setStrokeWidth( props[u"outline-width"_s].toDouble() );
2188 }
2189 else if ( props.contains( u"outline_width"_s ) )
2190 {
2191 m->setStrokeWidth( props[u"outline_width"_s].toDouble() );
2192 }
2193 else if ( props.contains( u"line_width"_s ) )
2194 {
2195 m->setStrokeWidth( props[u"line_width"_s].toDouble() );
2196 }
2197
2198 if ( props.contains( u"outline_width_unit"_s ) )
2199 {
2200 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[u"outline_width_unit"_s].toString() ) );
2201 }
2202 else if ( props.contains( u"line_width_unit"_s ) )
2203 {
2204 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[u"line_width_unit"_s].toString() ) );
2205 }
2206 if ( props.contains( u"outline_width_map_unit_scale"_s ) )
2207 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"outline_width_map_unit_scale"_s].toString() ) );
2208
2209 if ( props.contains( u"horizontal_anchor_point"_s ) )
2210 {
2211 m->setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( props[u"horizontal_anchor_point"_s].toInt() ) );
2212 }
2213 if ( props.contains( u"vertical_anchor_point"_s ) )
2214 {
2215 m->setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( props[u"vertical_anchor_point"_s].toInt() ) );
2216 }
2217
2219
2221
2222 if ( props.contains( u"parameters"_s ) )
2223 {
2224 const QVariantMap parameters = props[u"parameters"_s].toMap();
2226 }
2227
2228 return m;
2229}
2230
2231void QgsSvgMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
2232{
2233 const QVariantMap::iterator it = properties.find( u"name"_s );
2234 if ( it != properties.end() )
2235 {
2236 if ( saving )
2237 {
2238 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
2239 }
2240 else
2241 {
2242 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
2243 }
2244 }
2245}
2246
2248{
2250 mHasFillParam = false;
2251 mPath = path;
2252 QColor defaultFillColor, defaultStrokeColor;
2253 double strokeWidth, fillOpacity, strokeOpacity;
2254 bool hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
2255 bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
2257 path,
2259 hasDefaultFillColor,
2260 defaultFillColor,
2261 hasFillOpacityParam,
2262 hasDefaultFillOpacity,
2263 fillOpacity,
2264 hasStrokeParam,
2265 hasDefaultStrokeColor,
2266 defaultStrokeColor,
2267 hasStrokeWidthParam,
2268 hasDefaultStrokeWidth,
2270 hasStrokeOpacityParam,
2271 hasDefaultStrokeOpacity,
2272 strokeOpacity
2273 );
2274
2275 const double newFillOpacity = hasFillOpacityParam ? fillColor().alphaF() : 1.0;
2276 const double newStrokeOpacity = hasStrokeOpacityParam ? strokeColor().alphaF() : 1.0;
2277
2278 if ( hasDefaultFillColor )
2279 {
2280 defaultFillColor.setAlphaF( newFillOpacity );
2281 setFillColor( defaultFillColor );
2282 }
2283 if ( hasDefaultFillOpacity )
2284 {
2285 QColor c = fillColor();
2286 c.setAlphaF( fillOpacity );
2287 setFillColor( c );
2288 }
2289 if ( hasDefaultStrokeColor )
2290 {
2291 defaultStrokeColor.setAlphaF( newStrokeOpacity );
2292 setStrokeColor( defaultStrokeColor );
2293 }
2294 if ( hasDefaultStrokeWidth )
2295 {
2297 }
2298 if ( hasDefaultStrokeOpacity )
2299 {
2300 QColor c = strokeColor();
2301 c.setAlphaF( strokeOpacity );
2302 setStrokeColor( c );
2303 }
2304
2306}
2307
2309{
2310 if ( mDefaultAspectRatio == 0.0 )
2311 {
2312 //size
2313 const double size = mSize;
2314 //assume 88 dpi as standard value
2315 const double widthScaleFactor = 3.465;
2316 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( mPath, size, mColor, mStrokeColor, mStrokeWidth, widthScaleFactor );
2317 // set default aspect ratio
2318 mDefaultAspectRatio = svgViewbox.isValid() ? svgViewbox.height() / svgViewbox.width() : 0.0;
2319 }
2320 return mDefaultAspectRatio;
2321}
2322
2324{
2325 const bool aPreservedAspectRatio = preservedAspectRatio();
2326 if ( aPreservedAspectRatio && !par )
2327 {
2329 }
2330 else if ( !aPreservedAspectRatio && par )
2331 {
2332 mFixedAspectRatio = 0.0;
2333 }
2334 return preservedAspectRatio();
2335}
2336
2337void QgsSvgMarkerSymbolLayer::setParameters( const QMap<QString, QgsProperty> &parameters )
2338{
2340}
2341
2342
2344{
2345 return u"SvgMarker"_s;
2346}
2347
2352
2354{
2355 QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
2356 Q_UNUSED( context )
2357}
2358
2360{
2361 Q_UNUSED( context )
2362}
2363
2365{
2366 QPainter *p = context.renderContext().painter();
2367 if ( !p )
2368 return;
2369
2370 bool hasDataDefinedSize = false;
2371 const double scaledWidth = calculateSize( context, hasDataDefinedSize );
2372 const double devicePixelRatio = context.renderContext().devicePixelRatio();
2373 const double width = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2374
2375 //don't render symbols with a width below one or above 10,000 pixels
2376 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2377 {
2378 return;
2379 }
2380
2381 const QgsScopedQPainterState painterState( p );
2382
2383 bool hasDataDefinedAspectRatio = false;
2384 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2385 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2386
2388
2389 double strokeWidth = mStrokeWidth;
2391 {
2394 }
2396
2397 QColor fillColor = mColor;
2398 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
2399 if ( useSelectedColor && mHasFillParam )
2400 {
2402 }
2404 {
2407 }
2408
2409 QColor strokeColor = mStrokeColor;
2411 {
2414 }
2415
2416 QString path = mPath;
2418 {
2422 if ( path != mPath && qgsDoubleNear( aspectRatio, 0.0 ) && !mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Height ) )
2423 {
2424 // adjust height of data defined path
2425 const QSizeF svgViewbox
2427 ->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth, context.renderContext().scaleFactor(), aspectRatio, ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2428 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2429 }
2430 }
2431
2432 QPointF outputOffset;
2433 double angle = 0.0;
2434 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2435
2436 p->translate( point + outputOffset );
2437
2438 const bool rotated = !qgsDoubleNear( angle, 0 );
2439 if ( rotated )
2440 p->rotate( angle );
2441
2442 bool fitsInCache = true;
2443 bool usePict = true;
2444 const bool rasterizeSelected = !mHasFillParam || mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Name );
2445 if ( ( !context.forceVectorRendering() && !rotated ) || ( useSelectedColor && rasterizeSelected ) )
2446 {
2447 QImage img
2449 ->svgAsImage( path, width * devicePixelRatio, fillColor, strokeColor, strokeWidth, context.renderContext().scaleFactor(), fitsInCache, aspectRatio, ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2450 if ( fitsInCache && img.width() > 1 )
2451 {
2452 usePict = false;
2453
2454 if ( useSelectedColor )
2455 {
2457 }
2458
2459 //consider transparency
2460 if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2461 {
2462 QImage transparentImage = img.copy();
2463 QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2464 if ( devicePixelRatio == 1 )
2465 {
2466 p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
2467 }
2468 else
2469 {
2470 p->drawImage( QRectF( -transparentImage.width() / 2.0 / devicePixelRatio, -transparentImage.height() / 2.0 / devicePixelRatio, transparentImage.width() / devicePixelRatio, transparentImage.height() / devicePixelRatio ), transparentImage );
2471 }
2472 }
2473 else
2474 {
2475 if ( devicePixelRatio == 1 )
2476 {
2477 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2478 }
2479 else
2480 {
2481 p->drawImage( QRectF( -img.width() / 2.0 / devicePixelRatio, -img.height() / 2.0 / devicePixelRatio, img.width() / devicePixelRatio, img.height() / devicePixelRatio ), img );
2482 }
2483 }
2484 }
2485 }
2486
2487 if ( usePict || !fitsInCache )
2488 {
2489 p->setOpacity( context.opacity() );
2490 const QPicture pct
2492 ->svgAsPicture( path, width, fillColor, strokeColor, strokeWidth, context.renderContext().scaleFactor(), context.forceVectorRendering(), aspectRatio, ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2493 if ( pct.width() > 1 )
2494 {
2495 QgsPainting::drawPicture( p, QPointF( 0, 0 ), pct );
2496 }
2497 }
2498
2499 // workaround issue with nested QPictures forgetting antialiasing flag - see https://github.com/qgis/QGIS/issues/22909
2501}
2502
2503double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2504{
2505 double scaledSize = mSize;
2507
2508 bool ok = true;
2509 if ( hasDataDefinedSize )
2510 {
2513 }
2514 else
2515 {
2516 hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Width );
2517 if ( hasDataDefinedSize )
2518 {
2520 scaledSize = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::Width, context.renderContext().expressionContext(), mSize, &ok );
2521 }
2522 }
2523
2524 if ( hasDataDefinedSize && ok )
2525 {
2526 switch ( mScaleMethod )
2527 {
2529 scaledSize = std::sqrt( scaledSize );
2530 break;
2532 break;
2533 }
2534 }
2535
2536 return scaledSize;
2537}
2538
2539double QgsSvgMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2540{
2542 if ( !hasDataDefinedAspectRatio )
2543 return mFixedAspectRatio;
2544
2546 return 0.0;
2547
2548 double scaledAspectRatio = mDefaultAspectRatio;
2549 if ( mFixedAspectRatio > 0.0 )
2550 scaledAspectRatio = mFixedAspectRatio;
2551
2552 const double defaultHeight = mSize * scaledAspectRatio;
2553 scaledAspectRatio = defaultHeight / scaledSize;
2554
2555 bool ok = true;
2556 double scaledHeight = scaledSize * scaledAspectRatio;
2558 {
2559 context.setOriginalValueVariable( defaultHeight );
2560 scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::Height, context.renderContext().expressionContext(), defaultHeight, &ok );
2561 }
2562
2563 if ( hasDataDefinedAspectRatio && ok )
2564 {
2565 switch ( mScaleMethod )
2566 {
2568 scaledHeight = sqrt( scaledHeight );
2569 break;
2571 break;
2572 }
2573 }
2574
2575 scaledAspectRatio = scaledHeight / scaledSize;
2576
2577 return scaledAspectRatio;
2578}
2579
2580void QgsSvgMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
2581{
2582 //offset
2583 double offsetX = 0;
2584 double offsetY = 0;
2585 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
2586 offset = QPointF( offsetX, offsetY );
2587
2590 {
2593 }
2594
2595 const bool hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Angle );
2596 if ( hasDataDefinedRotation )
2597 {
2598 // For non-point markers, "dataDefinedRotation" means following the
2599 // shape (shape-data defined). For them, "field-data defined" does
2600 // not work at all. TODO: if "field-data defined" ever gets implemented
2601 // we'll need a way to distinguish here between the two, possibly
2602 // using another flag in renderHints()
2603 const QgsFeature *f = context.feature();
2604 if ( f )
2605 {
2606 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
2607 {
2608 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2609 angle += m2p.mapRotation();
2610 }
2611 }
2612 }
2613
2614 if ( angle )
2616}
2617
2618
2620{
2621 QVariantMap map;
2622 map[u"name"_s] = mPath;
2623 map[u"size"_s] = QString::number( mSize );
2624 map[u"size_unit"_s] = QgsUnitTypes::encodeUnit( mSizeUnit );
2625 map[u"size_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2626 map[u"fixedAspectRatio"_s] = QString::number( mFixedAspectRatio );
2627 map[u"angle"_s] = QString::number( mAngle );
2628 map[u"offset"_s] = QgsSymbolLayerUtils::encodePoint( mOffset );
2629 map[u"offset_unit"_s] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2630 map[u"offset_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2631 map[u"scale_method"_s] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2632 map[u"color"_s] = QgsColorUtils::colorToString( mColor );
2633 map[u"outline_color"_s] = QgsColorUtils::colorToString( mStrokeColor );
2634 map[u"outline_width"_s] = QString::number( mStrokeWidth );
2635 map[u"outline_width_unit"_s] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2636 map[u"outline_width_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2637 map[u"horizontal_anchor_point"_s] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
2638 map[u"vertical_anchor_point"_s] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
2639
2640 map[u"parameters"_s] = QgsProperty::propertyMapToVariantMap( mParameters );
2641
2642 return map;
2643}
2644
2654
2659
2660void QgsSvgMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
2661{
2662 QgsSldExportContext context;
2663 context.setExtraProperties( props );
2664 toSld( doc, element, context );
2665}
2666
2667bool QgsSvgMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
2668{
2669 return QgsMarkerSymbolLayer::toSld( doc, element, context );
2670}
2671
2677
2679{
2681 if ( unit != mStrokeWidthUnit )
2682 {
2684 }
2685 return unit;
2686}
2687
2693
2702
2703void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
2704{
2705 QgsSldExportContext context;
2706 context.setExtraProperties( props );
2707 writeSldMarker( doc, element, context );
2708}
2709
2710bool QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
2711{
2712 // <Graphic>
2713 QDomElement graphicElem = doc.createElement( u"se:Graphic"_s );
2714 element.appendChild( graphicElem );
2715
2716 const QVariantMap props = context.extraProperties();
2717 // encode a parametric SVG reference
2718 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
2721
2722 // <Rotation>
2723 QString angleFunc;
2724 bool ok;
2725 const double angle = props.value( u"angle"_s, u"0"_s ).toDouble( &ok );
2726 if ( !ok )
2727 {
2728 angleFunc = u"%1 + %2"_s.arg( props.value( u"angle"_s, u"0"_s ).toString() ).arg( mAngle );
2729 }
2730 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2731 {
2732 angleFunc = QString::number( angle + mAngle );
2733 }
2734
2735 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc, context );
2736
2737 // <Displacement>
2738 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2740 return true;
2741}
2742
2744{
2745 QgsDebugMsgLevel( u"Entered."_s, 4 );
2746
2747 QDomElement graphicElem = element.firstChildElement( u"Graphic"_s );
2748 if ( graphicElem.isNull() )
2749 return nullptr;
2750
2751 QString path, mimeType;
2752 // Unused and to be DEPRECATED in externalGraphicFromSld
2753 QColor fillColor_;
2754 double size;
2755
2756 if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor_, size ) )
2757 return nullptr;
2758
2759 double scaleFactor = 1.0;
2760 const QString uom = element.attribute( u"uom"_s );
2761 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
2762 size = size * scaleFactor;
2763
2764 if ( mimeType != "image/svg+xml"_L1 )
2765 return nullptr;
2766
2767 double angle = 0.0;
2768 QString angleFunc;
2769 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2770 {
2771 bool ok;
2772 const double d = angleFunc.toDouble( &ok );
2773 if ( ok )
2774 angle = d;
2775 }
2776
2777 QPointF offset;
2779
2780 // Extract parameters from URL
2781 QString realPath { path };
2782 QUrl svgUrl { path };
2783
2784 // Because color definition can start with '#', the url parsing won't recognize the query string entirely
2785 QUrlQuery queryString;
2786
2787 if ( svgUrl.hasQuery() && svgUrl.hasFragment() )
2788 {
2789 const QString queryPart { path.mid( path.indexOf( '?' ) + 1 ) };
2790 queryString.setQuery( queryPart );
2791 }
2792
2793 // Remove query for simple file paths
2794 if ( svgUrl.scheme().isEmpty() || svgUrl.isLocalFile() )
2795 {
2796 svgUrl.setQuery( QString() );
2797 realPath = svgUrl.path();
2798 }
2799
2801
2802 QMap<QString, QgsProperty> params;
2803
2804 bool ok;
2805
2806 if ( queryString.hasQueryItem( u"fill"_s ) )
2807 {
2808 const QColor fillColor { queryString.queryItemValue( u"fill"_s ) };
2809 m->setFillColor( fillColor );
2810 }
2811
2812 if ( queryString.hasQueryItem( u"fill-opacity"_s ) )
2813 {
2814 const double alpha { queryString.queryItemValue( u"fill-opacity"_s ).toDouble( &ok ) };
2815 if ( ok )
2816 {
2817 params.insert( u"fill-opacity"_s, QgsProperty::fromValue( alpha ) );
2818 }
2819 }
2820
2821 if ( queryString.hasQueryItem( u"outline"_s ) )
2822 {
2823 const QColor strokeColor { queryString.queryItemValue( u"outline"_s ) };
2825 }
2826
2827 if ( queryString.hasQueryItem( u"outline-opacity"_s ) )
2828 {
2829 const double alpha { queryString.queryItemValue( u"outline-opacity"_s ).toDouble( &ok ) };
2830 if ( ok )
2831 {
2832 params.insert( u"outline-opacity"_s, QgsProperty::fromValue( alpha ) );
2833 }
2834 }
2835
2836 if ( queryString.hasQueryItem( u"outline-width"_s ) )
2837 {
2838 const int width { queryString.queryItemValue( u"outline-width"_s ).toInt( &ok ) };
2839 if ( ok )
2840 {
2841 m->setStrokeWidth( width );
2842 }
2843 }
2844
2845 if ( !params.isEmpty() )
2846 {
2847 m->setParameters( params );
2848 }
2849
2850 m->setOutputUnit( sldUnitSize );
2851 m->setAngle( angle );
2852 m->setOffset( offset );
2853 return m;
2854}
2855
2856bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
2857{
2858 //size
2859 double size = mSize;
2860
2861 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
2862
2863 bool ok = true;
2864 if ( hasDataDefinedSize )
2865 {
2868 }
2869
2870 if ( hasDataDefinedSize && ok )
2871 {
2872 switch ( mScaleMethod )
2873 {
2875 size = std::sqrt( size );
2876 break;
2878 break;
2879 }
2880 }
2881
2883 {
2884 size *= mmMapUnitScaleFactor;
2885 }
2886
2887 //offset, angle
2888 QPointF offset = mOffset;
2889
2891 {
2893 const QVariant val = mDataDefinedProperties.value( QgsSymbolLayer::Property::Offset, context.renderContext().expressionContext(), QString() );
2894 const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
2895 if ( ok )
2896 offset = res;
2897 }
2898 const double offsetX = offset.x();
2899 const double offsetY = offset.y();
2900
2901 QPointF outputOffset( offsetX, offsetY );
2902
2903 double angle = mAngle + mLineAngle;
2905 {
2908 }
2909
2910 if ( angle )
2911 outputOffset = _rotatedOffset( outputOffset, angle );
2912
2914
2915 QString path = mPath;
2917 {
2921 }
2922
2923 double strokeWidth = mStrokeWidth;
2925 {
2928 }
2930
2931 QColor fillColor = mColor;
2933 {
2936 }
2937
2938 QColor strokeColor = mStrokeColor;
2940 {
2943 }
2944
2946
2947 const QByteArray &svgContent
2950
2951 QSvgRenderer r( svgContent );
2952 if ( !r.isValid() )
2953 return false;
2954
2955 QgsDxfPaintDevice pd( &e );
2956 pd.setDrawingSize( QSizeF( r.defaultSize() ) );
2957
2958 QSizeF outSize( r.defaultSize() );
2959 outSize.scale( size, size, Qt::KeepAspectRatio );
2960
2961 QPainter p;
2962 p.begin( &pd );
2963 if ( !qgsDoubleNear( angle, 0.0 ) )
2964 {
2965 p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
2966 p.rotate( angle );
2967 p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
2968 }
2969 pd.setShift( shift + QPointF( outputOffset.x(), -outputOffset.y() ) );
2970 pd.setOutputSize( QRectF( -outSize.width() / 2.0, -outSize.height() / 2.0, outSize.width(), outSize.height() ) );
2971 pd.setLayer( layerName );
2972 r.render( &p );
2973 p.end();
2974 return true;
2975}
2976
2978{
2979 bool hasDataDefinedSize = false;
2980 double scaledWidth = calculateSize( context, hasDataDefinedSize );
2981
2982 bool hasDataDefinedAspectRatio = false;
2983 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2984 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2985
2986 scaledWidth = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2987 scaledHeight = context.renderContext().convertToPainterUnits( scaledHeight, mSizeUnit, mSizeMapUnitScale );
2988
2989 //don't render symbols with size below one or above 10,000 pixels
2990 if ( static_cast< int >( scaledWidth ) < 1 || 10000.0 < scaledWidth )
2991 {
2992 return QRectF();
2993 }
2994
2995 QPointF outputOffset;
2996 double angle = 0.0;
2997 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2998
2999 double strokeWidth = mStrokeWidth;
3001 {
3004 }
3006
3007 QString path = mPath;
3009 {
3013 if ( path != mPath && qgsDoubleNear( aspectRatio, 0.0 ) && !mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Height ) )
3014 {
3015 // need to get colors to take advantage of cached SVGs
3016 QColor fillColor = mColor;
3018 {
3021 }
3022
3023 const QColor strokeColor = mStrokeColor;
3025 {
3028 }
3029
3031
3032 // adjust height of data defined path
3033 const QSizeF svgViewbox
3035 ->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth, context.renderContext().scaleFactor(), aspectRatio, ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
3036 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
3037 }
3038 }
3039
3040 QTransform transform;
3041 // move to the desired position
3042 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3043
3044 if ( !qgsDoubleNear( angle, 0.0 ) )
3045 transform.rotate( angle );
3046
3047 //antialiasing
3048 strokeWidth += 1.0 / 2.0;
3049
3050 QRectF symbolBounds = transform.mapRect( QRectF( -scaledWidth / 2.0, -scaledHeight / 2.0, scaledWidth, scaledHeight ) );
3051
3052 //extend bounds by pen width / 2.0
3053 symbolBounds.adjust( -strokeWidth / 2.0, -strokeWidth / 2.0, strokeWidth / 2.0, strokeWidth / 2.0 );
3054
3055 return symbolBounds;
3056}
3057
3059
3061 : mPath( path )
3062{
3063 mSize = size;
3064 mAngle = angle;
3065 mOffset = QPointF( 0, 0 );
3068}
3069
3071
3073{
3074 QString path;
3078
3079 if ( props.contains( u"imageFile"_s ) )
3080 path = props[u"imageFile"_s].toString();
3081 if ( props.contains( u"size"_s ) )
3082 size = props[u"size"_s].toDouble();
3083 if ( props.contains( u"angle"_s ) )
3084 angle = props[u"angle"_s].toDouble();
3085 if ( props.contains( u"scale_method"_s ) )
3086 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[u"scale_method"_s].toString() );
3087
3088 auto m = std::make_unique< QgsRasterMarkerSymbolLayer >( path, size, angle, scaleMethod );
3089 m->setCommonProperties( props );
3090 return m.release();
3091}
3092
3094{
3095 const QDomElement graphicElem = element.firstChildElement( u"Graphic"_s );
3096 if ( graphicElem.isNull() )
3097 return nullptr;
3098
3099 const QDomElement externalGraphicElem = graphicElem.firstChildElement( u"ExternalGraphic"_s );
3100 if ( externalGraphicElem.isNull() )
3101 return nullptr;
3102
3103 const QDomElement onlineResourceElem = externalGraphicElem.firstChildElement( u"OnlineResource"_s );
3104 const QDomElement inlineContentElem = externalGraphicElem.firstChildElement( u"InlineContent"_s );
3105
3106 QString url;
3107 if ( !onlineResourceElem.isNull() )
3108 {
3109 url = onlineResourceElem.attribute( u"href"_s );
3110 // no further processing to do, both base64 data urls and direct file/http urls are compatible with raster markers already
3111 }
3112 else if ( !inlineContentElem.isNull() && inlineContentElem.attribute( u"encoding"_s ) == "base64"_L1 )
3113 {
3114 url = u"base64:"_s + inlineContentElem.text();
3115 }
3116 else
3117 {
3118 return nullptr;
3119 }
3120
3122 // TODO: parse other attributes from the SLD spec (Opacity, Size, Rotation, AnchorPoint, Displacement)
3123 return m;
3124}
3125
3127{
3128 if ( properties.contains( u"alpha"_s ) )
3129 {
3130 setOpacity( properties[u"alpha"_s].toDouble() );
3131 }
3132
3133 if ( properties.contains( u"size_unit"_s ) )
3134 setSizeUnit( QgsUnitTypes::decodeRenderUnit( properties[u"size_unit"_s].toString() ) );
3135 if ( properties.contains( u"size_map_unit_scale"_s ) )
3136 setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[u"size_map_unit_scale"_s].toString() ) );
3137 if ( properties.contains( u"fixedAspectRatio"_s ) )
3138 setFixedAspectRatio( properties[u"fixedAspectRatio"_s].toDouble() );
3139
3140 if ( properties.contains( u"offset"_s ) )
3141 setOffset( QgsSymbolLayerUtils::decodePoint( properties[u"offset"_s].toString() ) );
3142 if ( properties.contains( u"offset_unit"_s ) )
3143 setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[u"offset_unit"_s].toString() ) );
3144 if ( properties.contains( u"offset_map_unit_scale"_s ) )
3145 setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[u"offset_map_unit_scale"_s].toString() ) );
3146
3147 if ( properties.contains( u"horizontal_anchor_point"_s ) )
3148 {
3149 setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( properties[u"horizontal_anchor_point"_s].toInt() ) );
3150 }
3151 if ( properties.contains( u"vertical_anchor_point"_s ) )
3152 {
3153 setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( properties[u"vertical_anchor_point"_s].toInt() ) );
3154 }
3155
3158}
3159
3160void QgsRasterMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
3161{
3162 const QVariantMap::iterator it = properties.find( u"name"_s );
3163 if ( it != properties.end() && it.value().userType() == QMetaType::Type::QString )
3164 {
3165 if ( saving )
3166 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
3167 else
3168 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
3169 }
3170}
3171
3173{
3174 mPath = path;
3176}
3177
3179{
3180 const bool aPreservedAspectRatio = preservedAspectRatio();
3181 if ( aPreservedAspectRatio && !par )
3182 {
3184 }
3185 else if ( !aPreservedAspectRatio && par )
3186 {
3187 mFixedAspectRatio = 0.0;
3188 }
3189 return preservedAspectRatio();
3190}
3191
3193{
3194 if ( mDefaultAspectRatio == 0.0 && !mPath.isEmpty() )
3195 {
3197 mDefaultAspectRatio = ( !size.isNull() && size.isValid() && size.width() > 0 ) ? static_cast< double >( size.height() ) / static_cast< double >( size.width() ) : 0.0;
3198 }
3199 return mDefaultAspectRatio;
3200}
3201
3203{
3204 return u"RasterMarker"_s;
3205}
3206
3211
3213{
3214 QPainter *p = context.renderContext().painter();
3215 if ( !p )
3216 return;
3217
3218 QString path = mPath;
3220 {
3223 }
3224
3225 if ( path.isEmpty() )
3226 return;
3227
3228 double width = 0.0;
3229 double height = 0.0;
3230
3231 bool hasDataDefinedSize = false;
3232 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3233
3234 bool hasDataDefinedAspectRatio = false;
3235 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3236
3237 QPointF outputOffset;
3238 double angle = 0.0;
3239
3240 // RenderPercentage Unit Type takes original image size
3242 {
3244 if ( size.isEmpty() )
3245 return;
3246
3247 width = ( scaledSize * static_cast< double >( size.width() ) ) / 100.0;
3248 height = ( scaledSize * static_cast< double >( size.height() ) ) / 100.0;
3249
3250 // don't render symbols with size below one or above 10,000 pixels
3251 if ( static_cast< int >( width ) < 1 || 10000.0 < width || static_cast< int >( height ) < 1 || 10000.0 < height )
3252 return;
3253
3254 calculateOffsetAndRotation( context, width, height, outputOffset, angle );
3255 }
3256 else
3257 {
3258 width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3259 height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3260
3261 if ( preservedAspectRatio() && path != mPath )
3262 {
3264 if ( !size.isNull() && size.isValid() && size.width() > 0 )
3265 {
3266 height = width * ( static_cast< double >( size.height() ) / static_cast< double >( size.width() ) );
3267 }
3268 }
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 )
3272 return;
3273
3274 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3275 }
3276
3277 const QgsScopedQPainterState painterState( p );
3278 p->translate( point + outputOffset );
3279
3280 const bool rotated = !qgsDoubleNear( angle, 0 );
3281 if ( rotated )
3282 p->rotate( angle );
3283
3284 double opacity = mOpacity;
3286 {
3289 }
3290 opacity *= context.opacity();
3291
3292 QImage img = fetchImage( context.renderContext(), path, QSize( width, preservedAspectRatio() ? 0 : width * aspectRatio ), preservedAspectRatio(), opacity );
3293 if ( !img.isNull() )
3294 {
3295 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
3296 if ( useSelectedColor )
3297 {
3299 }
3300
3301 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
3302 }
3303}
3304
3305QImage QgsRasterMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
3306{
3307 bool cached = false;
3308 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking );
3309}
3310
3311double QgsRasterMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
3312{
3313 double scaledSize = mSize;
3315
3316 bool ok = true;
3317 if ( hasDataDefinedSize )
3318 {
3321 }
3322 else
3323 {
3324 hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Width );
3325 if ( hasDataDefinedSize )
3326 {
3328 scaledSize = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::Width, context.renderContext().expressionContext(), mSize, &ok );
3329 }
3330 }
3331
3332 if ( hasDataDefinedSize && ok )
3333 {
3334 switch ( mScaleMethod )
3335 {
3337 scaledSize = std::sqrt( scaledSize );
3338 break;
3340 break;
3341 }
3342 }
3343
3344 return scaledSize;
3345}
3346
3347double QgsRasterMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
3348{
3350 if ( !hasDataDefinedAspectRatio )
3351 return mFixedAspectRatio;
3352
3354 return 0.0;
3355
3356 double scaledAspectRatio = mDefaultAspectRatio;
3357 if ( mFixedAspectRatio > 0.0 )
3358 scaledAspectRatio = mFixedAspectRatio;
3359
3360 const double defaultHeight = mSize * scaledAspectRatio;
3361 scaledAspectRatio = defaultHeight / scaledSize;
3362
3363 bool ok = true;
3364 double scaledHeight = scaledSize * scaledAspectRatio;
3366 {
3367 context.setOriginalValueVariable( defaultHeight );
3368 scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::Height, context.renderContext().expressionContext(), defaultHeight, &ok );
3369 }
3370
3371 if ( hasDataDefinedAspectRatio && ok )
3372 {
3373 switch ( mScaleMethod )
3374 {
3376 scaledHeight = sqrt( scaledHeight );
3377 break;
3379 break;
3380 }
3381 }
3382
3383 scaledAspectRatio = scaledHeight / scaledSize;
3384
3385 return scaledAspectRatio;
3386}
3387
3388void QgsRasterMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
3389{
3390 //offset
3391 double offsetX = 0;
3392 double offsetY = 0;
3393 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
3394 offset = QPointF( offsetX, offsetY );
3395
3398 {
3401 }
3402
3403 const bool hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Angle );
3404 if ( hasDataDefinedRotation )
3405 {
3406 const QgsFeature *f = context.feature();
3407 if ( f )
3408 {
3409 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3410 {
3411 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3412 angle += m2p.mapRotation();
3413 }
3414 }
3415 }
3416
3417 if ( angle )
3419}
3420
3421
3423{
3424 QVariantMap map;
3425 map[u"imageFile"_s] = mPath;
3426 map[u"size"_s] = QString::number( mSize );
3427 map[u"size_unit"_s] = QgsUnitTypes::encodeUnit( mSizeUnit );
3428 map[u"size_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3429 map[u"fixedAspectRatio"_s] = QString::number( mFixedAspectRatio );
3430 map[u"angle"_s] = QString::number( mAngle );
3431 map[u"alpha"_s] = QString::number( mOpacity );
3432 map[u"offset"_s] = QgsSymbolLayerUtils::encodePoint( mOffset );
3433 map[u"offset_unit"_s] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3434 map[u"offset_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3435 map[u"scale_method"_s] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
3436 map[u"horizontal_anchor_point"_s] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
3437 map[u"vertical_anchor_point"_s] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
3438 return map;
3439}
3440
3442{
3443 auto m = std::make_unique< QgsRasterMarkerSymbolLayer >();
3444 m->mPath = mPath;
3445 m->mDefaultAspectRatio = mDefaultAspectRatio;
3446 m->mSize = mSize;
3447 m->mAngle = mAngle;
3448 // other members are copied by:
3449 copyCommonProperties( m.get() );
3450 return m.release();
3451}
3452
3453
3467
3472
3474{
3475 return QColor();
3476}
3477
3482
3487
3489{
3490 bool hasDataDefinedSize = false;
3491 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3492 const double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3493 bool hasDataDefinedAspectRatio = false;
3494 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3495 const double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3496
3497 //don't render symbols with size below one or above 10,000 pixels
3498 if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
3499 {
3500 return QRectF();
3501 }
3502
3503 QPointF outputOffset;
3504 double angle = 0.0;
3505 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3506
3507 QTransform transform;
3508
3509 // move to the desired position
3510 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3511
3512 if ( !qgsDoubleNear( angle, 0.0 ) )
3513 transform.rotate( angle );
3514
3515 QRectF symbolBounds = transform.mapRect( QRectF( -width / 2.0, -height / 2.0, width, height ) );
3516
3517 return symbolBounds;
3518}
3519
3520void QgsRasterMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
3521{
3522 QgsSldExportContext context;
3523 context.setExtraProperties( props );
3524 writeSldMarker( doc, element, context );
3525}
3526
3527bool QgsRasterMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
3528{
3529 Q_UNUSED( context )
3530
3531 // <Graphic>
3532 QDomElement graphicElem = doc.createElement( u"se:Graphic"_s );
3533 element.appendChild( graphicElem );
3534
3535 // <ExternalGraphic>
3536 QDomElement extGraphElem = doc.createElement( u"se:ExternalGraphic"_s );
3537 graphicElem.appendChild( extGraphElem );
3538
3539 QMimeDatabase mimeDB;
3540 QMimeType mimeType;
3541
3542 QString base64data;
3543 if ( mPath.startsWith( "base64:"_L1 ) )
3544 {
3545 base64data = mPath.mid( 7 );
3546 }
3547 else
3548 {
3549 QString mime;
3550 QString data;
3552 {
3553 base64data = data;
3554 }
3555 }
3556
3557 if ( !base64data.isEmpty() )
3558 {
3559 // <InlineContent>
3560 QDomElement inlineContEleme = doc.createElement( u"se:InlineContent"_s );
3561
3562 inlineContEleme.setAttribute( u"encoding"_s, u"base64"_s );
3563 inlineContEleme.appendChild( doc.createTextNode( base64data ) );
3564 extGraphElem.appendChild( inlineContEleme );
3565
3566 // determine mime type
3567 const QByteArray ba = QByteArray::fromBase64( base64data.toUtf8() );
3568 mimeType = mimeDB.mimeTypeForData( ba );
3569 }
3570 else
3571 {
3572 // <ExternalGraphic>
3573 QDomElement onlineResElem = doc.createElement( u"se:OnlineResource"_s );
3574 QString url = mPath;
3575
3576 onlineResElem.setAttribute( u"xlink:href"_s, url );
3577 onlineResElem.setAttribute( u"xlink:type"_s, u"simple"_s );
3578 extGraphElem.appendChild( onlineResElem );
3579
3580 // determine mime type
3581 if ( mPath.startsWith( "http://"_L1 ) || mPath.startsWith( "https://"_L1 ) )
3582 {
3583 // Qt can't guess mime type for remote URLs, and it seems geoserver can handle wrong image mime types
3584 // but not generic ones, so let's hardcode to png.
3585 mimeType = mimeDB.mimeTypeForName( "image/png" );
3586 }
3587 else
3588 {
3589 mimeType = mimeDB.mimeTypeForUrl( url );
3590 }
3591 }
3592
3593 QDomElement formatElem = doc.createElement( u"se:Format"_s );
3594 formatElem.appendChild( doc.createTextNode( mimeType.name() ) );
3595 extGraphElem.appendChild( formatElem );
3596
3597 // TODO: write other attributes from the SLD spec (Opacity, Size, Rotation, AnchorPoint, Displacement)
3598 return true;
3599}
3600
3602
3603QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QString chr, double pointSize, const QColor &color, double angle )
3604{
3605 mFontFamily = fontFamily;
3606 mString = chr;
3607 mColor = color;
3608 mAngle = angle;
3609 mSize = pointSize;
3610 mOrigSize = pointSize;
3612 mOffset = QPointF( 0, 0 );
3614 mStrokeColor = DEFAULT_FONTMARKER_BORDERCOLOR;
3615 mStrokeWidth = 0.0;
3616 mStrokeWidthUnit = Qgis::RenderUnit::Millimeters;
3617 mPenJoinStyle = DEFAULT_FONTMARKER_JOINSTYLE;
3618}
3619
3621
3623{
3625 QString string = DEFAULT_FONTMARKER_CHR;
3626 double pointSize = DEFAULT_FONTMARKER_SIZE;
3629
3630 if ( props.contains( u"font"_s ) )
3631 fontFamily = props[u"font"_s].toString();
3632 if ( props.contains( u"chr"_s ) && props[u"chr"_s].toString().length() > 0 )
3633 {
3634 string = props["chr"].toString();
3635 const thread_local QRegularExpression charRegExp( u"%1([0-9]+)%1"_s.arg( FONTMARKER_CHR_FIX ) );
3636 QRegularExpressionMatch match = charRegExp.match( string );
3637 while ( match.hasMatch() )
3638 {
3639 QChar replacement = QChar( match.captured( 1 ).toUShort() );
3640 string = string.mid( 0, match.capturedStart( 0 ) ) + replacement + string.mid( match.capturedEnd( 0 ) );
3641 match = charRegExp.match( string );
3642 }
3643 }
3644
3645 if ( props.contains( u"size"_s ) )
3646 pointSize = props[u"size"_s].toDouble();
3647 if ( props.contains( u"color"_s ) )
3648 color = QgsColorUtils::colorFromString( props[u"color"_s].toString() );
3649 if ( props.contains( u"angle"_s ) )
3650 angle = props[u"angle"_s].toDouble();
3651
3653
3654 if ( props.contains( u"font_style"_s ) )
3655 m->setFontStyle( props[u"font_style"_s].toString() );
3656 if ( props.contains( u"outline_color"_s ) )
3657 m->setStrokeColor( QgsColorUtils::colorFromString( props[u"outline_color"_s].toString() ) );
3658 if ( props.contains( u"outline_width"_s ) )
3659 m->setStrokeWidth( props[u"outline_width"_s].toDouble() );
3660 if ( props.contains( u"offset"_s ) )
3661 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[u"offset"_s].toString() ) );
3662 if ( props.contains( u"offset_unit"_s ) )
3663 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[u"offset_unit"_s].toString() ) );
3664 if ( props.contains( u"offset_map_unit_scale"_s ) )
3665 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"offset_map_unit_scale"_s].toString() ) );
3666 if ( props.contains( u"size_unit"_s ) )
3667 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[u"size_unit"_s].toString() ) );
3668 if ( props.contains( u"size_map_unit_scale"_s ) )
3669 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"size_map_unit_scale"_s].toString() ) );
3670 if ( props.contains( u"outline_width_unit"_s ) )
3671 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[u"outline_width_unit"_s].toString() ) );
3672 if ( props.contains( u"outline_width_map_unit_scale"_s ) )
3673 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[u"outline_width_map_unit_scale"_s].toString() ) );
3674 if ( props.contains( u"joinstyle"_s ) )
3675 m->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[u"joinstyle"_s].toString() ) );
3676 if ( props.contains( u"horizontal_anchor_point"_s ) )
3677 m->setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( props[u"horizontal_anchor_point"_s].toInt() ) );
3678 if ( props.contains( u"vertical_anchor_point"_s ) )
3679 m->setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( props[u"vertical_anchor_point"_s].toInt() ) );
3680
3682
3683 return m;
3684}
3685
3687{
3688 return u"FontMarker"_s;
3689}
3690
3695
3697{
3698 QColor brushColor = mColor;
3699 QColor penColor = mStrokeColor;
3700
3701 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
3702 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
3703
3704 mBrush = QBrush( brushColor );
3705 mPen = QPen( penColor );
3706 mPen.setJoinStyle( mPenJoinStyle );
3707 mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
3708
3709 mFont = QgsFontUtils::createFont( QgsApplication::fontManager()->processFontFamilyName( mFontFamily ) );
3710 if ( !mFontStyle.isEmpty() )
3711 {
3712 mFont.setStyleName( QgsFontUtils::translateNamedStyle( mFontStyle ) );
3713 }
3714
3715 double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3716 mNonZeroFontSize = !qgsDoubleNear( sizePixels, 0.0 );
3717
3718 if ( mNonZeroFontSize && sizePixels > MAX_FONT_CHARACTER_SIZE_IN_PIXELS )
3719 {
3720 // if font is too large (e.g using map units and map is very zoomed in), then we limit
3721 // the font size and instead scale up the painter.
3722 // this avoids issues with massive font sizes (eg https://github.com/qgis/QGIS/issues/42270)
3723 mFontSizeScale = sizePixels / MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3724 sizePixels = MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3725 }
3726 else
3727 mFontSizeScale = 1.0;
3728
3729 // if a non zero, but small pixel size results, round up to 2 pixels so that a "dot" is at least visible
3730 // (if we set a <=1 pixel size here Qt will reset the font to a default size, leading to much too large symbols)
3731 mFont.setPixelSize( std::max( 2, static_cast< int >( std::round( sizePixels ) ) ) );
3732 mFontMetrics = std::make_unique<QFontMetrics>( mFont );
3733 mChrWidth = mFontMetrics->horizontalAdvance( mString );
3734 switch ( mVerticalAnchorPoint )
3735 {
3737 {
3738 mChrOffset = QPointF( mChrWidth / 2.0, -sizePixels / 2.0 );
3739 break;
3740 }
3744 {
3745 mChrOffset = QPointF( mChrWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3746 break;
3747 }
3748 }
3749 mOrigSize = mSize; // save in case the size would be data defined
3750
3751 // use caching only when not using a data defined character
3755 if ( mUseCachedPath )
3756 {
3757 QPointF chrOffset = mChrOffset;
3758 double chrWidth;
3759 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3760 mCachedPath = QPainterPath();
3761 mCachedPath.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3762 }
3763}
3764
3766{
3767 Q_UNUSED( context )
3768}
3769
3770QString QgsFontMarkerSymbolLayer::characterToRender( QgsSymbolRenderContext &context, QPointF &charOffset, double &charWidth )
3771{
3772 charOffset = mChrOffset;
3773 QString stringToRender = mString;
3775 {
3776 context.setOriginalValueVariable( mString );
3778 if ( stringToRender != mString )
3779 {
3780 charWidth = mFontMetrics->horizontalAdvance( stringToRender );
3781 switch ( mVerticalAnchorPoint )
3782 {
3784 {
3785 const double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3786 charOffset = QPointF( charWidth / 2.0, -sizePixels / 2.0 );
3787 break;
3788 }
3792 {
3793 charOffset = QPointF( charWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3794 break;
3795 }
3796 }
3797 }
3798 }
3799 return stringToRender;
3800}
3801
3802void QgsFontMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle ) const
3803{
3804 //offset
3805 double offsetX = 0;
3806 double offsetY = 0;
3807 markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
3808 offset = QPointF( offsetX, offsetY );
3809 hasDataDefinedRotation = false;
3810
3811 //angle
3812 bool ok = true;
3815 {
3818
3819 // If the expression evaluation was not successful, fallback to static value
3820 if ( !ok )
3822
3823 hasDataDefinedRotation = true;
3824 }
3825
3826 hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || hasDataDefinedRotation;
3827 if ( hasDataDefinedRotation )
3828 {
3829 // For non-point markers, "dataDefinedRotation" means following the
3830 // shape (shape-data defined). For them, "field-data defined" does
3831 // not work at all. TODO: if "field-data defined" ever gets implemented
3832 // we'll need a way to distinguish here between the two, possibly
3833 // using another flag in renderHints()
3834 const QgsFeature *f = context.feature();
3835 if ( f )
3836 {
3837 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3838 {
3839 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3840 angle += m2p.mapRotation();
3841 }
3842 }
3843 }
3844
3845 if ( angle )
3847}
3848
3849double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context )
3850{
3851 double scaledSize = mSize;
3852 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
3853
3854 bool ok = true;
3855 if ( hasDataDefinedSize )
3856 {
3858 scaledSize = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::Size, context.renderContext().expressionContext(), mSize, &ok );
3859 }
3860
3861 if ( hasDataDefinedSize && ok )
3862 {
3863 switch ( mScaleMethod )
3864 {
3866 scaledSize = std::sqrt( scaledSize );
3867 break;
3869 break;
3870 }
3871 }
3872 return scaledSize;
3873}
3874
3876{
3877 QPainter *p = context.renderContext().painter();
3878 if ( !p || !mNonZeroFontSize )
3879 return;
3880
3881 QTransform transform;
3882
3883 bool ok;
3884 QColor brushColor = mColor;
3886 {
3888 brushColor = mDataDefinedProperties.valueAsColor( QgsSymbolLayer::Property::FillColor, context.renderContext().expressionContext(), brushColor );
3889 }
3890 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
3891 brushColor = useSelectedColor ? context.renderContext().selectionColor() : brushColor;
3892 if ( !useSelectedColor || !SELECTION_IS_OPAQUE )
3893 {
3894 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
3895 }
3896 mBrush.setColor( brushColor );
3897
3898 QColor penColor = mStrokeColor;
3900 {
3902 penColor = mDataDefinedProperties.valueAsColor( QgsSymbolLayer::Property::StrokeColor, context.renderContext().expressionContext(), penColor );
3903 }
3904 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
3905
3906 double penWidth = context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3908 {
3909 context.setOriginalValueVariable( mStrokeWidth );
3910 const double strokeWidth = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::StrokeWidth, context.renderContext().expressionContext(), mStrokeWidth, &ok );
3911 if ( ok )
3912 {
3913 penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3914 }
3915 }
3916
3918 {
3920 const QString style = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::JoinStyle, context.renderContext().expressionContext(), QString(), &ok );
3921 if ( ok )
3922 {
3923 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
3924 }
3925 }
3926
3927 const QgsScopedQPainterState painterState( p );
3928 p->setBrush( mBrush );
3929 if ( !qgsDoubleNear( penWidth, 0.0 ) )
3930 {
3931 mPen.setColor( penColor );
3932 mPen.setWidthF( penWidth );
3933 p->setPen( mPen );
3934 }
3935 else
3936 {
3937 p->setPen( Qt::NoPen );
3938 }
3939
3941 {
3942 context.setOriginalValueVariable( mFontFamily );
3943 const QString fontFamily = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::FontFamily, context.renderContext().expressionContext(), mFontFamily, &ok );
3944 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( ok ? fontFamily : mFontFamily );
3945 QgsFontUtils::setFontFamily( mFont, processedFamily );
3946 }
3948 {
3949 context.setOriginalValueVariable( mFontStyle );
3950 const QString fontStyle = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::FontStyle, context.renderContext().expressionContext(), mFontStyle, &ok );
3952 }
3954 {
3955 mFontMetrics = std::make_unique<QFontMetrics>( mFont );
3956 }
3957
3958 QPointF chrOffset = mChrOffset;
3959 double chrWidth;
3960 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3961
3962 const double sizeToRender = calculateSize( context );
3963
3964 bool hasDataDefinedRotation = false;
3965 QPointF offset;
3966 double angle = 0;
3967 calculateOffsetAndRotation( context, sizeToRender, hasDataDefinedRotation, offset, angle );
3968
3969 p->translate( point.x() + offset.x(), point.y() + offset.y() );
3970
3971 if ( !qgsDoubleNear( angle, 0.0 ) )
3972 transform.rotate( angle );
3973
3974 if ( !qgsDoubleNear( sizeToRender, mOrigSize ) )
3975 {
3976 const double s = sizeToRender / mOrigSize;
3977 transform.scale( s, s );
3978 }
3979
3980 if ( !qgsDoubleNear( mFontSizeScale, 1.0 ) )
3981 transform.scale( mFontSizeScale, mFontSizeScale );
3982
3983 if ( mUseCachedPath )
3984 {
3985 p->drawPath( transform.map( mCachedPath ) );
3986 }
3987 else
3988 {
3989 QPainterPath path;
3990 path.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3991 p->drawPath( transform.map( path ) );
3992 }
3993}
3994
3996{
3997 QVariantMap props;
3998 props[u"font"_s] = mFontFamily;
3999 props[u"font_style"_s] = mFontStyle;
4000 QString chr = mString;
4001 for ( int i = 0; i < 32; i++ )
4002 {
4003 if ( i == 9 || i == 10 || i == 13 )
4004 {
4005 continue;
4006 }
4007 chr.replace( QChar( i ), u"%1%2%1"_s.arg( FONTMARKER_CHR_FIX, QString::number( i ) ) );
4008 }
4009 props[u"chr"_s] = chr;
4010 props[u"size"_s] = QString::number( mSize );
4011 props[u"size_unit"_s] = QgsUnitTypes::encodeUnit( mSizeUnit );
4012 props[u"size_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
4013 props[u"color"_s] = QgsColorUtils::colorToString( mColor );
4014 props[u"outline_color"_s] = QgsColorUtils::colorToString( mStrokeColor );
4015 props[u"outline_width"_s] = QString::number( mStrokeWidth );
4016 props[u"outline_width_unit"_s] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
4017 props[u"outline_width_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
4018 props[u"joinstyle"_s] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
4019 props[u"angle"_s] = QString::number( mAngle );
4020 props[u"offset"_s] = QgsSymbolLayerUtils::encodePoint( mOffset );
4021 props[u"offset_unit"_s] = QgsUnitTypes::encodeUnit( mOffsetUnit );
4022 props[u"offset_map_unit_scale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
4023 props[u"horizontal_anchor_point"_s] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
4024 props[u"vertical_anchor_point"_s] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
4025 return props;
4026}
4027
4029{
4030 QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( mFontFamily, mString, mSize, mColor, mAngle );
4031 m->setFontStyle( mFontStyle );
4032 m->setStrokeColor( mStrokeColor );
4033 m->setStrokeWidth( mStrokeWidth );
4034 m->setStrokeWidthUnit( mStrokeWidthUnit );
4035 m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
4036 m->setPenJoinStyle( mPenJoinStyle );
4037 m->setOffset( mOffset );
4040 m->setSizeUnit( mSizeUnit );
4045 return m;
4046}
4047
4048void QgsFontMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
4049{
4050 QgsSldExportContext context;
4051 context.setExtraProperties( props );
4052 toSld( doc, element, context );
4053}
4054
4055bool QgsFontMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
4056{
4057 return QgsMarkerSymbolLayer::toSld( doc, element, context );
4058}
4059
4060void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
4061{
4062 QgsSldExportContext context;
4063 context.setExtraProperties( props );
4064 writeSldMarker( doc, element, context );
4065}
4066
4067bool QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
4068{
4069 // <Graphic>
4070 QDomElement graphicElem = doc.createElement( u"se:Graphic"_s );
4071 element.appendChild( graphicElem );
4072
4073 const QVariantMap props = context.extraProperties();
4074 const QString fontPath = u"ttf://%1"_s.arg( mFontFamily );
4075 int markIndex = !mString.isEmpty() ? mString.at( 0 ).unicode() : 0;
4076 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
4077 QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, u"ttf"_s, context, &markIndex, mColor, size );
4078
4079 // <Rotation>
4080 QString angleFunc;
4081 bool ok;
4082 const double angle = props.value( u"angle"_s, u"0"_s ).toDouble( &ok );
4083 if ( !ok )
4084 {
4085 angleFunc = u"%1 + %2"_s.arg( props.value( u"angle"_s, u"0"_s ).toString() ).arg( mAngle );
4086 }
4087 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
4088 {
4089 angleFunc = QString::number( angle + mAngle );
4090 }
4091 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc, context );
4092
4093 // <Displacement>
4094 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
4096 return true;
4097}
4098
4108
4110{
4112 mStrokeWidthUnit = unit;
4113}
4114
4116{
4117 QPointF chrOffset = mChrOffset;
4118 double chrWidth = mChrWidth;
4119 //calculate width of rendered character
4120 ( void ) characterToRender( context, chrOffset, chrWidth );
4121
4122 if ( !mFontMetrics )
4123 mFontMetrics = std::make_unique<QFontMetrics>( mFont );
4124
4125 double scaledSize = calculateSize( context );
4126 if ( !qgsDoubleNear( scaledSize, mOrigSize ) )
4127 {
4128 chrWidth *= scaledSize / mOrigSize;
4129 }
4130 chrWidth *= mFontSizeScale;
4131
4132 bool hasDataDefinedRotation = false;
4133 QPointF offset;
4134 double angle = 0;
4135 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
4136 scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
4137
4138 QTransform transform;
4139
4140 // move to the desired position
4141 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
4142
4143 if ( !qgsDoubleNear( angle, 0.0 ) )
4144 transform.rotate( angle );
4145
4146 QRectF symbolBounds = transform.mapRect( QRectF( -chrWidth / 2.0, -scaledSize / 2.0, chrWidth, scaledSize ) );
4147 return symbolBounds;
4148}
4149
4151{
4152 QgsDebugMsgLevel( u"Entered."_s, 4 );
4153
4154 QDomElement graphicElem = element.firstChildElement( u"Graphic"_s );
4155 if ( graphicElem.isNull() )
4156 return nullptr;
4157
4158 QString name, format;
4159 QColor color;
4160 double size;
4161 int chr;
4162
4163 if ( !QgsSymbolLayerUtils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
4164 return nullptr;
4165
4166 if ( !name.startsWith( "ttf://"_L1 ) || format != "ttf"_L1 )
4167 return nullptr;
4168
4169 const QString fontFamily = name.mid( 6 );
4170
4171 double angle = 0.0;
4172 QString angleFunc;
4173 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
4174 {
4175 bool ok;
4176 const double d = angleFunc.toDouble( &ok );
4177 if ( ok )
4178 angle = d;
4179 }
4180
4181 QPointF offset;
4183
4184 double scaleFactor = 1.0;
4185 const QString uom = element.attribute( u"uom"_s );
4186 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
4187 offset.setX( offset.x() * scaleFactor );
4188 offset.setY( offset.y() * scaleFactor );
4189 size = size * scaleFactor;
4190
4192 m->setOutputUnit( sldUnitSize );
4193 m->setAngle( angle );
4194 m->setOffset( offset );
4195 return m;
4196}
4197
4199{
4200 const QString fontFamily = properties.value( u"font"_s, DEFAULT_FONTMARKER_FONT ).toString();
4201 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( fontFamily );
4202 QString matched;
4203 if ( !QgsFontUtils::fontFamilyMatchOnSystem( processedFamily ) && !QgsApplication::fontManager()->tryToDownloadFontFamily( processedFamily, matched ) )
4204 {
4205 context.pushMessage( QObject::tr( "Font “%1” not available on system" ).arg( processedFamily ) );
4206 }
4207}
4208
4210{
4211 QMap<QString, QgsProperty>::iterator it = mParameters.begin();
4212 for ( ; it != mParameters.end(); ++it )
4213 it.value().prepare( context.renderContext().expressionContext() );
4214
4216}
4217
4218
4220{
4221 QSet<QString> attrs = QgsMarkerSymbolLayer::usedAttributes( context );
4222
4223 QMap<QString, QgsProperty>::const_iterator it = mParameters.constBegin();
4224 for ( ; it != mParameters.constEnd(); ++it )
4225 {
4226 attrs.unite( it.value().referencedFields( context.expressionContext(), true ) );
4227 }
4228
4229 return attrs;
4230}
4231
4232//
4233// QgsAnimatedMarkerSymbolLayer
4234//
4235
4239
4241
4242QgsSymbolLayer *QgsAnimatedMarkerSymbolLayer::create( const QVariantMap &properties ) // cppcheck-suppress duplInheritedMember
4243{
4244 QString path;
4247
4248 if ( properties.contains( u"imageFile"_s ) )
4249 path = properties[u"imageFile"_s].toString();
4250 if ( properties.contains( u"size"_s ) )
4251 size = properties[u"size"_s].toDouble();
4252 if ( properties.contains( u"angle"_s ) )
4253 angle = properties[u"angle"_s].toDouble();
4254
4255 auto m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( path, size, angle );
4256 m->setFrameRate( properties.value( u"frameRate"_s, u"10"_s ).toDouble() );
4257
4258 m->setCommonProperties( properties );
4259 return m.release();
4260}
4261
4263{
4264 return u"AnimatedMarker"_s;
4265}
4266
4268{
4269 QVariantMap res = QgsRasterMarkerSymbolLayer::properties();
4270 res.insert( u"frameRate"_s, mFrameRateFps );
4271 return res;
4272}
4273
4275{
4276 auto m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( mPath, mSize, mAngle );
4277 m->setFrameRate( mFrameRateFps );
4278 copyCommonProperties( m.get() );
4279 return m.release();
4280}
4281
4283{
4285
4286 mPreparedPaths.clear();
4287 if ( !mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Name ) && !mPath.isEmpty() )
4288 {
4290 mStaticPath = true;
4291 }
4292 else
4293 {
4294 mStaticPath = false;
4295 }
4296}
4297
4298QImage QgsAnimatedMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
4299{
4300 if ( !mStaticPath && !mPreparedPaths.contains( path ) )
4301 {
4303 mPreparedPaths.insert( path );
4304 }
4305
4306 const long long mapFrameNumber = context.currentFrame();
4308 const double markerAnimationDuration = totalFrameCount / mFrameRateFps;
4309
4310 double animationTimeSeconds = 0;
4311 if ( mapFrameNumber >= 0 && context.frameRate() > 0 )
4312 {
4313 // render is part of an animation, so we base the calculated frame on that
4314 animationTimeSeconds = mapFrameNumber / context.frameRate();
4315 }
4316 else
4317 {
4318 // render is outside of animation, so base the calculated frame on the current epoch
4319 animationTimeSeconds = QDateTime::currentMSecsSinceEpoch() / 1000.0;
4320 }
4321
4322 const double markerAnimationProgressSeconds = std::fmod( animationTimeSeconds, markerAnimationDuration );
4323 const int movieFrame = static_cast< int >( std::floor( markerAnimationProgressSeconds * mFrameRateFps ) );
4324
4325 bool cached = false;
4326 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking, 96, movieFrame );
4327}
@ DynamicRotation
Rotation of symbol may be changed during rendering and symbol should not be cached.
Definition qgis.h:796
@ IsSymbolLayerSubSymbol
Symbol is being rendered as a sub-symbol of a QgsSymbolLayer.
Definition qgis.h:797
@ CanCalculateMaskGeometryPerFeature
If present, indicates that mask geometry can safely be calculated per feature for the symbol layer....
Definition qgis.h:908
ScaleMethod
Scale methods.
Definition qgis.h:650
@ ScaleDiameter
Calculate scale by the diameter.
Definition qgis.h:652
@ ScaleArea
Calculate scale by the area.
Definition qgis.h:651
QFlags< SymbolLayerFlag > SymbolLayerFlags
Symbol layer flags.
Definition qgis.h:913
MarkerShape
Marker shapes.
Definition qgis.h:3194
@ Pentagon
Pentagon.
Definition qgis.h:3197
@ Arrow
Arrow.
Definition qgis.h:3202
@ Hexagon
Hexagon.
Definition qgis.h:3198
@ EquilateralTriangle
Equilateral triangle.
Definition qgis.h:3200
@ SemiCircle
Semi circle (top half).
Definition qgis.h:3210
@ QuarterCircle
Quarter circle (top left quarter).
Definition qgis.h:3212
@ Circle
Circle.
Definition qgis.h:3203
@ LeftHalfTriangle
Left half of triangle.
Definition qgis.h:3217
@ ArrowHead
Right facing arrow head (unfilled, lines only).
Definition qgis.h:3208
@ ParallelogramRight
Parallelogram that slants right.
Definition qgis.h:3224
@ AsteriskFill
A filled asterisk shape.
Definition qgis.h:3220
@ Octagon
Octagon.
Definition qgis.h:3218
@ HalfArc
A line-only half arc.
Definition qgis.h:3221
@ Line
Vertical line.
Definition qgis.h:3207
@ QuarterSquare
Quarter square (top left quarter).
Definition qgis.h:3213
@ Triangle
Triangle.
Definition qgis.h:3199
@ Cross2
Rotated cross (lines only), 'x' shape.
Definition qgis.h:3206
@ Trapezoid
Trapezoid.
Definition qgis.h:3226
@ ArrowHeadFilled
Right facing filled arrow head.
Definition qgis.h:3209
@ Diamond
Diamond.
Definition qgis.h:3196
@ Shield
A shape consisting of a triangle attached to a rectangle.
Definition qgis.h:3227
@ HalfSquare
Half square (left half).
Definition qgis.h:3214
@ CrossFill
Solid filled cross.
Definition qgis.h:3205
@ Decagon
Decagon.
Definition qgis.h:3230
@ RoundedSquare
A square with rounded corners.
Definition qgis.h:3231
@ RightHalfTriangle
Right half of triangle.
Definition qgis.h:3216
@ Square
Square.
Definition qgis.h:3195
@ ThirdCircle
One third circle (top left third).
Definition qgis.h:3211
@ ThirdArc
A line-only one third arc.
Definition qgis.h:3222
@ SquareWithCorners
A square with diagonal corners.
Definition qgis.h:3219
@ QuarterArc
A line-only one quarter arc.
Definition qgis.h:3223
@ DiamondStar
A 4-sided star.
Definition qgis.h:3228
@ Cross
Cross (lines only).
Definition qgis.h:3204
@ ParallelogramLeft
Parallelogram that slants left.
Definition qgis.h:3225
@ Heart
Heart.
Definition qgis.h:3229
@ DiagonalHalfSquare
Diagonal half square (bottom left half).
Definition qgis.h:3215
VerticalAnchorPoint
Marker symbol vertical anchor points.
Definition qgis.h:840
@ Bottom
Align to bottom of symbol.
Definition qgis.h:843
@ Center
Align to vertical center of symbol.
Definition qgis.h:842
@ Baseline
Align to baseline of symbol, e.g. font baseline for font marker symbol layers. Treated as Bottom if n...
Definition qgis.h:844
@ Top
Align to top of symbol.
Definition qgis.h:841
@ Point
Points.
Definition qgis.h:380
RenderUnit
Rendering size units.
Definition qgis.h:5340
@ Percentage
Percentage of another measurement (e.g., canvas size, feature size).
Definition qgis.h:5344
@ Millimeters
Millimeters.
Definition qgis.h:5341
@ Unknown
Mixed or unknown units.
Definition qgis.h:5347
@ MapUnits
Map units.
Definition qgis.h:5342
@ MetersInMapUnits
Meters value as Map units.
Definition qgis.h:5348
@ RenderingSubSymbol
Set whenever a sub-symbol of a parent symbol is currently being rendered. Can be used during symbol a...
Definition qgis.h:2862
@ RenderSymbolPreview
The render is for a symbol preview only and map based properties may not be available,...
Definition qgis.h:2857
@ RenderLayerTree
The render is for a layer tree display where map based properties are not available and where avoidan...
Definition qgis.h:2868
@ RenderBlocking
Render and load remote sources in the same thread to ensure rendering remote sources (svg and images)...
Definition qgis.h:2856
@ Fill
Fill symbol.
Definition qgis.h:639
HorizontalAnchorPoint
Marker symbol horizontal anchor points.
Definition qgis.h:826
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:227
Qgis::SymbolType type() const
Returns the symbol's type.
Definition qgssymbol.h:296
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:6975
QMap< QString, QString > QgsStringMap
Definition qgis.h:7475
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