QGIS API Documentation 3.39.0-Master (3aed037ce22)
Loading...
Searching...
No Matches
qgsmarkersymbollayer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmarkersymbollayer.cpp
3 ---------------------
4 begin : November 2009
5 copyright : (C) 2009 by Martin Dobias
6 email : wonder dot sk at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17#include "qgssymbollayerutils.h"
18
19#include "qgsdxfexport.h"
20#include "qgsdxfpaintdevice.h"
21#include "qgsfontutils.h"
22#include "qgsimagecache.h"
23#include "qgsimageoperation.h"
24#include "qgsrendercontext.h"
25#include "qgslogger.h"
26#include "qgssvgcache.h"
27#include "qgsunittypes.h"
28#include "qgssymbol.h"
29#include "qgsfillsymbol.h"
30#include "qgsfontmanager.h"
31#include "qgscolorutils.h"
32#include "qgspainting.h"
33
34#include <QPainter>
35#include <QSvgRenderer>
36#include <QFileInfo>
37#include <QDir>
38#include <QDomDocument>
39#include <QDomElement>
40#include <QUrlQuery>
41
42#include <cmath>
43
44static constexpr int MAX_FONT_CHARACTER_SIZE_IN_PIXELS = 500;
45
47
48
49//
50// QgsSimpleMarkerSymbolLayerBase
51//
52
96
107
109
111{
112 switch ( shape )
113 {
144 return true;
145
153 return false;
154 }
155 return true;
156}
157
159{
160 const bool hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation
162 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
163
164 // use either QPolygonF or QPainterPath for drawing
165 if ( !prepareMarkerShape( mShape ) ) // drawing as a polygon
166 {
167 prepareMarkerPath( mShape ); // drawing as a painter path
168 }
169
170 QTransform transform;
171
172 // scale the shape (if the size is not going to be modified)
173 if ( !hasDataDefinedSize )
174 {
175 double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
177 {
178 // rendering for symbol previews -- an size in meters in map units can't be calculated, so treat the size as millimeters
179 // and clamp it to a reasonable range. It's the best we can do in this situation!
180 scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
181 }
182
183 const double half = scaledSize / 2.0;
184 transform.scale( half, half );
185 }
186
187 // rotate if the rotation is not going to be changed during the rendering
188 if ( !hasDataDefinedRotation && !qgsDoubleNear( mAngle, 0.0 ) )
189 {
190 transform.rotate( mAngle );
191 }
192
193 if ( !mPolygon.isEmpty() )
194 mPolygon = transform.map( mPolygon );
195 else
196 mPath = transform.map( mPath );
197
199}
200
202{
203 Q_UNUSED( context )
204}
205
207{
208 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
209 //of the rendered point!
210
211 QPainter *p = context.renderContext().painter();
212 if ( !p )
213 {
214 return;
215 }
216
217 bool hasDataDefinedSize = false;
218 const double scaledSize = calculateSize( context, hasDataDefinedSize );
219
220 bool hasDataDefinedRotation = false;
221 QPointF offset;
222 double angle = 0;
223 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
224
225 //data defined shape?
226 bool createdNewPath = false;
227 bool ok = true;
228 Qgis::MarkerShape symbol = mShape;
230 {
231 context.setOriginalValueVariable( encodeShape( symbol ) );
233 if ( !QgsVariantUtils::isNull( exprVal ) )
234 {
235 const Qgis::MarkerShape decoded = decodeShape( exprVal.toString(), &ok );
236 if ( ok )
237 {
238 symbol = decoded;
239
240 if ( !prepareMarkerShape( symbol ) ) // drawing as a polygon
241 {
242 prepareMarkerPath( symbol ); // drawing as a painter path
243 }
244 createdNewPath = true;
245 }
246 }
247 else
248 {
249 symbol = mShape;
250 }
251 }
252
253 QTransform transform;
254
255 // move to the desired position
256 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
257
258 // resize if necessary
259 if ( hasDataDefinedSize || createdNewPath )
260 {
261 double s = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
263 {
264 // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
265 // and clamp it to a reasonable range. It's the best we can do in this situation!
266 s = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
267 }
268 const double half = s / 2.0;
269 transform.scale( half, half );
270 }
271
272 if ( !qgsDoubleNear( angle, 0.0 ) && ( hasDataDefinedRotation || createdNewPath ) )
273 {
274 transform.rotate( angle );
275 }
276
277 //need to pass: symbol, polygon, path
278
279 QPolygonF polygon;
280 QPainterPath path;
281 if ( !mPolygon.isEmpty() )
282 {
283 polygon = transform.map( mPolygon );
284 }
285 else
286 {
287 path = transform.map( mPath );
288 }
289 draw( context, symbol, polygon, path );
290}
291
293{
294 bool hasDataDefinedSize = false;
295 double scaledSize = calculateSize( context, hasDataDefinedSize );
296
297 bool hasDataDefinedRotation = false;
298 QPointF offset;
299 double angle = 0;
300 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
301
302 scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
303
304 QTransform transform;
305
306 // move to the desired position
307 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
308
309 if ( !qgsDoubleNear( angle, 0.0 ) )
310 transform.rotate( angle );
311
312 return transform.mapRect( QRectF( -scaledSize / 2.0,
313 -scaledSize / 2.0,
314 scaledSize,
315 scaledSize ) );
316}
317
319{
320 if ( ok )
321 *ok = true;
322 const QString cleaned = name.toLower().trimmed();
323
324 if ( cleaned == QLatin1String( "square" ) || cleaned == QLatin1String( "rectangle" ) )
326 else if ( cleaned == QLatin1String( "trapezoid" ) )
328 else if ( cleaned == QLatin1String( "parallelogram_right" ) )
330 else if ( cleaned == QLatin1String( "parallelogram_left" ) )
332 else if ( cleaned == QLatin1String( "square_with_corners" ) )
334 else if ( cleaned == QLatin1String( "rounded_square" ) )
336 else if ( cleaned == QLatin1String( "diamond" ) )
338 else if ( cleaned == QLatin1String( "shield" ) )
340 else if ( cleaned == QLatin1String( "pentagon" ) )
342 else if ( cleaned == QLatin1String( "hexagon" ) )
344 else if ( cleaned == QLatin1String( "octagon" ) )
346 else if ( cleaned == QLatin1String( "decagon" ) )
348 else if ( cleaned == QLatin1String( "triangle" ) )
350 else if ( cleaned == QLatin1String( "equilateral_triangle" ) )
352 else if ( cleaned == QLatin1String( "star_diamond" ) )
354 else if ( cleaned == QLatin1String( "star" ) || cleaned == QLatin1String( "regular_star" ) )
356 else if ( cleaned == QLatin1String( "heart" ) )
358 else if ( cleaned == QLatin1String( "arrow" ) )
360 else if ( cleaned == QLatin1String( "circle" ) )
362 else if ( cleaned == QLatin1String( "cross" ) )
364 else if ( cleaned == QLatin1String( "cross_fill" ) )
366 else if ( cleaned == QLatin1String( "cross2" ) || cleaned == QLatin1String( "x" ) )
368 else if ( cleaned == QLatin1String( "line" ) )
370 else if ( cleaned == QLatin1String( "arrowhead" ) )
372 else if ( cleaned == QLatin1String( "filled_arrowhead" ) )
374 else if ( cleaned == QLatin1String( "semi_circle" ) )
376 else if ( cleaned == QLatin1String( "third_circle" ) )
378 else if ( cleaned == QLatin1String( "quarter_circle" ) )
380 else if ( cleaned == QLatin1String( "quarter_square" ) )
382 else if ( cleaned == QLatin1String( "half_square" ) )
384 else if ( cleaned == QLatin1String( "diagonal_half_square" ) )
386 else if ( cleaned == QLatin1String( "right_half_triangle" ) )
388 else if ( cleaned == QLatin1String( "left_half_triangle" ) )
390 else if ( cleaned == QLatin1String( "asterisk_fill" ) )
392 else if ( cleaned == QLatin1String( "half_arc" ) )
394 else if ( cleaned == QLatin1String( "third_arc" ) )
396 else if ( cleaned == QLatin1String( "quarter_arc" ) )
398
399 if ( ok )
400 *ok = false;
402}
403
405{
406 switch ( shape )
407 {
409 return QStringLiteral( "square" );
411 return QStringLiteral( "quarter_square" );
413 return QStringLiteral( "half_square" );
415 return QStringLiteral( "diagonal_half_square" );
417 return QStringLiteral( "parallelogram_right" );
419 return QStringLiteral( "parallelogram_left" );
421 return QStringLiteral( "trapezoid" );
423 return QStringLiteral( "shield" );
425 return QStringLiteral( "diamond" );
427 return QStringLiteral( "pentagon" );
429 return QStringLiteral( "hexagon" );
431 return QStringLiteral( "octagon" );
433 return QStringLiteral( "decagon" );
435 return QStringLiteral( "square_with_corners" );
437 return QStringLiteral( "rounded_square" );
439 return QStringLiteral( "triangle" );
441 return QStringLiteral( "equilateral_triangle" );
443 return QStringLiteral( "left_half_triangle" );
445 return QStringLiteral( "right_half_triangle" );
447 return QStringLiteral( "star_diamond" );
449 return QStringLiteral( "star" );
451 return QStringLiteral( "heart" );
453 return QStringLiteral( "arrow" );
455 return QStringLiteral( "filled_arrowhead" );
457 return QStringLiteral( "cross_fill" );
459 return QStringLiteral( "circle" );
461 return QStringLiteral( "cross" );
463 return QStringLiteral( "cross2" );
465 return QStringLiteral( "line" );
467 return QStringLiteral( "arrowhead" );
469 return QStringLiteral( "semi_circle" );
471 return QStringLiteral( "third_circle" );
473 return QStringLiteral( "quarter_circle" );
475 return QStringLiteral( "asterisk_fill" );
477 return QStringLiteral( "half_arc" );
479 return QStringLiteral( "third_arc" );
481 return QStringLiteral( "quarter_arc" );
482 }
483 return QString();
484}
485
490
492{
493 polygon.clear();
494
495 switch ( shape )
496 {
498 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 1, 1 ) ) );
499 return true;
500
502 {
503 static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 0.6072;
504
505 polygon << QPointF( - VERTEX_OFFSET_FROM_ORIGIN, 1 )
506 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, 1 )
507 << QPointF( 1, VERTEX_OFFSET_FROM_ORIGIN )
508 << QPointF( 1, -VERTEX_OFFSET_FROM_ORIGIN )
509 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, -1 )
510 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, -1 )
511 << QPointF( -1, -VERTEX_OFFSET_FROM_ORIGIN )
512 << QPointF( -1, VERTEX_OFFSET_FROM_ORIGIN )
513 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 );
514 return true;
515 }
516
518 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 0 ) ) );
519 return true;
520
522 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 1 ) ) );
523 return true;
524
526 polygon << QPointF( -1, -1 ) << QPointF( 1, 1 ) << QPointF( -1, 1 ) << QPointF( -1, -1 );
527 return true;
528
530 polygon << QPointF( 0.5, -0.5 )
531 << QPointF( 1, 0.5 )
532 << QPointF( -1, 0.5 )
533 << QPointF( -0.5, -0.5 )
534 << QPointF( 0.5, -0.5 );
535 return true;
536
538 polygon << QPointF( 0.5, 0.5 )
539 << QPointF( 1, -0.5 )
540 << QPointF( -0.5, -0.5 )
541 << QPointF( -1, 0.5 )
542 << QPointF( 0.5, 0.5 );
543 return true;
544
546 polygon << QPointF( 1, 0.5 )
547 << QPointF( 0.5, -0.5 )
548 << QPointF( -1, -0.5 )
549 << QPointF( -0.5, 0.5 )
550 << QPointF( 1, 0.5 );
551 return true;
552
554 polygon << QPointF( -1, 0 ) << QPointF( 0, 1 )
555 << QPointF( 1, 0 ) << QPointF( 0, -1 ) << QPointF( -1, 0 );
556 return true;
557
559 polygon << QPointF( 1, 0.5 )
560 << QPointF( 1, -1 )
561 << QPointF( -1, -1 )
562 << QPointF( -1, 0.5 )
563 << QPointF( 0, 1 )
564 << QPointF( 1, 0.5 );
565 return true;
566
568 /* angular-representation of hardcoded values used
569 polygon << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288.0 ) ) )
570 << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) )
571 << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) )
572 << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) )
573 << QPointF( 0, -1 ); */
574 polygon << QPointF( -0.9511, -0.3090 )
575 << QPointF( -0.5878, 0.8090 )
576 << QPointF( 0.5878, 0.8090 )
577 << QPointF( 0.9511, -0.3090 )
578 << QPointF( 0, -1 )
579 << QPointF( -0.9511, -0.3090 );
580 return true;
581
583 /* angular-representation of hardcoded values used
584 polygon << QPointF( std::sin( DEG2RAD( 300.0 ) ), - std::cos( DEG2RAD( 300.0 ) ) )
585 << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
586 << QPointF( std::sin( DEG2RAD( 180.0 ) ), - std::cos( DEG2RAD( 180.0 ) ) )
587 << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
588 << QPointF( std::sin( DEG2RAD( 60.0 ) ), - std::cos( DEG2RAD( 60.0 ) ) )
589 << QPointF( 0, -1 ); */
590 polygon << QPointF( -0.8660, -0.5 )
591 << QPointF( -0.8660, 0.5 )
592 << QPointF( 0, 1 )
593 << QPointF( 0.8660, 0.5 )
594 << QPointF( 0.8660, -0.5 )
595 << QPointF( 0, -1 )
596 << QPointF( -0.8660, -0.5 );
597 return true;
598
600 {
601 static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 1.0 / ( 1 + M_SQRT2 );
602
603 polygon << QPointF( - VERTEX_OFFSET_FROM_ORIGIN, 1 )
604 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, 1 )
605 << QPointF( 1, VERTEX_OFFSET_FROM_ORIGIN )
606 << QPointF( 1, -VERTEX_OFFSET_FROM_ORIGIN )
607 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, -1 )
608 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, -1 )
609 << QPointF( -1, -VERTEX_OFFSET_FROM_ORIGIN )
610 << QPointF( -1, VERTEX_OFFSET_FROM_ORIGIN )
611 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 );
612 return true;
613 }
614
616 {
617
618 polygon << QPointF( 0.587785252, 0.809016994 )
619 << QPointF( 0.951056516, 0.309016994 )
620 << QPointF( 0.951056516, -0.309016994 )
621 << QPointF( 0.587785252, -0.809016994 )
622 << QPointF( 0, -1 )
623 << QPointF( -0.587785252, -0.809016994 )
624 << QPointF( -0.951056516, -0.309016994 )
625 << QPointF( -0.951056516, 0.309016994 )
626 << QPointF( -0.587785252, 0.809016994 )
627 << QPointF( 0, 1 )
628 << QPointF( 0.587785252, 0.809016994 );
629 return true;
630 }
631
633 polygon << QPointF( -1, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
634 return true;
635
637 /* angular-representation of hardcoded values used
638 polygon << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
639 << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
640 << QPointF( 0, -1 ); */
641 polygon << QPointF( -0.8660, 0.5 )
642 << QPointF( 0.8660, 0.5 )
643 << QPointF( 0, -1 )
644 << QPointF( -0.8660, 0.5 );
645 return true;
646
648 polygon << QPointF( 0, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( 0, 1 );
649 return true;
650
652 polygon << QPointF( -1, 1 ) << QPointF( 0, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
653 return true;
654
656 {
657 const double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
658
659 polygon << QPointF( inner_r * std::sin( DEG2RAD( 315.0 ) ), - inner_r * std::cos( DEG2RAD( 315.0 ) ) )
660 << QPointF( std::sin( DEG2RAD( 270 ) ), - std::cos( DEG2RAD( 270 ) ) )
661 << QPointF( inner_r * std::sin( DEG2RAD( 225.0 ) ), - inner_r * std::cos( DEG2RAD( 225.0 ) ) )
662 << QPointF( std::sin( DEG2RAD( 180 ) ), - std::cos( DEG2RAD( 180 ) ) )
663 << QPointF( inner_r * std::sin( DEG2RAD( 135.0 ) ), - inner_r * std::cos( DEG2RAD( 135.0 ) ) )
664 << QPointF( std::sin( DEG2RAD( 90 ) ), - std::cos( DEG2RAD( 90 ) ) )
665 << QPointF( inner_r * std::sin( DEG2RAD( 45.0 ) ), - inner_r * std::cos( DEG2RAD( 45.0 ) ) )
666 << QPointF( std::sin( DEG2RAD( 0 ) ), - std::cos( DEG2RAD( 0 ) ) );
667 return true;
668 }
669
671 {
672 const double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
673
674 polygon << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ) // 324
675 << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288 ) ) ) // 288
676 << QPointF( inner_r * std::sin( DEG2RAD( 252.0 ) ), - inner_r * std::cos( DEG2RAD( 252.0 ) ) ) // 252
677 << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) ) // 216
678 << QPointF( 0, inner_r ) // 180
679 << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) ) // 144
680 << QPointF( inner_r * std::sin( DEG2RAD( 108.0 ) ), - inner_r * std::cos( DEG2RAD( 108.0 ) ) ) // 108
681 << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) ) // 72
682 << QPointF( inner_r * std::sin( DEG2RAD( 36.0 ) ), - inner_r * std::cos( DEG2RAD( 36.0 ) ) ) // 36
683 << QPointF( 0, -1 )
684 << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ); // 324; // 0
685 return true;
686 }
687
689 polygon << QPointF( 0, -1 )
690 << QPointF( 0.5, -0.5 )
691 << QPointF( 0.25, -0.5 )
692 << QPointF( 0.25, 1 )
693 << QPointF( -0.25, 1 )
694 << QPointF( -0.25, -0.5 )
695 << QPointF( -0.5, -0.5 )
696 << QPointF( 0, -1 );
697 return true;
698
700 polygon << QPointF( 0, 0 ) << QPointF( -1, 1 ) << QPointF( -1, -1 ) << QPointF( 0, 0 );
701 return true;
702
704 polygon << QPointF( -1, -0.2 )
705 << QPointF( -1, -0.2 )
706 << QPointF( -1, 0.2 )
707 << QPointF( -0.2, 0.2 )
708 << QPointF( -0.2, 1 )
709 << QPointF( 0.2, 1 )
710 << QPointF( 0.2, 0.2 )
711 << QPointF( 1, 0.2 )
712 << QPointF( 1, -0.2 )
713 << QPointF( 0.2, -0.2 )
714 << QPointF( 0.2, -1 )
715 << QPointF( -0.2, -1 )
716 << QPointF( -0.2, -0.2 )
717 << QPointF( -1, -0.2 );
718 return true;
719
721 {
722 static constexpr double THICKNESS = 0.3;
723 static constexpr double HALF_THICKNESS = THICKNESS / 2.0;
724 static constexpr double INTERSECTION_POINT = THICKNESS / M_SQRT2;
725 static constexpr double DIAGONAL1 = M_SQRT1_2 - INTERSECTION_POINT * 0.5;
726 static constexpr double DIAGONAL2 = M_SQRT1_2 + INTERSECTION_POINT * 0.5;
727
728 polygon << QPointF( -HALF_THICKNESS, -1 )
729 << QPointF( HALF_THICKNESS, -1 )
730 << QPointF( HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
731 << QPointF( DIAGONAL1, -DIAGONAL2 )
732 << QPointF( DIAGONAL2, -DIAGONAL1 )
733 << QPointF( HALF_THICKNESS + INTERSECTION_POINT, -HALF_THICKNESS )
734 << QPointF( 1, -HALF_THICKNESS )
735 << QPointF( 1, HALF_THICKNESS )
736 << QPointF( HALF_THICKNESS + INTERSECTION_POINT, HALF_THICKNESS )
737 << QPointF( DIAGONAL2, DIAGONAL1 )
738 << QPointF( DIAGONAL1, DIAGONAL2 )
739 << QPointF( HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
740 << QPointF( HALF_THICKNESS, 1 )
741 << QPointF( -HALF_THICKNESS, 1 )
742 << QPointF( -HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
743 << QPointF( -DIAGONAL1, DIAGONAL2 )
744 << QPointF( -DIAGONAL2, DIAGONAL1 )
745 << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, HALF_THICKNESS )
746 << QPointF( -1, HALF_THICKNESS )
747 << QPointF( -1, -HALF_THICKNESS )
748 << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, -HALF_THICKNESS )
749 << QPointF( -DIAGONAL2, -DIAGONAL1 )
750 << QPointF( -DIAGONAL1, -DIAGONAL2 )
751 << QPointF( -HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
752 << QPointF( -HALF_THICKNESS, -1 );
753 return true;
754 }
755
769 return false;
770 }
771
772 return false;
773}
774
776{
777 mPath = QPainterPath();
778
779 switch ( symbol )
780 {
782
783 mPath.addEllipse( QRectF( -1, -1, 2, 2 ) ); // x,y,w,h
784 return true;
785
787 mPath.moveTo( -1, -1 );
788 mPath.addRoundedRect( -1, -1, 2, 2, 0.25, 0.25 );
789 return true;
790
792 mPath.arcTo( -1, -1, 2, 2, 0, 180 );
793 mPath.lineTo( 0, 0 );
794 return true;
795
797 mPath.arcTo( -1, -1, 2, 2, 90, 120 );
798 mPath.lineTo( 0, 0 );
799 return true;
800
802 mPath.arcTo( -1, -1, 2, 2, 90, 90 );
803 mPath.lineTo( 0, 0 );
804 return true;
805
807 mPath.moveTo( 1, 0 );
808 mPath.arcTo( -1, -1, 2, 2, 0, 180 );
809 return true;
810
812 mPath.moveTo( 0, -1 );
813 mPath.arcTo( -1, -1, 2, 2, 90, 120 );
814 return true;
815
817 mPath.moveTo( 0, -1 );
818 mPath.arcTo( -1, -1, 2, 2, 90, 90 );
819 return true;
820
822 mPath.moveTo( -1, 0 );
823 mPath.lineTo( 1, 0 ); // horizontal
824 mPath.moveTo( 0, -1 );
825 mPath.lineTo( 0, 1 ); // vertical
826 return true;
827
829 mPath.moveTo( -1, -1 );
830 mPath.lineTo( 1, 1 );
831 mPath.moveTo( 1, -1 );
832 mPath.lineTo( -1, 1 );
833 return true;
834
836 mPath.moveTo( 0, -1 );
837 mPath.lineTo( 0, 1 ); // vertical line
838 return true;
839
841 mPath.moveTo( -1, -1 );
842 mPath.lineTo( 0, 0 );
843 mPath.lineTo( -1, 1 );
844 return true;
845
847 mPath.moveTo( 0, 0.75 );
848 mPath.arcTo( 0, -1, 1, 1, -45, 210 );
849 mPath.arcTo( -1, -1, 1, 1, 15, 210 );
850 mPath.lineTo( 0, 0.75 );
851 return true;
852
877 return false;
878 }
879 return false;
880}
881
882double QgsSimpleMarkerSymbolLayerBase::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
883{
884 double scaledSize = mSize;
885
887 bool ok = true;
888 if ( hasDataDefinedSize )
889 {
892 mSize, &ok );
893 }
894
895 if ( hasDataDefinedSize && ok )
896 {
897 switch ( mScaleMethod )
898 {
900 scaledSize = std::sqrt( scaledSize );
901 break;
903 break;
904 }
905 }
906
907 return scaledSize;
908}
909
910void QgsSimpleMarkerSymbolLayerBase::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle ) const
911{
912 //offset
913 double offsetX = 0;
914 double offsetY = 0;
915 markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
916 offset = QPointF( offsetX, offsetY );
917
918 hasDataDefinedRotation = false;
919 //angle
920 bool ok = true;
923 {
926
927 // If the expression evaluation was not successful, fallback to static value
928 if ( !ok )
930
931 hasDataDefinedRotation = true;
932 }
933
934 hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || hasDataDefinedRotation;
935
936 if ( hasDataDefinedRotation )
937 {
938 // For non-point markers, "dataDefinedRotation" means following the
939 // shape (shape-data defined). For them, "field-data defined" does
940 // not work at all. TODO: if "field-data defined" ever gets implemented
941 // we'll need a way to distinguish here between the two, possibly
942 // using another flag in renderHints()
943 const QgsFeature *f = context.feature();
944 if ( f )
945 {
946 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
947 {
948 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
949 angle += m2p.mapRotation();
950 }
951 }
952 }
953
954 if ( angle )
956}
957
958
959//
960// QgsSimpleMarkerSymbolLayer
961//
962
963QgsSimpleMarkerSymbolLayer::QgsSimpleMarkerSymbolLayer( Qgis::MarkerShape shape, double size, double angle, Qgis::ScaleMethod scaleMethod, const QColor &color, const QColor &strokeColor, Qt::PenJoinStyle penJoinStyle )
964 : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
965 , mStrokeColor( strokeColor )
966 , mPenJoinStyle( penJoinStyle )
967{
968 mColor = color;
969}
970
972
974{
982
983 if ( props.contains( QStringLiteral( "name" ) ) )
984 {
985 shape = decodeShape( props[QStringLiteral( "name" )].toString() );
986 }
987 if ( props.contains( QStringLiteral( "color" ) ) )
988 color = QgsColorUtils::colorFromString( props[QStringLiteral( "color" )].toString() );
989 if ( props.contains( QStringLiteral( "color_border" ) ) )
990 {
991 //pre 2.5 projects use "color_border"
992 strokeColor = QgsColorUtils::colorFromString( props[QStringLiteral( "color_border" )].toString() );
993 }
994 else if ( props.contains( QStringLiteral( "outline_color" ) ) )
995 {
996 strokeColor = QgsColorUtils::colorFromString( props[QStringLiteral( "outline_color" )].toString() );
997 }
998 else if ( props.contains( QStringLiteral( "line_color" ) ) )
999 {
1000 strokeColor = QgsColorUtils::colorFromString( props[QStringLiteral( "line_color" )].toString() );
1001 }
1002 if ( props.contains( QStringLiteral( "joinstyle" ) ) )
1003 {
1004 penJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() );
1005 }
1006 if ( props.contains( QStringLiteral( "size" ) ) )
1007 size = props[QStringLiteral( "size" )].toDouble();
1008 if ( props.contains( QStringLiteral( "angle" ) ) )
1009 angle = props[QStringLiteral( "angle" )].toDouble();
1010 if ( props.contains( QStringLiteral( "scale_method" ) ) )
1011 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1012
1014 if ( props.contains( QStringLiteral( "offset" ) ) )
1015 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1016 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1017 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1018 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1019 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1020 if ( props.contains( QStringLiteral( "size_unit" ) ) )
1021 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1022 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1023 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1024
1025 if ( props.contains( QStringLiteral( "outline_style" ) ) )
1026 {
1027 m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "outline_style" )].toString() ) );
1028 }
1029 else if ( props.contains( QStringLiteral( "line_style" ) ) )
1030 {
1031 m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "line_style" )].toString() ) );
1032 }
1033 if ( props.contains( QStringLiteral( "outline_width" ) ) )
1034 {
1035 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
1036 }
1037 else if ( props.contains( QStringLiteral( "line_width" ) ) )
1038 {
1039 m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
1040 }
1041 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
1042 {
1043 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
1044 }
1045 if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
1046 {
1047 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
1048 }
1049 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
1050 {
1051 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
1052 }
1053
1054 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1055 {
1056 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1057 }
1058 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1059 {
1060 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1061 }
1062
1063 if ( props.contains( QStringLiteral( "cap_style" ) ) )
1064 {
1065 m->setPenCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( props[QStringLiteral( "cap_style" )].toString() ) );
1066 }
1067
1069
1070 return m;
1071}
1072
1073
1075{
1076 return QStringLiteral( "SimpleMarker" );
1077}
1078
1083
1085{
1087
1088 QColor brushColor = mColor;
1089 QColor penColor = mStrokeColor;
1090
1091 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
1092 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
1093
1094 mBrush = QBrush( brushColor );
1095 mPen = QPen( penColor );
1096 mPen.setStyle( mStrokeStyle );
1097 mPen.setCapStyle( mPenCapStyle );
1098 mPen.setJoinStyle( mPenJoinStyle );
1100
1101 QColor selBrushColor = context.renderContext().selectionColor();
1102 QColor selPenColor = selBrushColor == mColor ? selBrushColor : mStrokeColor;
1103 if ( context.opacity() < 1 && !SELECTION_IS_OPAQUE )
1104 {
1105 selBrushColor.setAlphaF( context.opacity() );
1106 selPenColor.setAlphaF( context.opacity() );
1107 }
1108 mSelBrush = QBrush( selBrushColor );
1109 mSelPen = QPen( selPenColor );
1110 mSelPen.setStyle( mStrokeStyle );
1112
1114 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
1115
1116 // use caching only when:
1117 // - size, rotation, shape, color, stroke color is not data-defined
1118 // - drawing to screen (not printer)
1119 mUsingCache = !hasDataDefinedRotation && !hasDataDefinedSize && !context.renderContext().forceVectorOutput()
1123
1124 if ( mUsingCache )
1125 mCachedOpacity = context.opacity();
1126
1127 if ( !shapeIsFilled( mShape ) )
1128 {
1129 // some markers can't be drawn as a polygon (circle, cross)
1130 // For these set the selected stroke color to the selected color
1131 mSelPen.setColor( selBrushColor );
1132 }
1133
1134
1135 if ( mUsingCache )
1136 {
1137 if ( !prepareCache( context ) )
1138 {
1139 mUsingCache = false;
1140 }
1141 }
1142 else
1143 {
1144 mCache = QImage();
1145 mSelCache = QImage();
1146 }
1147}
1148
1149
1151{
1152 double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
1153 const double deviceRatio = context.renderContext().devicePixelRatio();
1155 {
1156 // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
1157 // and clamp it to a reasonable range. It's the best we can do in this situation!
1158 scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
1159 }
1160
1161 // take into account angle (which is not data-defined otherwise cache wouldn't be used)
1162 if ( !qgsDoubleNear( mAngle, 0.0 ) )
1163 {
1164 scaledSize = ( std::abs( std::sin( mAngle * M_PI / 180 ) ) + std::abs( std::cos( mAngle * M_PI / 180 ) ) ) * scaledSize;
1165 }
1166 // calculate necessary image size for the cache
1167 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
1168 const int imageSize = ( static_cast< int >( scaledSize ) + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width
1169 const double center = imageSize / 2.0;
1170 if ( imageSize * deviceRatio > MAXIMUM_CACHE_WIDTH )
1171 {
1172 return false;
1173 }
1174
1175 mCache = QImage( QSize( imageSize * deviceRatio,
1176 imageSize * deviceRatio ), QImage::Format_ARGB32_Premultiplied );
1177 mCache.setDevicePixelRatio( context.renderContext().devicePixelRatio() );
1178 mCache.setDotsPerMeterX( std::round( context.renderContext().scaleFactor() * 1000 ) );
1179 mCache.setDotsPerMeterY( std::round( context.renderContext().scaleFactor() * 1000 ) );
1180 mCache.fill( 0 );
1181
1182 const bool needsBrush = shapeIsFilled( mShape );
1183
1184 QPainter p;
1185 p.begin( &mCache );
1186 p.setRenderHint( QPainter::Antialiasing );
1187 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1188 p.setPen( mPen );
1189 p.translate( QPointF( center, center ) );
1190 drawMarker( &p, context );
1191 p.end();
1192
1193 // Construct the selected version of the Cache
1194
1195 const QColor selColor = context.renderContext().selectionColor();
1196
1197 mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
1198 mSelCache.fill( 0 );
1199
1200 p.begin( &mSelCache );
1201 p.setRenderHint( QPainter::Antialiasing );
1202 p.setBrush( needsBrush ? mSelBrush : Qt::NoBrush );
1203 p.setPen( mSelPen );
1204 p.translate( QPointF( center, center ) );
1205 drawMarker( &p, context );
1206 p.end();
1207
1208 // Check that the selected version is different. If not, then re-render,
1209 // filling the background with the selection color and using the normal
1210 // colors for the symbol .. could be ugly!
1211
1212 if ( mSelCache == mCache )
1213 {
1214 p.begin( &mSelCache );
1215 p.setRenderHint( QPainter::Antialiasing );
1216 p.fillRect( 0, 0, imageSize, imageSize, selColor );
1217 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1218 p.setPen( mPen );
1219 p.translate( QPointF( center, center ) );
1220 drawMarker( &p, context );
1221 p.end();
1222 }
1223
1224 return true;
1225}
1226
1227void QgsSimpleMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
1228{
1229 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1230 //of the rendered point!
1231
1232 QPainter *p = context.renderContext().painter();
1233 if ( !p )
1234 {
1235 return;
1236 }
1237
1238 QColor brushColor = mColor;
1239 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
1240 mBrush.setColor( brushColor );
1241
1242 QColor penColor = mStrokeColor;
1243 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
1244 mPen.setColor( penColor );
1245
1246 bool ok = true;
1248 {
1251 if ( ok )
1252 {
1253 c.setAlphaF( c.alphaF() * context.opacity() );
1254 mBrush.setColor( c );
1255 }
1256 }
1258 {
1261 if ( ok )
1262 {
1263 c.setAlphaF( c.alphaF() * context.opacity() );
1264 mPen.setColor( c );
1265 mSelPen.setColor( c );
1266 }
1267 }
1269 {
1272 if ( ok )
1273 {
1276 }
1277 }
1279 {
1282 if ( ok )
1283 {
1286 }
1287 }
1289 {
1292 if ( ok )
1293 {
1294 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1295 mSelPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1296 }
1297 }
1299 {
1302 if ( ok )
1303 {
1304 mPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1305 mSelPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1306 }
1307 }
1308
1309 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
1310 if ( shapeIsFilled( shape ) )
1311 {
1312 p->setBrush( useSelectedColor ? mSelBrush : mBrush );
1313 }
1314 else
1315 {
1316 p->setBrush( Qt::NoBrush );
1317 }
1318 p->setPen( useSelectedColor ? mSelPen : mPen );
1319
1320 if ( !polygon.isEmpty() )
1321 p->drawPolygon( polygon );
1322 else
1323 p->drawPath( path );
1324}
1325
1327{
1328 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1329 //of the rendered point!
1330
1331 QPainter *p = context.renderContext().painter();
1332 if ( !p )
1333 {
1334 return;
1335 }
1336
1337 if ( mUsingCache && qgsDoubleNear( mCachedOpacity, context.opacity() ) )
1338 {
1339 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
1340 const QImage &img = useSelectedColor ? mSelCache : mCache;
1341 const double s = img.width() / img.devicePixelRatioF();
1342
1343 bool hasDataDefinedSize = false;
1344 const double scaledSize = calculateSize( context, hasDataDefinedSize );
1345
1346 bool hasDataDefinedRotation = false;
1347 QPointF offset;
1348 double angle = 0;
1349 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
1350
1351 p->drawImage( QRectF( point.x() - s / 2.0 + offset.x(),
1352 point.y() - s / 2.0 + offset.y(),
1353 s, s ), img );
1354 }
1355 else
1356 {
1358 }
1359}
1360
1362{
1363 QVariantMap map;
1364 map[QStringLiteral( "name" )] = encodeShape( mShape );
1365 map[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mColor );
1366 map[QStringLiteral( "outline_color" )] = QgsColorUtils::colorToString( mStrokeColor );
1367 map[QStringLiteral( "size" )] = QString::number( mSize );
1368 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1369 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1370 map[QStringLiteral( "angle" )] = QString::number( mAngle );
1371 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1372 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1373 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1374 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1375 map[QStringLiteral( "outline_style" )] = QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle );
1376 map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
1377 map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
1378 map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
1379 map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
1380 map[QStringLiteral( "cap_style" )] = QgsSymbolLayerUtils::encodePenCapStyle( mPenCapStyle );
1381 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1382 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1383 return map;
1384}
1385
1405
1406void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1407{
1408 // <Graphic>
1409 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
1410 element.appendChild( graphicElem );
1411
1413 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
1415
1416 // <Rotation>
1417 QString angleFunc;
1418
1420 {
1422 }
1423 else
1424 {
1425 bool ok;
1426 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
1427 if ( !ok )
1428 {
1429 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
1430 }
1431 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
1432 {
1433 angleFunc = QString::number( angle + mAngle );
1434 }
1435 }
1436
1437 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
1438
1439 // <Displacement>
1440 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
1442}
1443
1444QString QgsSimpleMarkerSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
1445{
1446 Q_UNUSED( mmScaleFactor )
1447 Q_UNUSED( mapUnitScaleFactor )
1448#if 0
1449 QString ogrType = "3"; //default is circle
1450 if ( mName == "square" )
1451 {
1452 ogrType = "5";
1453 }
1454 else if ( mName == "triangle" )
1455 {
1456 ogrType = "7";
1457 }
1458 else if ( mName == "star" )
1459 {
1460 ogrType = "9";
1461 }
1462 else if ( mName == "circle" )
1463 {
1464 ogrType = "3";
1465 }
1466 else if ( mName == "cross" )
1467 {
1468 ogrType = "0";
1469 }
1470 else if ( mName == "x" || mName == "cross2" )
1471 {
1472 ogrType = "1";
1473 }
1474 else if ( mName == "line" )
1475 {
1476 ogrType = "10";
1477 }
1478
1479 QString ogrString;
1480 ogrString.append( "SYMBOL(" );
1481 ogrString.append( "id:" );
1482 ogrString.append( '\"' );
1483 ogrString.append( "ogr-sym-" );
1484 ogrString.append( ogrType );
1485 ogrString.append( '\"' );
1486 ogrString.append( ",c:" );
1487 ogrString.append( mColor.name() );
1488 ogrString.append( ",o:" );
1489 ogrString.append( mStrokeColor.name() );
1490 ogrString.append( QString( ",s:%1mm" ).arg( mSize ) );
1491 ogrString.append( ')' );
1492 return ogrString;
1493#endif //0
1494
1495 QString ogrString;
1496 ogrString.append( "PEN(" );
1497 ogrString.append( "c:" );
1498 ogrString.append( mColor.name() );
1499 ogrString.append( ",w:" );
1500 ogrString.append( QString::number( mSize ) );
1501 ogrString.append( "mm" );
1502 ogrString.append( ")" );
1503 return ogrString;
1504}
1505
1507{
1508 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
1509
1510 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
1511 if ( graphicElem.isNull() )
1512 return nullptr;
1513
1514 QString name = QStringLiteral( "square" );
1515 QColor color, strokeColor;
1516 double strokeWidth, size;
1517 Qt::PenStyle strokeStyle;
1518
1520 return nullptr;
1521
1522 double angle = 0.0;
1523 QString angleFunc;
1524 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
1525 {
1526 bool ok;
1527 const double d = angleFunc.toDouble( &ok );
1528 if ( ok )
1529 angle = d;
1530 }
1531
1532 QPointF offset;
1534
1535 const Qgis::MarkerShape shape = decodeShape( name );
1536
1537 double scaleFactor = 1.0;
1538 const QString uom = element.attribute( QStringLiteral( "uom" ) );
1539 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
1540 size = size * scaleFactor;
1541 offset.setX( offset.x() * scaleFactor );
1542 offset.setY( offset.y() * scaleFactor );
1543
1545 m->setOutputUnit( sldUnitSize );
1546 m->setColor( color );
1548 m->setAngle( angle );
1549 m->setOffset( offset );
1552 return m;
1553}
1554
1556{
1557 Q_UNUSED( context )
1558
1559 if ( mPolygon.count() != 0 )
1560 {
1561 p->drawPolygon( mPolygon );
1562 }
1563 else
1564 {
1565 p->drawPath( mPath );
1566 }
1567}
1568
1569bool QgsSimpleMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
1570{
1571 //data defined size?
1572 double size = mSize;
1573
1574 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
1575
1576 //data defined size
1577 bool ok = true;
1578 if ( hasDataDefinedSize )
1579 {
1581
1582 if ( ok )
1583 {
1584 switch ( mScaleMethod )
1585 {
1587 size = std::sqrt( size );
1588 break;
1590 break;
1591 }
1592 }
1593 }
1594
1596 {
1597 size *= mmMapUnitScaleFactor;
1598 }
1599
1601 {
1603 }
1604 const double halfSize = size / 2.0;
1605
1606 //strokeWidth
1607 double strokeWidth = mStrokeWidth;
1608
1610 {
1613 }
1616 {
1618 }
1619
1620 //color
1621 QColor pc = mPen.color();
1622 QColor bc = mBrush.color();
1624 {
1627 }
1629 {
1632 }
1633
1634 //offset
1635 double offsetX = 0;
1636 double offsetY = 0;
1637 markerOffset( context, offsetX, offsetY );
1638 offsetX *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1639 offsetY *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1640
1641
1642 QPointF off( offsetX, offsetY );
1643
1644 //angle
1645 double angle = mAngle + mLineAngle;
1647 {
1650 }
1651
1654 {
1656 const QString shapeName = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::Name, context.renderContext().expressionContext(), QString(), &ok );
1657 if ( ok )
1658 {
1659 shape = decodeShape( shapeName, &ok );
1660 if ( !ok )
1661 shape = mShape;
1662 }
1663 }
1664
1665 if ( angle )
1666 off = _rotatedOffset( off, angle );
1667
1669
1670 QTransform t;
1671 t.translate( shift.x() + off.x(), shift.y() - off.y() );
1672
1673 if ( !qgsDoubleNear( angle, 0.0 ) )
1674 t.rotate( -angle );
1675
1676 QPolygonF polygon;
1677 if ( shapeToPolygon( shape, polygon ) )
1678 {
1679 t.scale( halfSize, -halfSize );
1680
1681 polygon = t.map( polygon );
1682
1684 p.reserve( polygon.size() );
1685 for ( int i = 0; i < polygon.size(); i++ )
1686 {
1687 p << QgsPoint( polygon[i] );
1688 }
1689
1690 if ( mBrush.style() != Qt::NoBrush )
1691 e.writePolygon( QgsRingSequence() << p, layerName, QStringLiteral( "SOLID" ), bc );
1692 if ( mPen.style() != Qt::NoPen )
1693 e.writePolyline( p, layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1694 }
1695 else if ( shape == Qgis::MarkerShape::Circle )
1696 {
1697 shift += QPointF( off.x(), -off.y() );
1698 if ( mBrush.style() != Qt::NoBrush )
1699 e.writeFilledCircle( layerName, bc, QgsPoint( shift ), halfSize );
1700 if ( mPen.style() != Qt::NoPen )
1701 e.writeCircle( layerName, pc, QgsPoint( shift ), halfSize, QStringLiteral( "CONTINUOUS" ), strokeWidth );
1702 }
1703 else if ( shape == Qgis::MarkerShape::Line )
1704 {
1705 const QPointF pt1 = t.map( QPointF( 0, -halfSize ) );
1706 const QPointF pt2 = t.map( QPointF( 0, halfSize ) );
1707
1708 if ( mPen.style() != Qt::NoPen )
1709 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1710 }
1711 else if ( shape == Qgis::MarkerShape::Cross )
1712 {
1713 if ( mPen.style() != Qt::NoPen )
1714 {
1715 const QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
1716 const QPointF pt2 = t.map( QPointF( halfSize, 0 ) );
1717 const QPointF pt3 = t.map( QPointF( 0, -halfSize ) );
1718 const QPointF pt4 = t.map( QPointF( 0, halfSize ) );
1719
1720 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1721 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1722 }
1723 }
1724 else if ( shape == Qgis::MarkerShape::Cross2 )
1725 {
1726 if ( mPen.style() != Qt::NoPen )
1727 {
1728 const QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
1729 const QPointF pt2 = t.map( QPointF( halfSize, halfSize ) );
1730 const QPointF pt3 = t.map( QPointF( halfSize, -halfSize ) );
1731 const QPointF pt4 = t.map( QPointF( -halfSize, halfSize ) );
1732
1733 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1734 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1735 }
1736 }
1737 else if ( shape == Qgis::MarkerShape::ArrowHead )
1738 {
1739 if ( mPen.style() != Qt::NoPen )
1740 {
1741 const QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
1742 const QPointF pt2 = t.map( QPointF( 0, 0 ) );
1743 const QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
1744
1745 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1746 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1747 }
1748 }
1749 else
1750 {
1751 QgsDebugError( QStringLiteral( "Unsupported dxf marker name %1" ).arg( encodeShape( shape ) ) );
1752 return false;
1753 }
1754
1755 return true;
1756}
1757
1758
1764
1773
1779
1788
1795
1797{
1798 QRectF symbolBounds = QgsSimpleMarkerSymbolLayerBase::bounds( point, context );
1799
1800 // need to account for stroke width
1801 double penWidth = mStrokeWidth;
1802 bool ok = true;
1804 {
1807 if ( ok )
1808 {
1809 penWidth = strokeWidth;
1810 }
1811 }
1814 {
1817 if ( ok && strokeStyle == QLatin1String( "no" ) )
1818 {
1819 penWidth = 0.0;
1820 }
1821 }
1822 else if ( mStrokeStyle == Qt::NoPen )
1823 penWidth = 0;
1824
1825 //antialiasing, add 1 pixel
1826 penWidth += 1;
1827
1828 //extend bounds by pen width / 2.0
1829 symbolBounds.adjust( -penWidth / 2.0, -penWidth / 2.0,
1830 penWidth / 2.0, penWidth / 2.0 );
1831
1832 return symbolBounds;
1833}
1834
1835void QgsSimpleMarkerSymbolLayer::setColor( const QColor &color )
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
1862//
1863// QgsFilledMarkerSymbolLayer
1864//
1865
1867 : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
1868{
1869 mFill.reset( static_cast<QgsFillSymbol *>( QgsFillSymbol::createSimple( QVariantMap() ) ) );
1870}
1871
1873
1875{
1876 QString name = DEFAULT_SIMPLEMARKER_NAME;
1880
1881 if ( props.contains( QStringLiteral( "name" ) ) )
1882 name = props[QStringLiteral( "name" )].toString();
1883 if ( props.contains( QStringLiteral( "size" ) ) )
1884 size = props[QStringLiteral( "size" )].toDouble();
1885 if ( props.contains( QStringLiteral( "angle" ) ) )
1886 angle = props[QStringLiteral( "angle" )].toDouble();
1887 if ( props.contains( QStringLiteral( "scale_method" ) ) )
1888 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1889
1891 if ( props.contains( QStringLiteral( "offset" ) ) )
1892 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1893 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1894 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1895 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1896 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1897 if ( props.contains( QStringLiteral( "size_unit" ) ) )
1898 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1899 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1900 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1901 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1902 {
1903 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1904 }
1905 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1906 {
1907 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1908 }
1909
1911
1913
1914 return m;
1915}
1916
1918{
1919 return QStringLiteral( "FilledMarker" );
1920}
1921
1923{
1924 if ( mFill )
1925 {
1926 mFill->setRenderHints( mFill->renderHints() | Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol );
1927 mFill->startRender( context.renderContext(), context.fields() );
1928 }
1929
1931}
1932
1934{
1935 if ( mFill )
1936 {
1937 mFill->stopRender( context.renderContext() );
1938 }
1939}
1940
1942{
1943 QVariantMap map;
1944 map[QStringLiteral( "name" )] = encodeShape( mShape );
1945 map[QStringLiteral( "size" )] = QString::number( mSize );
1946 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1947 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1948 map[QStringLiteral( "angle" )] = QString::number( mAngle );
1949 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1950 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1951 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1952 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1953 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1954 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1955
1956 if ( mFill )
1957 {
1958 map[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mFill->color() );
1959 }
1960 return map;
1961}
1962
1971
1973{
1974 return mFill.get();
1975}
1976
1978{
1979 if ( symbol && symbol->type() == Qgis::SymbolType::Fill )
1980 {
1981 mFill.reset( static_cast<QgsFillSymbol *>( symbol ) );
1982 return true;
1983 }
1984 else
1985 {
1986 delete symbol;
1987 return false;
1988 }
1989}
1990
1992{
1993 if ( mFill )
1994 {
1995 return QgsSymbolLayerUtils::estimateMaxSymbolBleed( mFill.get(), context );
1996 }
1997 return 0;
1998}
1999
2001{
2002 QSet<QString> attr = QgsSimpleMarkerSymbolLayerBase::usedAttributes( context );
2003 if ( mFill )
2004 attr.unite( mFill->usedAttributes( context ) );
2005 return attr;
2006}
2007
2009{
2011 return true;
2012 if ( mFill && mFill->hasDataDefinedProperties() )
2013 return true;
2014 return false;
2015}
2016
2018{
2019 mColor = c;
2020 if ( mFill )
2021 mFill->setColor( c );
2022}
2023
2025{
2026 return mFill ? mFill->color() : mColor;
2027}
2028
2035
2037{
2039 if ( mFill )
2040 mFill->setOutputUnit( unit );
2041}
2042
2043void QgsFilledMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
2044{
2045 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
2046 //of the rendered point!
2047
2048 QPainter *p = context.renderContext().painter();
2049 if ( !p )
2050 {
2051 return;
2052 }
2053
2054 const double prevOpacity = mFill->opacity();
2055 mFill->setOpacity( mFill->opacity() * context.opacity() );
2056
2057 if ( shapeIsFilled( shape ) )
2058 {
2059 p->setBrush( Qt::red );
2060 }
2061 else
2062 {
2063 p->setBrush( Qt::NoBrush );
2064 }
2065 p->setPen( Qt::black );
2066
2067 const bool prevIsSubsymbol = context.renderContext().flags() & Qgis::RenderContextFlag::RenderingSubSymbol;
2069
2070 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
2071 if ( !polygon.isEmpty() )
2072 {
2073 mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
2074 }
2075 else
2076 {
2077 const QPolygonF poly = path.toFillPolygon();
2078 mFill->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
2079 }
2080
2082
2083 mFill->setOpacity( prevOpacity );
2084}
2085
2086
2088
2089
2090QgsSvgMarkerSymbolLayer::QgsSvgMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
2091{
2092 mSize = size;
2093 mAngle = angle;
2094 mOffset = QPointF( 0, 0 );
2096 mStrokeWidth = 0.2;
2098 mColor = QColor( 35, 35, 35 );
2099 mStrokeColor = QColor( 35, 35, 35 );
2100 setPath( path );
2101}
2102
2104
2106{
2107 QString name;
2111
2112 if ( props.contains( QStringLiteral( "name" ) ) )
2113 name = props[QStringLiteral( "name" )].toString();
2114 if ( props.contains( QStringLiteral( "size" ) ) )
2115 size = props[QStringLiteral( "size" )].toDouble();
2116 if ( props.contains( QStringLiteral( "angle" ) ) )
2117 angle = props[QStringLiteral( "angle" )].toDouble();
2118 if ( props.contains( QStringLiteral( "scale_method" ) ) )
2119 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
2120
2122
2123 if ( props.contains( QStringLiteral( "size_unit" ) ) )
2124 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
2125 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
2126 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
2127 if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
2128 m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
2129 if ( props.contains( QStringLiteral( "offset" ) ) )
2130 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
2131 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
2132 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
2133 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2134 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
2135 if ( props.contains( QStringLiteral( "fill" ) ) )
2136 {
2137 //pre 2.5 projects used "fill"
2138 m->setFillColor( QgsColorUtils::colorFromString( props[QStringLiteral( "fill" )].toString() ) );
2139 }
2140 else if ( props.contains( QStringLiteral( "color" ) ) )
2141 {
2142 m->setFillColor( QgsColorUtils::colorFromString( props[QStringLiteral( "color" )].toString() ) );
2143 }
2144 if ( props.contains( QStringLiteral( "outline" ) ) )
2145 {
2146 //pre 2.5 projects used "outline"
2147 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "outline" )].toString() ) );
2148 }
2149 else if ( props.contains( QStringLiteral( "outline_color" ) ) )
2150 {
2151 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "outline_color" )].toString() ) );
2152 }
2153 else if ( props.contains( QStringLiteral( "line_color" ) ) )
2154 {
2155 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "line_color" )].toString() ) );
2156 }
2157
2158 if ( props.contains( QStringLiteral( "outline-width" ) ) )
2159 {
2160 //pre 2.5 projects used "outline-width"
2161 m->setStrokeWidth( props[QStringLiteral( "outline-width" )].toDouble() );
2162 }
2163 else if ( props.contains( QStringLiteral( "outline_width" ) ) )
2164 {
2165 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
2166 }
2167 else if ( props.contains( QStringLiteral( "line_width" ) ) )
2168 {
2169 m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
2170 }
2171
2172 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
2173 {
2174 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
2175 }
2176 else if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
2177 {
2178 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
2179 }
2180 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
2181 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
2182
2183 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2184 {
2185 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2186 }
2187 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2188 {
2189 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2190 }
2191
2193
2195
2196 if ( props.contains( QStringLiteral( "parameters" ) ) )
2197 {
2198 const QVariantMap parameters = props[QStringLiteral( "parameters" )].toMap();
2200 }
2201
2202 return m;
2203}
2204
2205void QgsSvgMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
2206{
2207 const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
2208 if ( it != properties.end() )
2209 {
2210 if ( saving )
2211 {
2212 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
2213 }
2214 else
2215 {
2216 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
2217 }
2218 }
2219}
2220
2221void QgsSvgMarkerSymbolLayer::setPath( const QString &path )
2222{
2224 mHasFillParam = false;
2225 mPath = path;
2226 QColor defaultFillColor, defaultStrokeColor;
2227 double strokeWidth, fillOpacity, strokeOpacity;
2228 bool hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
2229 bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
2230 QgsApplication::svgCache()->containsParams( path, mHasFillParam, hasDefaultFillColor, defaultFillColor,
2231 hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
2232 hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2233 hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
2234 hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
2235
2236 const double newFillOpacity = hasFillOpacityParam ? fillColor().alphaF() : 1.0;
2237 const double newStrokeOpacity = hasStrokeOpacityParam ? strokeColor().alphaF() : 1.0;
2238
2239 if ( hasDefaultFillColor )
2240 {
2241 defaultFillColor.setAlphaF( newFillOpacity );
2242 setFillColor( defaultFillColor );
2243 }
2244 if ( hasDefaultFillOpacity )
2245 {
2246 QColor c = fillColor();
2247 c.setAlphaF( fillOpacity );
2248 setFillColor( c );
2249 }
2250 if ( hasDefaultStrokeColor )
2251 {
2252 defaultStrokeColor.setAlphaF( newStrokeOpacity );
2253 setStrokeColor( defaultStrokeColor );
2254 }
2255 if ( hasDefaultStrokeWidth )
2256 {
2258 }
2259 if ( hasDefaultStrokeOpacity )
2260 {
2261 QColor c = strokeColor();
2262 c.setAlphaF( strokeOpacity );
2263 setStrokeColor( c );
2264 }
2265
2267}
2268
2270{
2271 if ( mDefaultAspectRatio == 0.0 )
2272 {
2273 //size
2274 const double size = mSize;
2275 //assume 88 dpi as standard value
2276 const double widthScaleFactor = 3.465;
2277 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( mPath, size, mColor, mStrokeColor, mStrokeWidth, widthScaleFactor );
2278 // set default aspect ratio
2279 mDefaultAspectRatio = svgViewbox.isValid() ? svgViewbox.height() / svgViewbox.width() : 0.0;
2280 }
2281 return mDefaultAspectRatio;
2282}
2283
2285{
2286 const bool aPreservedAspectRatio = preservedAspectRatio();
2287 if ( aPreservedAspectRatio && !par )
2288 {
2290 }
2291 else if ( !aPreservedAspectRatio && par )
2292 {
2293 mFixedAspectRatio = 0.0;
2294 }
2295 return preservedAspectRatio();
2296}
2297
2298void QgsSvgMarkerSymbolLayer::setParameters( const QMap<QString, QgsProperty> &parameters )
2299{
2301}
2302
2303
2305{
2306 return QStringLiteral( "SvgMarker" );
2307}
2308
2313
2315{
2316 QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
2317 Q_UNUSED( context )
2318}
2319
2321{
2322 Q_UNUSED( context )
2323}
2324
2326{
2327 QPainter *p = context.renderContext().painter();
2328 if ( !p )
2329 return;
2330
2331 bool hasDataDefinedSize = false;
2332 const double scaledWidth = calculateSize( context, hasDataDefinedSize );
2333 const double devicePixelRatio = context.renderContext().devicePixelRatio();
2334 const double width = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2335
2336 //don't render symbols with a width below one or above 10,000 pixels
2337 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2338 {
2339 return;
2340 }
2341
2342 const QgsScopedQPainterState painterState( p );
2343
2344 bool hasDataDefinedAspectRatio = false;
2345 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2346 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2347
2349
2350 double strokeWidth = mStrokeWidth;
2352 {
2355 }
2357
2358 QColor fillColor = mColor;
2359 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
2360 if ( useSelectedColor && mHasFillParam )
2361 {
2363 }
2365 {
2368 }
2369
2370 QColor strokeColor = mStrokeColor;
2372 {
2375 }
2376
2377 QString path = mPath;
2379 {
2382 context.renderContext().pathResolver() );
2384 {
2385 // adjust height of data defined path
2386 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2387 context.renderContext().scaleFactor(), aspectRatio,
2388 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2389 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2390 }
2391 }
2392
2393 QPointF outputOffset;
2394 double angle = 0.0;
2395 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2396
2397 p->translate( point + outputOffset );
2398
2399 const bool rotated = !qgsDoubleNear( angle, 0 );
2400 if ( rotated )
2401 p->rotate( angle );
2402
2403 bool fitsInCache = true;
2404 bool usePict = true;
2406 if ( ( !context.renderContext().forceVectorOutput() && !rotated ) || ( useSelectedColor && rasterizeSelected ) )
2407 {
2408 QImage img = QgsApplication::svgCache()->svgAsImage( path, width * devicePixelRatio, fillColor, strokeColor, strokeWidth,
2409 context.renderContext().scaleFactor(), fitsInCache, aspectRatio,
2410 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2411 if ( fitsInCache && img.width() > 1 )
2412 {
2413 usePict = false;
2414
2415 if ( useSelectedColor )
2416 {
2418 }
2419
2420 //consider transparency
2421 if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2422 {
2423 QImage transparentImage = img.copy();
2424 QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2425 if ( devicePixelRatio == 1 )
2426 {
2427 p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
2428 }
2429 else
2430 {
2431 p->drawImage( QRectF( -transparentImage.width() / 2.0 / devicePixelRatio, -transparentImage.height() / 2.0 / devicePixelRatio,
2432 transparentImage.width() / devicePixelRatio, transparentImage.height() / devicePixelRatio
2433 ), transparentImage );
2434 }
2435 }
2436 else
2437 {
2438 if ( devicePixelRatio == 1 )
2439 {
2440 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2441 }
2442 else
2443 {
2444 p->drawImage( QRectF( -img.width() / 2.0 / devicePixelRatio, -img.height() / 2.0 / devicePixelRatio,
2445 img.width() / devicePixelRatio, img.height() / devicePixelRatio ), img );
2446 }
2447 }
2448 }
2449 }
2450
2451 if ( usePict || !fitsInCache )
2452 {
2453 p->setOpacity( context.opacity() );
2455 context.renderContext().scaleFactor(), context.renderContext().forceVectorOutput(), aspectRatio,
2456 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2457 if ( pct.width() > 1 )
2458 {
2459 QgsPainting::drawPicture( p, QPointF( 0, 0 ), pct );
2460 }
2461 }
2462
2463 // workaround issue with nested QPictures forgetting antialiasing flag - see https://github.com/qgis/QGIS/issues/22909
2465}
2466
2467double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2468{
2469 double scaledSize = mSize;
2471
2472 bool ok = true;
2473 if ( hasDataDefinedSize )
2474 {
2477 }
2478 else
2479 {
2481 if ( hasDataDefinedSize )
2482 {
2485 }
2486 }
2487
2488 if ( hasDataDefinedSize && ok )
2489 {
2490 switch ( mScaleMethod )
2491 {
2493 scaledSize = std::sqrt( scaledSize );
2494 break;
2496 break;
2497 }
2498 }
2499
2500 return scaledSize;
2501}
2502
2503double QgsSvgMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2504{
2506 if ( !hasDataDefinedAspectRatio )
2507 return mFixedAspectRatio;
2508
2510 return 0.0;
2511
2512 double scaledAspectRatio = mDefaultAspectRatio;
2513 if ( mFixedAspectRatio > 0.0 )
2514 scaledAspectRatio = mFixedAspectRatio;
2515
2516 const double defaultHeight = mSize * scaledAspectRatio;
2517 scaledAspectRatio = defaultHeight / scaledSize;
2518
2519 bool ok = true;
2520 double scaledHeight = scaledSize * scaledAspectRatio;
2522 {
2523 context.setOriginalValueVariable( defaultHeight );
2525 }
2526
2527 if ( hasDataDefinedAspectRatio && ok )
2528 {
2529 switch ( mScaleMethod )
2530 {
2532 scaledHeight = sqrt( scaledHeight );
2533 break;
2535 break;
2536 }
2537 }
2538
2539 scaledAspectRatio = scaledHeight / scaledSize;
2540
2541 return scaledAspectRatio;
2542}
2543
2544void QgsSvgMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
2545{
2546 //offset
2547 double offsetX = 0;
2548 double offsetY = 0;
2549 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
2550 offset = QPointF( offsetX, offsetY );
2551
2554 {
2557 }
2558
2560 if ( hasDataDefinedRotation )
2561 {
2562 // For non-point markers, "dataDefinedRotation" means following the
2563 // shape (shape-data defined). For them, "field-data defined" does
2564 // not work at all. TODO: if "field-data defined" ever gets implemented
2565 // we'll need a way to distinguish here between the two, possibly
2566 // using another flag in renderHints()
2567 const QgsFeature *f = context.feature();
2568 if ( f )
2569 {
2570 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
2571 {
2572 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2573 angle += m2p.mapRotation();
2574 }
2575 }
2576 }
2577
2578 if ( angle )
2580}
2581
2582
2584{
2585 QVariantMap map;
2586 map[QStringLiteral( "name" )] = mPath;
2587 map[QStringLiteral( "size" )] = QString::number( mSize );
2588 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2589 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2590 map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2591 map[QStringLiteral( "angle" )] = QString::number( mAngle );
2592 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2593 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2594 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2595 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2596 map[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mColor );
2597 map[QStringLiteral( "outline_color" )] = QgsColorUtils::colorToString( mStrokeColor );
2598 map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
2599 map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2600 map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2601 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
2602 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
2603
2604 map[QStringLiteral( "parameters" )] = QgsProperty::propertyMapToVariantMap( mParameters );
2605
2606 return map;
2607}
2608
2615
2638
2644
2646{
2648 if ( unit != mStrokeWidthUnit )
2649 {
2651 }
2652 return unit;
2653}
2654
2660
2669
2670void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
2671{
2672 // <Graphic>
2673 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2674 element.appendChild( graphicElem );
2675
2676 // encode a parametric SVG reference
2677 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
2680
2681 // <Rotation>
2682 QString angleFunc;
2683 bool ok;
2684 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2685 if ( !ok )
2686 {
2687 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
2688 }
2689 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2690 {
2691 angleFunc = QString::number( angle + mAngle );
2692 }
2693
2694 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2695
2696 // <Displacement>
2697 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2699}
2700
2702{
2703 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
2704
2705 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
2706 if ( graphicElem.isNull() )
2707 return nullptr;
2708
2709 QString path, mimeType;
2710 // Unused and to be DEPRECATED in externalGraphicFromSld
2711 QColor fillColor_;
2712 double size;
2713
2714 if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor_, size ) )
2715 return nullptr;
2716
2717 double scaleFactor = 1.0;
2718 const QString uom = element.attribute( QStringLiteral( "uom" ) );
2719 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
2720 size = size * scaleFactor;
2721
2722 if ( mimeType != QLatin1String( "image/svg+xml" ) )
2723 return nullptr;
2724
2725 double angle = 0.0;
2726 QString angleFunc;
2727 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2728 {
2729 bool ok;
2730 const double d = angleFunc.toDouble( &ok );
2731 if ( ok )
2732 angle = d;
2733 }
2734
2735 QPointF offset;
2737
2738 // Extract parameters from URL
2739 QString realPath { path };
2740 QUrl svgUrl { path };
2741
2742 // Because color definition can start with '#', the url parsing won't recognize the query string entirely
2743 QUrlQuery queryString;
2744
2745 if ( svgUrl.hasQuery() && svgUrl.hasFragment() )
2746 {
2747 const QString queryPart { path.mid( path.indexOf( '?' ) + 1 ) };
2748 queryString.setQuery( queryPart );
2749 }
2750
2751 // Remove query for simple file paths
2752 if ( svgUrl.scheme().isEmpty() || svgUrl.isLocalFile() )
2753 {
2754 svgUrl.setQuery( QString() );
2755 realPath = svgUrl.path();
2756 }
2757
2759
2760 QMap<QString, QgsProperty> params;
2761
2762 bool ok;
2763
2764 if ( queryString.hasQueryItem( QStringLiteral( "fill" ) ) )
2765 {
2766 const QColor fillColor { queryString.queryItemValue( QStringLiteral( "fill" ) ) };
2767 m->setFillColor( fillColor );
2768 }
2769
2770 if ( queryString.hasQueryItem( QStringLiteral( "fill-opacity" ) ) )
2771 {
2772 const double alpha { queryString.queryItemValue( QStringLiteral( "fill-opacity" ) ).toDouble( &ok ) };
2773 if ( ok )
2774 {
2775 params.insert( QStringLiteral( "fill-opacity" ), QgsProperty::fromValue( alpha ) );
2776 }
2777 }
2778
2779 if ( queryString.hasQueryItem( QStringLiteral( "outline" ) ) )
2780 {
2781 const QColor strokeColor { queryString.queryItemValue( QStringLiteral( "outline" ) ) };
2783 }
2784
2785 if ( queryString.hasQueryItem( QStringLiteral( "outline-opacity" ) ) )
2786 {
2787 const double alpha { queryString.queryItemValue( QStringLiteral( "outline-opacity" ) ).toDouble( &ok ) };
2788 if ( ok )
2789 {
2790 params.insert( QStringLiteral( "outline-opacity" ), QgsProperty::fromValue( alpha ) );
2791 }
2792 }
2793
2794 if ( queryString.hasQueryItem( QStringLiteral( "outline-width" ) ) )
2795 {
2796 const int width { queryString.queryItemValue( QStringLiteral( "outline-width" ) ).toInt( &ok )};
2797 if ( ok )
2798 {
2799 m->setStrokeWidth( width );
2800 }
2801 }
2802
2803 if ( ! params.isEmpty() )
2804 {
2805 m->setParameters( params );
2806 }
2807
2808 m->setOutputUnit( sldUnitSize );
2809 m->setAngle( angle );
2810 m->setOffset( offset );
2811 return m;
2812}
2813
2814bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
2815{
2816 //size
2817 double size = mSize;
2818
2819 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
2820
2821 bool ok = true;
2822 if ( hasDataDefinedSize )
2823 {
2826 }
2827
2828 if ( hasDataDefinedSize && ok )
2829 {
2830 switch ( mScaleMethod )
2831 {
2833 size = std::sqrt( size );
2834 break;
2836 break;
2837 }
2838 }
2839
2841 {
2842 size *= mmMapUnitScaleFactor;
2843 }
2844
2845//offset, angle
2846 QPointF offset = mOffset;
2847
2849 {
2852 const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
2853 if ( ok )
2854 offset = res;
2855 }
2856 const double offsetX = offset.x();
2857 const double offsetY = offset.y();
2858
2859 QPointF outputOffset( offsetX, offsetY );
2860
2861 double angle = mAngle + mLineAngle;
2863 {
2866 }
2867
2868 if ( angle )
2869 outputOffset = _rotatedOffset( outputOffset, angle );
2870
2872
2873 QString path = mPath;
2875 {
2878 context.renderContext().pathResolver() );
2879 }
2880
2881 double strokeWidth = mStrokeWidth;
2883 {
2886 }
2888
2889 QColor fillColor = mColor;
2891 {
2894 }
2895
2896 QColor strokeColor = mStrokeColor;
2898 {
2901 }
2902
2904
2905 const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, size, fillColor, strokeColor, strokeWidth,
2907 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2908
2909 QSvgRenderer r( svgContent );
2910 if ( !r.isValid() )
2911 return false;
2912
2913 QgsDxfPaintDevice pd( &e );
2914 pd.setDrawingSize( QSizeF( r.defaultSize() ) );
2915
2916 QSizeF outSize( r.defaultSize() );
2917 outSize.scale( size, size, Qt::KeepAspectRatio );
2918
2919 QPainter p;
2920 p.begin( &pd );
2921 if ( !qgsDoubleNear( angle, 0.0 ) )
2922 {
2923 p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
2924 p.rotate( angle );
2925 p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
2926 }
2927 pd.setShift( shift + QPointF( outputOffset.x(), -outputOffset.y() ) );
2928 pd.setOutputSize( QRectF( -outSize.width() / 2.0, -outSize.height() / 2.0, outSize.width(), outSize.height() ) );
2929 pd.setLayer( layerName );
2930 r.render( &p );
2931 p.end();
2932 return true;
2933}
2934
2936{
2937 bool hasDataDefinedSize = false;
2938 double scaledWidth = calculateSize( context, hasDataDefinedSize );
2939
2940 bool hasDataDefinedAspectRatio = false;
2941 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2942 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2943
2944 scaledWidth = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2945 scaledHeight = context.renderContext().convertToPainterUnits( scaledHeight, mSizeUnit, mSizeMapUnitScale );
2946
2947 //don't render symbols with size below one or above 10,000 pixels
2948 if ( static_cast< int >( scaledWidth ) < 1 || 10000.0 < scaledWidth )
2949 {
2950 return QRectF();
2951 }
2952
2953 QPointF outputOffset;
2954 double angle = 0.0;
2955 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2956
2957 double strokeWidth = mStrokeWidth;
2959 {
2962 }
2964
2965 QString path = mPath;
2967 {
2970 context.renderContext().pathResolver() );
2972 {
2973 // need to get colors to take advantage of cached SVGs
2974 QColor fillColor = mColor;
2976 {
2979 }
2980
2981 const QColor strokeColor = mStrokeColor;
2983 {
2986 }
2987
2989
2990 // adjust height of data defined path
2991 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2992 context.renderContext().scaleFactor(), aspectRatio,
2993 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2994 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2995 }
2996 }
2997
2998 QTransform transform;
2999 // move to the desired position
3000 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3001
3002 if ( !qgsDoubleNear( angle, 0.0 ) )
3003 transform.rotate( angle );
3004
3005 //antialiasing
3006 strokeWidth += 1.0 / 2.0;
3007
3008 QRectF symbolBounds = transform.mapRect( QRectF( -scaledWidth / 2.0,
3009 -scaledHeight / 2.0,
3010 scaledWidth,
3011 scaledHeight ) );
3012
3013 //extend bounds by pen width / 2.0
3014 symbolBounds.adjust( -strokeWidth / 2.0, -strokeWidth / 2.0,
3015 strokeWidth / 2.0, strokeWidth / 2.0 );
3016
3017 return symbolBounds;
3018}
3019
3021
3022QgsRasterMarkerSymbolLayer::QgsRasterMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
3023 : mPath( path )
3024{
3025 mSize = size;
3026 mAngle = angle;
3027 mOffset = QPointF( 0, 0 );
3030}
3031
3033
3035{
3036 QString path;
3040
3041 if ( props.contains( QStringLiteral( "imageFile" ) ) )
3042 path = props[QStringLiteral( "imageFile" )].toString();
3043 if ( props.contains( QStringLiteral( "size" ) ) )
3044 size = props[QStringLiteral( "size" )].toDouble();
3045 if ( props.contains( QStringLiteral( "angle" ) ) )
3046 angle = props[QStringLiteral( "angle" )].toDouble();
3047 if ( props.contains( QStringLiteral( "scale_method" ) ) )
3048 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
3049
3050 std::unique_ptr< QgsRasterMarkerSymbolLayer > m = std::make_unique< QgsRasterMarkerSymbolLayer >( path, size, angle, scaleMethod );
3051 m->setCommonProperties( props );
3052 return m.release();
3053}
3054
3055void QgsRasterMarkerSymbolLayer::setCommonProperties( const QVariantMap &properties )
3056{
3057 if ( properties.contains( QStringLiteral( "alpha" ) ) )
3058 {
3059 setOpacity( properties[QStringLiteral( "alpha" )].toDouble() );
3060 }
3061
3062 if ( properties.contains( QStringLiteral( "size_unit" ) ) )
3063 setSizeUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "size_unit" )].toString() ) );
3064 if ( properties.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3065 setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3066 if ( properties.contains( QStringLiteral( "fixedAspectRatio" ) ) )
3067 setFixedAspectRatio( properties[QStringLiteral( "fixedAspectRatio" )].toDouble() );
3068
3069 if ( properties.contains( QStringLiteral( "offset" ) ) )
3070 setOffset( QgsSymbolLayerUtils::decodePoint( properties[QStringLiteral( "offset" )].toString() ) );
3071 if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
3072 setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )].toString() ) );
3073 if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3074 setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3075
3076 if ( properties.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3077 {
3078 setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( properties[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3079 }
3080 if ( properties.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3081 {
3082 setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( properties[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3083 }
3084
3087}
3088
3089void QgsRasterMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
3090{
3091 const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
3092 if ( it != properties.end() && it.value().userType() == QMetaType::Type::QString )
3093 {
3094 if ( saving )
3095 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
3096 else
3097 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
3098 }
3099}
3100
3101void QgsRasterMarkerSymbolLayer::setPath( const QString &path )
3102{
3103 mPath = path;
3105}
3106
3108{
3109 const bool aPreservedAspectRatio = preservedAspectRatio();
3110 if ( aPreservedAspectRatio && !par )
3111 {
3113 }
3114 else if ( !aPreservedAspectRatio && par )
3115 {
3116 mFixedAspectRatio = 0.0;
3117 }
3118 return preservedAspectRatio();
3119}
3120
3122{
3123 if ( mDefaultAspectRatio == 0.0 )
3124 {
3126 mDefaultAspectRatio = ( !size.isNull() && size.isValid() && size.width() > 0 ) ? static_cast< double >( size.height() ) / static_cast< double >( size.width() ) : 0.0;
3127 }
3128 return mDefaultAspectRatio;
3129}
3130
3132{
3133 return QStringLiteral( "RasterMarker" );
3134}
3135
3140
3142{
3143 QPainter *p = context.renderContext().painter();
3144 if ( !p )
3145 return;
3146
3147 QString path = mPath;
3149 {
3152 }
3153
3154 if ( path.isEmpty() )
3155 return;
3156
3157 double width = 0.0;
3158 double height = 0.0;
3159
3160 bool hasDataDefinedSize = false;
3161 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3162
3163 bool hasDataDefinedAspectRatio = false;
3164 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3165
3166 QPointF outputOffset;
3167 double angle = 0.0;
3168
3169 // RenderPercentage Unit Type takes original image size
3171 {
3173 if ( size.isEmpty() )
3174 return;
3175
3176 width = ( scaledSize * static_cast< double >( size.width() ) ) / 100.0;
3177 height = ( scaledSize * static_cast< double >( size.height() ) ) / 100.0;
3178
3179 // don't render symbols with size below one or above 10,000 pixels
3180 if ( static_cast< int >( width ) < 1 || 10000.0 < width || static_cast< int >( height ) < 1 || 10000.0 < height )
3181 return;
3182
3183 calculateOffsetAndRotation( context, width, height, outputOffset, angle );
3184 }
3185 else
3186 {
3187 width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3188 height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3189
3190 if ( preservedAspectRatio() && path != mPath )
3191 {
3193 if ( !size.isNull() && size.isValid() && size.width() > 0 )
3194 {
3195 height = width * ( static_cast< double >( size.height() ) / static_cast< double >( size.width() ) );
3196 }
3197 }
3198
3199 // don't render symbols with size below one or above 10,000 pixels
3200 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
3201 return;
3202
3203 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3204 }
3205
3206 const QgsScopedQPainterState painterState( p );
3207 p->translate( point + outputOffset );
3208
3209 const bool rotated = !qgsDoubleNear( angle, 0 );
3210 if ( rotated )
3211 p->rotate( angle );
3212
3213 double opacity = mOpacity;
3215 {
3218 }
3219 opacity *= context.opacity();
3220
3221 QImage img = fetchImage( context.renderContext(), path, QSize( width, preservedAspectRatio() ? 0 : width * aspectRatio ), preservedAspectRatio(), opacity );
3222 if ( !img.isNull() )
3223 {
3224 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
3225 if ( useSelectedColor )
3226 {
3228 }
3229
3230 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
3231 }
3232}
3233
3234QImage QgsRasterMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
3235{
3236 bool cached = false;
3237 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking );
3238}
3239
3240double QgsRasterMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
3241{
3242 double scaledSize = mSize;
3244
3245 bool ok = true;
3246 if ( hasDataDefinedSize )
3247 {
3250 }
3251 else
3252 {
3254 if ( hasDataDefinedSize )
3255 {
3258 }
3259 }
3260
3261 if ( hasDataDefinedSize && ok )
3262 {
3263 switch ( mScaleMethod )
3264 {
3266 scaledSize = std::sqrt( scaledSize );
3267 break;
3269 break;
3270 }
3271 }
3272
3273 return scaledSize;
3274}
3275
3276double QgsRasterMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
3277{
3279 if ( !hasDataDefinedAspectRatio )
3280 return mFixedAspectRatio;
3281
3283 return 0.0;
3284
3285 double scaledAspectRatio = mDefaultAspectRatio;
3286 if ( mFixedAspectRatio > 0.0 )
3287 scaledAspectRatio = mFixedAspectRatio;
3288
3289 const double defaultHeight = mSize * scaledAspectRatio;
3290 scaledAspectRatio = defaultHeight / scaledSize;
3291
3292 bool ok = true;
3293 double scaledHeight = scaledSize * scaledAspectRatio;
3295 {
3296 context.setOriginalValueVariable( defaultHeight );
3298 }
3299
3300 if ( hasDataDefinedAspectRatio && ok )
3301 {
3302 switch ( mScaleMethod )
3303 {
3305 scaledHeight = sqrt( scaledHeight );
3306 break;
3308 break;
3309 }
3310 }
3311
3312 scaledAspectRatio = scaledHeight / scaledSize;
3313
3314 return scaledAspectRatio;
3315}
3316
3317void QgsRasterMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
3318{
3319 //offset
3320 double offsetX = 0;
3321 double offsetY = 0;
3322 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
3323 offset = QPointF( offsetX, offsetY );
3324
3327 {
3330 }
3331
3333 if ( hasDataDefinedRotation )
3334 {
3335 const QgsFeature *f = context.feature();
3336 if ( f )
3337 {
3338 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3339 {
3340 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3341 angle += m2p.mapRotation();
3342 }
3343 }
3344 }
3345
3346 if ( angle )
3348}
3349
3350
3352{
3353 QVariantMap map;
3354 map[QStringLiteral( "imageFile" )] = mPath;
3355 map[QStringLiteral( "size" )] = QString::number( mSize );
3356 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3357 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3358 map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
3359 map[QStringLiteral( "angle" )] = QString::number( mAngle );
3360 map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
3361 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3362 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3363 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3364 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
3365 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3366 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3367 return map;
3368}
3369
3371{
3372 std::unique_ptr< QgsRasterMarkerSymbolLayer > m = std::make_unique< QgsRasterMarkerSymbolLayer >( mPath, mSize, mAngle );
3373 copyCommonProperties( m.get() );
3374 return m.release();
3375}
3376
3377
3392
3398
3400{
3401 return QColor();
3402}
3403
3408
3413
3415{
3416 bool hasDataDefinedSize = false;
3417 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3418 const double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3419 bool hasDataDefinedAspectRatio = false;
3420 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3421 const double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3422
3423 //don't render symbols with size below one or above 10,000 pixels
3424 if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
3425 {
3426 return QRectF();
3427 }
3428
3429 QPointF outputOffset;
3430 double angle = 0.0;
3431 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3432
3433 QTransform transform;
3434
3435 // move to the desired position
3436 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3437
3438 if ( !qgsDoubleNear( angle, 0.0 ) )
3439 transform.rotate( angle );
3440
3441 QRectF symbolBounds = transform.mapRect( QRectF( -width / 2.0,
3442 -height / 2.0,
3443 width,
3444 height ) );
3445
3446 return symbolBounds;
3447}
3448
3450
3451QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QString chr, double pointSize, const QColor &color, double angle )
3452{
3453 mFontFamily = fontFamily;
3454 mString = chr;
3455 mColor = color;
3456 mAngle = angle;
3457 mSize = pointSize;
3458 mOrigSize = pointSize;
3460 mOffset = QPointF( 0, 0 );
3462 mStrokeColor = DEFAULT_FONTMARKER_BORDERCOLOR;
3463 mStrokeWidth = 0.0;
3464 mStrokeWidthUnit = Qgis::RenderUnit::Millimeters;
3465 mPenJoinStyle = DEFAULT_FONTMARKER_JOINSTYLE;
3466}
3467
3469
3471{
3473 QString string = DEFAULT_FONTMARKER_CHR;
3474 double pointSize = DEFAULT_FONTMARKER_SIZE;
3477
3478 if ( props.contains( QStringLiteral( "font" ) ) )
3479 fontFamily = props[QStringLiteral( "font" )].toString();
3480 if ( props.contains( QStringLiteral( "chr" ) ) && props[QStringLiteral( "chr" )].toString().length() > 0 )
3481 {
3482 string = props["chr"].toString();
3483 const thread_local QRegularExpression charRegExp( QStringLiteral( "%1([0-9]+)%1" ).arg( FONTMARKER_CHR_FIX ) );
3484 QRegularExpressionMatch match = charRegExp.match( string );
3485 while ( match.hasMatch() )
3486 {
3487 QChar replacement = QChar( match.captured( 1 ).toUShort() );
3488 string = string.mid( 0, match.capturedStart( 0 ) ) + replacement + string.mid( match.capturedEnd( 0 ) );
3489 match = charRegExp.match( string );
3490 }
3491 }
3492
3493 if ( props.contains( QStringLiteral( "size" ) ) )
3494 pointSize = props[QStringLiteral( "size" )].toDouble();
3495 if ( props.contains( QStringLiteral( "color" ) ) )
3496 color = QgsColorUtils::colorFromString( props[QStringLiteral( "color" )].toString() );
3497 if ( props.contains( QStringLiteral( "angle" ) ) )
3498 angle = props[QStringLiteral( "angle" )].toDouble();
3499
3501
3502 if ( props.contains( QStringLiteral( "font_style" ) ) )
3503 m->setFontStyle( props[QStringLiteral( "font_style" )].toString() );
3504 if ( props.contains( QStringLiteral( "outline_color" ) ) )
3505 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "outline_color" )].toString() ) );
3506 if ( props.contains( QStringLiteral( "outline_width" ) ) )
3507 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
3508 if ( props.contains( QStringLiteral( "offset" ) ) )
3509 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
3510 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
3511 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
3512 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3513 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3514 if ( props.contains( QStringLiteral( "size_unit" ) ) )
3515 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
3516 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3517 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3518 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
3519 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
3520 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
3521 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
3522 if ( props.contains( QStringLiteral( "joinstyle" ) ) )
3523 m->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() ) );
3524 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3525 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3526 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3527 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3528
3530
3531 return m;
3532}
3533
3535{
3536 return QStringLiteral( "FontMarker" );
3537}
3538
3543
3545{
3546 QColor brushColor = mColor;
3547 QColor penColor = mStrokeColor;
3548
3549 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
3550 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
3551
3552 mBrush = QBrush( brushColor );
3553 mPen = QPen( penColor );
3554 mPen.setJoinStyle( mPenJoinStyle );
3555 mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
3556
3557 mFont = QgsFontUtils::createFont( QgsApplication::fontManager()->processFontFamilyName( mFontFamily ) );
3558 if ( !mFontStyle.isEmpty() )
3559 {
3560 mFont.setStyleName( QgsFontUtils::translateNamedStyle( mFontStyle ) );
3561 }
3562
3563 double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3564 mNonZeroFontSize = !qgsDoubleNear( sizePixels, 0.0 );
3565
3566 if ( mNonZeroFontSize && sizePixels > MAX_FONT_CHARACTER_SIZE_IN_PIXELS )
3567 {
3568 // if font is too large (e.g using map units and map is very zoomed in), then we limit
3569 // the font size and instead scale up the painter.
3570 // this avoids issues with massive font sizes (eg https://github.com/qgis/QGIS/issues/42270)
3571 mFontSizeScale = sizePixels / MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3572 sizePixels = MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3573 }
3574 else
3575 mFontSizeScale = 1.0;
3576
3577 // if a non zero, but small pixel size results, round up to 2 pixels so that a "dot" is at least visible
3578 // (if we set a <=1 pixel size here Qt will reset the font to a default size, leading to much too large symbols)
3579 mFont.setPixelSize( std::max( 2, static_cast< int >( std::round( sizePixels ) ) ) );
3580 mFontMetrics.reset( new QFontMetrics( mFont ) );
3581 mChrWidth = mFontMetrics->horizontalAdvance( mString );
3582 mChrOffset = QPointF( mChrWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3583 mOrigSize = mSize; // save in case the size would be data defined
3584
3585 // use caching only when not using a data defined character
3589 if ( mUseCachedPath )
3590 {
3591 QPointF chrOffset = mChrOffset;
3592 double chrWidth;
3593 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3594 mCachedPath = QPainterPath();
3595 mCachedPath.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3596 }
3597}
3598
3600{
3601 Q_UNUSED( context )
3602}
3603
3604QString QgsFontMarkerSymbolLayer::characterToRender( QgsSymbolRenderContext &context, QPointF &charOffset, double &charWidth )
3605{
3606 charOffset = mChrOffset;
3607 QString stringToRender = mString;
3609 {
3610 context.setOriginalValueVariable( mString );
3612 if ( stringToRender != mString )
3613 {
3614 charWidth = mFontMetrics->horizontalAdvance( stringToRender );
3615 charOffset = QPointF( charWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3616 }
3617 }
3618 return stringToRender;
3619}
3620
3621void QgsFontMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
3622 double scaledSize,
3623 bool &hasDataDefinedRotation,
3624 QPointF &offset,
3625 double &angle ) const
3626{
3627 //offset
3628 double offsetX = 0;
3629 double offsetY = 0;
3630 markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
3631 offset = QPointF( offsetX, offsetY );
3632
3633 //angle
3634 bool ok = true;
3637 {
3640
3641 // If the expression evaluation was not successful, fallback to static value
3642 if ( !ok )
3644 }
3645
3646 hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation;
3647 if ( hasDataDefinedRotation )
3648 {
3649 // For non-point markers, "dataDefinedRotation" means following the
3650 // shape (shape-data defined). For them, "field-data defined" does
3651 // not work at all. TODO: if "field-data defined" ever gets implemented
3652 // we'll need a way to distinguish here between the two, possibly
3653 // using another flag in renderHints()
3654 const QgsFeature *f = context.feature();
3655 if ( f )
3656 {
3657 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3658 {
3659 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3660 angle += m2p.mapRotation();
3661 }
3662 }
3663 }
3664
3665 if ( angle )
3667}
3668
3669double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context )
3670{
3671 double scaledSize = mSize;
3672 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
3673
3674 bool ok = true;
3675 if ( hasDataDefinedSize )
3676 {
3679 }
3680
3681 if ( hasDataDefinedSize && ok )
3682 {
3683 switch ( mScaleMethod )
3684 {
3686 scaledSize = std::sqrt( scaledSize );
3687 break;
3689 break;
3690 }
3691 }
3692 return scaledSize;
3693}
3694
3696{
3697 QPainter *p = context.renderContext().painter();
3698 if ( !p || !mNonZeroFontSize )
3699 return;
3700
3701 QTransform transform;
3702
3703 bool ok;
3704 QColor brushColor = mColor;
3706 {
3709 }
3710 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
3711 brushColor = useSelectedColor ? context.renderContext().selectionColor() : brushColor;
3712 if ( !useSelectedColor || !SELECTION_IS_OPAQUE )
3713 {
3714 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
3715 }
3716 mBrush.setColor( brushColor );
3717
3718 QColor penColor = mStrokeColor;
3720 {
3723 }
3724 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
3725
3726 double penWidth = context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3728 {
3729 context.setOriginalValueVariable( mStrokeWidth );
3731 if ( ok )
3732 {
3733 penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3734 }
3735 }
3736
3738 {
3741 if ( ok )
3742 {
3743 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
3744 }
3745 }
3746
3747 const QgsScopedQPainterState painterState( p );
3748 p->setBrush( mBrush );
3749 if ( !qgsDoubleNear( penWidth, 0.0 ) )
3750 {
3751 mPen.setColor( penColor );
3752 mPen.setWidthF( penWidth );
3753 p->setPen( mPen );
3754 }
3755 else
3756 {
3757 p->setPen( Qt::NoPen );
3758 }
3759
3761 {
3762 context.setOriginalValueVariable( mFontFamily );
3764 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( ok ? fontFamily : mFontFamily );
3765 QgsFontUtils::setFontFamily( mFont, processedFamily );
3766 }
3768 {
3769 context.setOriginalValueVariable( mFontStyle );
3772 }
3774 {
3775 mFontMetrics.reset( new QFontMetrics( mFont ) );
3776 }
3777
3778 QPointF chrOffset = mChrOffset;
3779 double chrWidth;
3780 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3781
3782 const double sizeToRender = calculateSize( context );
3783
3784 bool hasDataDefinedRotation = false;
3785 QPointF offset;
3786 double angle = 0;
3787 calculateOffsetAndRotation( context, sizeToRender, hasDataDefinedRotation, offset, angle );
3788
3789 p->translate( point.x() + offset.x(), point.y() + offset.y() );
3790
3791 if ( !qgsDoubleNear( angle, 0.0 ) )
3792 transform.rotate( angle );
3793
3794 if ( !qgsDoubleNear( sizeToRender, mOrigSize ) )
3795 {
3796 const double s = sizeToRender / mOrigSize;
3797 transform.scale( s, s );
3798 }
3799
3800 if ( !qgsDoubleNear( mFontSizeScale, 1.0 ) )
3801 transform.scale( mFontSizeScale, mFontSizeScale );
3802
3803 if ( mUseCachedPath )
3804 {
3805 p->drawPath( transform.map( mCachedPath ) );
3806 }
3807 else
3808 {
3809 QPainterPath path;
3810 path.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3811 p->drawPath( transform.map( path ) );
3812 }
3813}
3814
3816{
3817 QVariantMap props;
3818 props[QStringLiteral( "font" )] = mFontFamily;
3819 props[QStringLiteral( "font_style" )] = mFontStyle;
3820 QString chr = mString;
3821 for ( int i = 0; i < 32; i++ )
3822 {
3823 if ( i == 9 || i == 10 || i == 13 )
3824 {
3825 continue;
3826 }
3827 chr.replace( QChar( i ), QStringLiteral( "%1%2%1" ).arg( FONTMARKER_CHR_FIX, QString::number( i ) ) );
3828 }
3829 props[QStringLiteral( "chr" )] = chr;
3830 props[QStringLiteral( "size" )] = QString::number( mSize );
3831 props[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3832 props[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3833 props[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mColor );
3834 props[QStringLiteral( "outline_color" )] = QgsColorUtils::colorToString( mStrokeColor );
3835 props[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
3836 props[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
3837 props[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
3838 props[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
3839 props[QStringLiteral( "angle" )] = QString::number( mAngle );
3840 props[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3841 props[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3842 props[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3843 props[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3844 props[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3845 return props;
3846}
3847
3849{
3850 QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( mFontFamily, mString, mSize, mColor, mAngle );
3851 m->setFontStyle( mFontStyle );
3852 m->setStrokeColor( mStrokeColor );
3853 m->setStrokeWidth( mStrokeWidth );
3854 m->setStrokeWidthUnit( mStrokeWidthUnit );
3855 m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
3856 m->setPenJoinStyle( mPenJoinStyle );
3857 m->setOffset( mOffset );
3860 m->setSizeUnit( mSizeUnit );
3865 copyPaintEffect( m );
3866 return m;
3867}
3868
3869void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
3870{
3871 // <Graphic>
3872 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
3873 element.appendChild( graphicElem );
3874
3875 const QString fontPath = QStringLiteral( "ttf://%1" ).arg( mFontFamily );
3876 int markIndex = !mString.isEmpty() ? mString.at( 0 ).unicode() : 0;
3877 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
3878 QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, QStringLiteral( "ttf" ), &markIndex, mColor, size );
3879
3880 // <Rotation>
3881 QString angleFunc;
3882 bool ok;
3883 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
3884 if ( !ok )
3885 {
3886 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
3887 }
3888 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
3889 {
3890 angleFunc = QString::number( angle + mAngle );
3891 }
3892 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
3893
3894 // <Displacement>
3895 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
3897}
3898
3905
3907{
3909 mStrokeWidthUnit = unit;
3910}
3911
3913{
3914 QPointF chrOffset = mChrOffset;
3915 double chrWidth = mChrWidth;
3916 //calculate width of rendered character
3917 ( void )characterToRender( context, chrOffset, chrWidth );
3918
3919 if ( !mFontMetrics )
3920 mFontMetrics.reset( new QFontMetrics( mFont ) );
3921
3922 double scaledSize = calculateSize( context );
3923 if ( !qgsDoubleNear( scaledSize, mOrigSize ) )
3924 {
3925 chrWidth *= scaledSize / mOrigSize;
3926 }
3927 chrWidth *= mFontSizeScale;
3928
3929 bool hasDataDefinedRotation = false;
3930 QPointF offset;
3931 double angle = 0;
3932 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
3933 scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3934
3935 QTransform transform;
3936
3937 // move to the desired position
3938 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
3939
3940 if ( !qgsDoubleNear( angle, 0.0 ) )
3941 transform.rotate( angle );
3942
3943 QRectF symbolBounds = transform.mapRect( QRectF( -chrWidth / 2.0,
3944 -scaledSize / 2.0,
3945 chrWidth,
3946 scaledSize ) );
3947 return symbolBounds;
3948}
3949
3951{
3952 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
3953
3954 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
3955 if ( graphicElem.isNull() )
3956 return nullptr;
3957
3958 QString name, format;
3959 QColor color;
3960 double size;
3961 int chr;
3962
3963 if ( !QgsSymbolLayerUtils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
3964 return nullptr;
3965
3966 if ( !name.startsWith( QLatin1String( "ttf://" ) ) || format != QLatin1String( "ttf" ) )
3967 return nullptr;
3968
3969 const QString fontFamily = name.mid( 6 );
3970
3971 double angle = 0.0;
3972 QString angleFunc;
3973 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
3974 {
3975 bool ok;
3976 const double d = angleFunc.toDouble( &ok );
3977 if ( ok )
3978 angle = d;
3979 }
3980
3981 QPointF offset;
3983
3984 double scaleFactor = 1.0;
3985 const QString uom = element.attribute( QStringLiteral( "uom" ) );
3986 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
3987 offset.setX( offset.x() * scaleFactor );
3988 offset.setY( offset.y() * scaleFactor );
3989 size = size * scaleFactor;
3990
3992 m->setOutputUnit( sldUnitSize );
3993 m->setAngle( angle );
3994 m->setOffset( offset );
3995 return m;
3996}
3997
3998void QgsFontMarkerSymbolLayer::resolveFonts( const QVariantMap &properties, const QgsReadWriteContext &context )
3999{
4000 const QString fontFamily = properties.value( QStringLiteral( "font" ), DEFAULT_FONTMARKER_FONT ).toString();
4001 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( fontFamily );
4002 QString matched;
4003 if ( !QgsFontUtils::fontFamilyMatchOnSystem( processedFamily )
4004 && !QgsApplication::fontManager()->tryToDownloadFontFamily( processedFamily, matched ) )
4005 {
4006 context.pushMessage( QObject::tr( "Font “%1” not available on system" ).arg( processedFamily ) );
4007 }
4008}
4009
4011{
4012 QMap<QString, QgsProperty>::iterator it = mParameters.begin();
4013 for ( ; it != mParameters.end(); ++it )
4014 it.value().prepare( context.renderContext().expressionContext() );
4015
4017}
4018
4019
4021{
4022 QSet<QString> attrs = QgsMarkerSymbolLayer::usedAttributes( context );
4023
4024 QMap<QString, QgsProperty>::const_iterator it = mParameters.constBegin();
4025 for ( ; it != mParameters.constEnd(); ++it )
4026 {
4027 attrs.unite( it.value().referencedFields( context.expressionContext(), true ) );
4028 }
4029
4030 return attrs;
4031}
4032
4033//
4034// QgsAnimatedMarkerSymbolLayer
4035//
4036
4037QgsAnimatedMarkerSymbolLayer::QgsAnimatedMarkerSymbolLayer( const QString &path, double size, double angle )
4038 : QgsRasterMarkerSymbolLayer( path, size, angle )
4039{
4040
4041}
4042
4044
4046{
4047 QString path;
4050
4051 if ( properties.contains( QStringLiteral( "imageFile" ) ) )
4052 path = properties[QStringLiteral( "imageFile" )].toString();
4053 if ( properties.contains( QStringLiteral( "size" ) ) )
4054 size = properties[QStringLiteral( "size" )].toDouble();
4055 if ( properties.contains( QStringLiteral( "angle" ) ) )
4056 angle = properties[QStringLiteral( "angle" )].toDouble();
4057
4058 std::unique_ptr< QgsAnimatedMarkerSymbolLayer > m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( path, size, angle );
4059 m->setFrameRate( properties.value( QStringLiteral( "frameRate" ), QStringLiteral( "10" ) ).toDouble() );
4060
4061 m->setCommonProperties( properties );
4062 return m.release();
4063}
4064
4066{
4067 return QStringLiteral( "AnimatedMarker" );
4068}
4069
4071{
4072 QVariantMap res = QgsRasterMarkerSymbolLayer::properties();
4073 res.insert( QStringLiteral( "frameRate" ), mFrameRateFps );
4074 return res;
4075}
4076
4078{
4079 std::unique_ptr< QgsAnimatedMarkerSymbolLayer > m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( mPath, mSize, mAngle );
4080 m->setFrameRate( mFrameRateFps );
4081 copyCommonProperties( m.get() );
4082 return m.release();
4083}
4084
4086{
4088
4089 mPreparedPaths.clear();
4091 {
4093 mStaticPath = true;
4094 }
4095 else
4096 {
4097 mStaticPath = false;
4098 }
4099}
4100
4101QImage QgsAnimatedMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
4102{
4103 if ( !mStaticPath && !mPreparedPaths.contains( path ) )
4104 {
4106 mPreparedPaths.insert( path );
4107 }
4108
4109 const long long mapFrameNumber = context.currentFrame();
4111 const double markerAnimationDuration = totalFrameCount / mFrameRateFps;
4112
4113 double animationTimeSeconds = 0;
4114 if ( mapFrameNumber >= 0 && context.frameRate() > 0 )
4115 {
4116 // render is part of an animation, so we base the calculated frame on that
4117 animationTimeSeconds = mapFrameNumber / context.frameRate();
4118 }
4119 else
4120 {
4121 // render is outside of animation, so base the calculated frame on the current epoch
4122 animationTimeSeconds = QDateTime::currentMSecsSinceEpoch() / 1000.0;
4123 }
4124
4125 const double markerAnimationProgressSeconds = std::fmod( animationTimeSeconds, markerAnimationDuration );
4126 const int movieFrame = static_cast< int >( std::floor( markerAnimationProgressSeconds * mFrameRateFps ) );
4127
4128 bool cached = false;
4129 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking, 96, movieFrame );
4130}
4131
@ DynamicRotation
Rotation of symbol may be changed during rendering and symbol should not be cached.
@ IsSymbolLayerSubSymbol
Symbol is being rendered as a sub-symbol of a QgsSymbolLayer (since QGIS 3.38)
@ CanCalculateMaskGeometryPerFeature
If present, indicates that mask geometry can safely be calculated per feature for the symbol layer....
ScaleMethod
Scale methods.
Definition qgis.h:514
@ ScaleDiameter
Calculate scale by the diameter.
@ ScaleArea
Calculate scale by the area.
QFlags< SymbolLayerFlag > SymbolLayerFlags
Symbol layer flags.
Definition qgis.h:724
MarkerShape
Marker shapes.
Definition qgis.h:2713
@ Pentagon
Pentagon.
@ EquilateralTriangle
Equilateral triangle.
@ SemiCircle
Semi circle (top half)
@ QuarterCircle
Quarter circle (top left quarter)
@ LeftHalfTriangle
Left half of triangle.
@ ArrowHead
Right facing arrow head (unfilled, lines only)
@ ParallelogramRight
Parallelogram that slants right (since QGIS 3.28)
@ AsteriskFill
A filled asterisk shape (since QGIS 3.18)
@ Octagon
Octagon (since QGIS 3.18)
@ HalfArc
A line-only half arc (since QGIS 3.20)
@ Line
Vertical line.
@ QuarterSquare
Quarter square (top left quarter)
@ Triangle
Triangle.
@ Cross2
Rotated cross (lines only), 'x' shape.
@ Trapezoid
Trapezoid (since QGIS 3.28)
@ ArrowHeadFilled
Right facing filled arrow head.
@ Shield
A shape consisting of a triangle attached to a rectangle (since QGIS 3.28)
@ HalfSquare
Half square (left half)
@ CrossFill
Solid filled cross.
@ Decagon
Decagon (since QGIS 3.28)
@ RoundedSquare
A square with rounded corners (since QGIS 3.28)
@ RightHalfTriangle
Right half of triangle.
@ ThirdCircle
One third circle (top left third)
@ ThirdArc
A line-only one third arc (since QGIS 3.20)
@ SquareWithCorners
A square with diagonal corners (since QGIS 3.18)
@ QuarterArc
A line-only one quarter arc (since QGIS 3.20)
@ DiamondStar
A 4-sided star (since QGIS 3.28)
@ Cross
Cross (lines only)
@ ParallelogramLeft
Parallelogram that slants left (since QGIS 3.28)
@ Heart
Heart (since QGIS 3.28)
@ DiagonalHalfSquare
Diagonal half square (bottom left half)
RenderUnit
Rendering size units.
Definition qgis.h:4594
@ Percentage
Percentage of another measurement (e.g., canvas size, feature size)
@ Millimeters
Millimeters.
@ Unknown
Mixed or unknown units.
@ MapUnits
Map units.
@ MetersInMapUnits
Meters value as Map units.
@ RenderingSubSymbol
Set whenever a sub-symbol of a parent symbol is currently being rendered. Can be used during symbol a...
@ RenderSymbolPreview
The render is for a symbol preview only and map based properties may not be available,...
@ RenderBlocking
Render and load remote sources in the same thread to ensure rendering remote sources (svg and images)...
@ Fill
Fill symbol.
QColor valueAsColor(int key, const QgsExpressionContext &context, const QColor &defaultColor=QColor(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a color.
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double.
QString valueAsString(int key, const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a string.
Animated marker symbol layer class.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsAnimatedMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
~QgsAnimatedMarkerSymbolLayer() override
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
QImage fetchImage(QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity) const override
Fetches the image to render.
QgsAnimatedMarkerSymbolLayer(const QString &path=QString(), double size=DEFAULT_RASTERMARKER_SIZE, double angle=DEFAULT_RASTERMARKER_ANGLE)
Constructor for animated marker symbol layer using the specified source image path.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates an animated marker symbol layer from a string map of properties.
QString layerType() const override
Returns a string that represents this layer type.
static QgsImageCache * imageCache()
Returns the application's image cache, used for caching resampled versions of raster images.
static QgsSvgCache * svgCache()
Returns the application's SVG cache, used for caching SVG images and handling parameter replacement w...
static QgsFontManager * fontManager()
Returns the application font manager, which manages available fonts and font installation for the QGI...
static QColor colorFromString(const QString &string)
Decodes a string into a color value.
static QString colorToString(const QColor &color)
Encodes a color into a string value.
Exports QGIS layers to the DXF format.
void writeFilledCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius)
Write filled circle (as hatch)
void writeCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius, const QString &lineStyleName, double width)
Write circle (as polyline)
static double mapUnitScaleFactor(double scale, Qgis::RenderUnit symbolUnits, Qgis::DistanceUnit mapUnits, double mapUnitsPerPixel=1.0)
Returns scale factor for conversion to map units.
void writeLine(const QgsPoint &pt1, const QgsPoint &pt2, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Write line (as a polyline)
void writePolygon(const QgsRingSequence &polygon, const QString &layer, const QString &hatchPattern, const QColor &color)
Draw dxf filled polygon (HATCH)
Qgis::DistanceUnit mapUnits() const
Retrieve map units.
double symbologyScale() const
Returns the reference scale for output.
void clipValueToMapUnitScale(double &value, const QgsMapUnitScale &scale, double pixelToMMFactor) const
Clips value to scale minimum/maximum.
void writePolyline(const QgsPointSequence &line, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Draw dxf primitives (LWPOLYLINE)
A paint device for drawing into dxf files.
void setShift(QPointF shift)
void setLayer(const QString &layer)
void setOutputSize(const QRectF &r)
void setDrawingSize(QSizeF size)
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
QgsGeometry geometry
Definition qgsfeature.h:69
bool hasGeometry() const
Returns true if the feature has an associated geometry.
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
static QgsFillSymbol * createSimple(const QVariantMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties.
Filled marker symbol layer, consisting of a shape which is rendered using a QgsFillSymbol.
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QColor color() const override
Returns the "representative" color of the symbol layer.
QgsFilledMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QgsSymbol * subSymbol() override
Returns the symbol's sub symbol, if present.
~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.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
void writeSldMarker(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Writes the symbol layer definition as a SLD XML element.
void setStrokeWidth(double width)
Set's the marker's stroke width.
static void resolveFonts(const QVariantMap &properties, const QgsReadWriteContext &context)
Resolves fonts from a properties map, raising warnings in the specified context if the required fonts...
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
void setPenJoinStyle(Qt::PenJoinStyle style)
Sets the stroke join style.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsFontMarkerSymbolLayer from an SLD XML element.
QgsFontMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
QString layerType() const override
Returns a string that represents this layer type.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsFontMarkerSymbolLayer from a property map (see properties())
Qgis::SymbolLayerFlags flags() const override
Returns flags which control the symbol layer's behavior.
static QString translateNamedStyle(const QString &namedStyle)
Returns the localized named style of a font, if such a translation is available.
static QFont createFont(const QString &family, int pointSize=-1, int weight=-1, bool italic=false)
Creates a font with the specified family.
static bool fontFamilyMatchOnSystem(const QString &family, QString *chosen=nullptr, bool *match=nullptr)
Check whether font family is on system.
static bool updateFontViaStyle(QFont &f, const QString &fontstyle, bool fallback=false)
Updates font with named style and retain all font properties.
static void setFontFamily(QFont &font, const QString &family)
Sets the family for a font object.
Qgis::GeometryType type
QSize originalSize(const QString &path, bool blocking=false) const
Returns the original size (in pixels) of the image at the specified path.
int totalFrameCount(const QString &path, bool blocking=false)
Returns the total frame count of the image at the specified path.
QImage pathAsImage(const QString &path, const QSize size, const bool keepAspectRatio, const double opacity, bool &fitsInCache, bool blocking=false, double targetDpi=96, int frameNumber=-1, bool *isMissing=nullptr)
Returns the specified path rendered as an image.
void prepareAnimation(const QString &path)
Prepares for optimized retrieval of frames for the animation at the given path.
static void overlayColor(QImage &image, const QColor &color)
Overlays a color onto an image.
Perform transforms between map coordinates and device coordinates.
double mapUnitsPerPixel() const
Returns the current map units per pixel.
double mapRotation() const
Returns the current map rotation in degrees (clockwise).
Struct for storing maximum and minimum scales for measurements in map units.
Abstract base class for marker symbol layers.
double mSize
Marker size.
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)
HorizontalAnchorPoint
Symbol horizontal anchor points.
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.
Qgis::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
void setVerticalAnchorPoint(VerticalAnchorPoint v)
Sets the vertical anchor point for positioning the symbol.
QPointF mOffset
Marker offset.
void setHorizontalAnchorPoint(HorizontalAnchorPoint h)
Sets the horizontal anchor point for positioning the symbol.
QgsMapUnitScale mapUnitScale() const override
void setOffset(QPointF offset)
Sets the marker's offset, which is the horizontal and vertical displacement which the rendered marker...
void setSizeMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol's size.
double size() const
Returns the symbol size.
QgsMapUnitScale mOffsetMapUnitScale
Offset map unit scale.
HorizontalAnchorPoint mHorizontalAnchorPoint
Horizontal anchor point.
static QPointF _rotatedOffset(QPointF offset, double angle)
Adjusts a marker offset to account for rotation.
Qgis::ScaleMethod mScaleMethod
Marker size scaling method.
QgsMapUnitScale mSizeMapUnitScale
Marker size map unit scale.
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.
VerticalAnchorPoint
Symbol vertical anchor points.
void markerOffset(QgsSymbolRenderContext &context, double &offsetX, double &offsetY) const
Calculates the required marker offset, including both the symbol offset and any displacement required...
VerticalAnchorPoint mVerticalAnchorPoint
Vertical anchor point.
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol's offset.
double mAngle
Marker rotation angle, in degrees clockwise from north.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
double angle() const
Returns the rotation angle for the marker, in degrees clockwise from north.
void setMapUnitScale(const QgsMapUnitScale &scale) override
static void drawPicture(QPainter *painter, const QPointF &point, const QPicture &picture)
Draws a picture onto a painter, correctly applying workarounds to avoid issues with incorrect scaling...
Resolves relative paths into absolute paths and vice versa.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const final
Returns the calculated value of the property with the specified key from within the collection.
bool isActive(int key) const final
Returns true if the collection contains an active property with the specified key.
QgsProperty property(int key) const final
Returns a matching property from the collection, if one exists.
QString asExpression() const
Returns an expression string representing the state of the property, or an empty string if the proper...
static QVariantMap propertyMapToVariantMap(const QMap< QString, QgsProperty > &propertyMap)
Convert a map of QgsProperty to a map of QVariant This is useful to save a map of properties.
static QMap< QString, QgsProperty > variantMapToPropertyMap(const QVariantMap &variantMap)
Convert a map of QVariant to a map of QgsProperty This is useful to restore a map of properties.
static QgsProperty fromValue(const QVariant &value, bool isActive=true)
Returns a new StaticProperty created from the specified value.
Raster marker symbol layer class.
double mFixedAspectRatio
The marker fixed aspect ratio.
QColor color() const override
Returns the "representative" color of the symbol layer.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsMapUnitScale mapUnitScale() const override
void copyCommonProperties(QgsRasterMarkerSymbolLayer *other) const
Copies common properties to another layer.
void setOpacity(double opacity)
Set the marker opacity.
QString path() const
Returns the marker raster image path.
double calculateAspectRatio(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio) const
Calculates the marker aspect ratio between width and height.
QgsRasterMarkerSymbolLayer(const QString &path=QString(), double size=DEFAULT_SVGMARKER_SIZE, double angle=DEFAULT_SVGMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructs raster marker symbol layer with picture from given absolute path to a raster image file.
void setPath(const QString &path)
Set the marker raster image path.
virtual QImage fetchImage(QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity) const
Fetches the image to render.
double defaultAspectRatio() const
Returns the default marker aspect ratio between width and height, 0 if not yet calculated.
Qgis::SymbolLayerFlags flags() const override
Returns flags which control the symbol layer's behavior.
void setFixedAspectRatio(double ratio)
Set the marker aspect ratio between width and height to be used in rendering, if the value set is low...
void setMapUnitScale(const QgsMapUnitScale &scale) override
void setCommonProperties(const QVariantMap &properties)
Sets common class properties from a properties map.
QgsRasterMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
bool preservedAspectRatio() const
Returns the preserved aspect ratio value, true if fixed aspect ratio has been lower or equal to 0.
static void resolvePaths(QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving)
Turns relative paths in properties map to absolute when reading and vice versa when writing.
~QgsRasterMarkerSymbolLayer() override
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a raster marker symbol layer from a string map of properties.
double mOpacity
The marker default opacity.
double updateDefaultAspectRatio()
Calculates the default marker aspect ratio between width and height.
double mDefaultAspectRatio
The marker default aspect ratio.
bool setPreservedAspectRatio(bool par)
Set preserved the marker aspect ratio between width and height.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QString layerType() const override
Returns a string that represents this layer type.
double opacity() const
Returns the marker opacity.
The class is used as a container of context for various read/write operations on other objects.
void pushMessage(const QString &message, Qgis::MessageLevel level=Qgis::MessageLevel::Warning) const
Append a message to the context.
Contains information about the context of a rendering operation.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
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.
bool forceVectorOutput() const
Returns true if rendering operations should use vector operations instead of any faster raster shortc...
long long currentFrame() const
Returns the current frame number of the map (in frames per second), for maps which are part of an ani...
float devicePixelRatio() const
Returns the device pixel ratio.
void setFlag(Qgis::RenderContextFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
double frameRate() const
Returns the frame rate of the map, for maps which are part of an animation.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
QColor selectionColor() const
Returns the color to use when rendering selected features.
Qgis::RenderContextFlags flags() const
Returns combination of flags used for rendering.
const QgsPathResolver & pathResolver() const
Returns the path resolver for conversion between relative and absolute paths during rendering operati...
Scoped object for saving and restoring a QPainter object's state.
Abstract base class for simple marker symbol layers.
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
void calculateOffsetAndRotation(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle) const
Calculates the marker offset and rotation.
Qgis::MarkerShape mShape
Symbol shape.
QPainterPath mPath
Painter path representing shape. If mPolygon is empty then the shape is stored in mPath.
bool shapeToPolygon(Qgis::MarkerShape shape, QPolygonF &polygon) const
Creates a polygon representing the specified shape.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
static QList< Qgis::MarkerShape > availableShapes()
Returns a list of all available shape types.
~QgsSimpleMarkerSymbolLayerBase() override
static bool shapeIsFilled(Qgis::MarkerShape shape)
Returns true if a symbol shape has a fill.
QPolygonF mPolygon
Polygon of points in shape. If polygon is empty then shape is using mPath.
Qgis::MarkerShape shape() const
Returns the shape for the rendered marker symbol.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
QgsSimpleMarkerSymbolLayerBase(Qgis::MarkerShape shape=Qgis::MarkerShape::Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructor for QgsSimpleMarkerSymbolLayerBase.
static QString encodeShape(Qgis::MarkerShape shape)
Encodes a shape to its string representation.
double calculateSize(QgsSymbolRenderContext &context, bool &hasDataDefinedSize) const
Calculates the desired size of the marker, considering data defined size overrides.
static Qgis::MarkerShape decodeShape(const QString &name, bool *ok=nullptr)
Attempts to decode a string representation of a shape name to the corresponding shape.
bool prepareMarkerPath(Qgis::MarkerShape symbol)
Prepares the layer for drawing the specified shape (QPainterPath version)
bool prepareMarkerShape(Qgis::MarkerShape shape)
Prepares the layer for drawing the specified shape (QPolygonF version)
Simple marker symbol layer, consisting of a rendered shape with solid fill color and an stroke.
QPen mSelPen
QPen to use as stroke of selected symbols.
void setColor(const QColor &color) override
Sets the "representative" color for the symbol layer.
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
void writeSldMarker(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Writes the symbol layer definition as a SLD XML element.
static const int MAXIMUM_CACHE_WIDTH
Maximum width/height of cache image.
QString ogrFeatureStyle(double mmScaleFactor, double mapUnitScaleFactor) const override
bool prepareCache(QgsSymbolRenderContext &context)
Prepares cache image.
QgsMapUnitScale mStrokeWidthMapUnitScale
Stroke width map unit scale.
QgsSimpleMarkerSymbolLayer(Qgis::MarkerShape shape=Qgis::MarkerShape::Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD, const QColor &color=DEFAULT_SIMPLEMARKER_COLOR, const QColor &strokeColor=DEFAULT_SIMPLEMARKER_BORDERCOLOR, Qt::PenJoinStyle penJoinStyle=DEFAULT_SIMPLEMARKER_JOINSTYLE)
Constructor for QgsSimpleMarkerSymbolLayer.
double strokeWidth() const
Returns the width of the marker's stroke.
Qt::PenJoinStyle mPenJoinStyle
Stroke pen join style.
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
QSizeF svgViewboxSize(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >())
Calculates the viewbox size of a (possibly cached) SVG file.
QPicture svgAsPicture(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool forceVectorOutput=false, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >())
Returns an SVG drawing as a QPicture.
void containsParams(const QString &path, bool &hasFillParam, QColor &defaultFillColor, bool &hasStrokeParam, QColor &defaultStrokeColor, bool &hasStrokeWidthParam, double &defaultStrokeWidth, bool blocking=false) const
Tests if an SVG file contains parameters for fill, stroke color, stroke width.
QImage svgAsImage(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool &fitsInCache, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >())
Returns an SVG drawing as a QImage.
QByteArray svgContent(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >(), bool *isMissingImage=nullptr)
Gets the SVG content corresponding to the given path.
QgsSvgMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
QColor fillColor() const override
Returns the fill color for the symbol layer.
QgsMapUnitScale mapUnitScale() const override
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates the symbol.
double mDefaultAspectRatio
The marker default aspect ratio.
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.
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.
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 void parametricSvgToSld(QDomDocument &doc, QDomElement &graphicElem, const QString &path, const QColor &fillColor, double size, const QColor &strokeColor, double strokeWidth)
Encodes a reference to a parametric SVG into SLD, as a succession of parametric SVG using URL paramet...
static Qgis::ScaleMethod decodeScaleMethod(const QString &str)
Decodes a symbol scale method from a string.
static QString encodePenCapStyle(Qt::PenCapStyle style)
static void externalMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &path, const QString &format, int *markIndex=nullptr, const QColor &color=QColor(), double size=-1)
static bool wellKnownMarkerFromSld(QDomElement &element, QString &name, QColor &color, QColor &strokeColor, Qt::PenStyle &strokeStyle, double &strokeWidth, double &size)
static void createDisplacementElement(QDomDocument &doc, QDomElement &element, QPointF offset)
static QString svgSymbolNameToPath(const QString &name, const QgsPathResolver &pathResolver)
Determines an SVG symbol's path from its name.
static QString encodeColor(const QColor &color)
static 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 void wellKnownMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &name, const QColor &color, const QColor &strokeColor, Qt::PenStyle strokeStyle, double strokeWidth=-1, double size=-1)
static QString encodeScaleMethod(Qgis::ScaleMethod scaleMethod)
Encodes a symbol scale method to a string.
static Qt::PenStyle decodePenStyle(const QString &str)
static void createRotationElement(QDomDocument &doc, QDomElement &element, const QString &rotationFunc)
static QString encodePoint(QPointF point)
Encodes a QPointF to a string.
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
static QPointF decodePoint(const QString &string)
Decodes a QSizeF from a string.
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.
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns the set of attributes referenced by the layer.
void copyDataDefinedProperties(QgsSymbolLayer *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
@ StrokeStyle
Stroke style (eg solid, dashed)
@ Name
Name, eg shape name for simple markers.
@ Character
Character, eg for font marker symbol layers.
@ StrokeColor
Stroke color.
@ CapStyle
Line cap style.
@ JoinStyle
Line join style.
@ StrokeWidth
Stroke width.
@ FontFamily
Font family.
@ Offset
Symbol offset.
@ Height
Symbol height.
void restoreOldDataDefinedProperties(const QVariantMap &stringMap)
Restores older data defined properties from string map.
virtual void startRender(QgsSymbolRenderContext &context)=0
Called before a set of rendering operations commences on the supplied render context.
virtual void prepareExpressions(const QgsSymbolRenderContext &context)
Prepares all data defined property expressions for evaluation.
virtual void setColor(const QColor &color)
Sets the "representative" color for the symbol layer.
virtual QColor color() const
Returns the "representative" color of the symbol layer.
virtual void setOutputUnit(Qgis::RenderUnit unit)
Sets the units to use for sizes and widths within the symbol layer.
void copyPaintEffect(QgsSymbolLayer *destLayer) const
Copies paint effect of this layer to another symbol layer.
virtual Qgis::SymbolLayerFlags flags() const
Returns flags which control the symbol layer's behavior.
QgsPropertyCollection mDataDefinedProperties
virtual bool hasDataDefinedProperties() const
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
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.
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:94
Qgis::SymbolType type() const
Returns the symbol's type.
Definition qgssymbol.h:156
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:5652
QMap< QString, QString > QgsStringMap
Definition qgis.h:6190
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
#define DEG2RAD(x)
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
#define QgsDebugError(str)
Definition qgslogger.h:38
#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