QGIS API Documentation 3.43.0-Master (3ee7834ace6)
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 << QPointF( inner_r * std::sin( DEG2RAD( 315.0 ) ), - inner_r * std::cos( DEG2RAD( 315.0 ) ) );
668 return true;
669 }
670
672 {
673 const double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
674
675 polygon << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ) // 324
676 << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288 ) ) ) // 288
677 << QPointF( inner_r * std::sin( DEG2RAD( 252.0 ) ), - inner_r * std::cos( DEG2RAD( 252.0 ) ) ) // 252
678 << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) ) // 216
679 << QPointF( 0, inner_r ) // 180
680 << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) ) // 144
681 << QPointF( inner_r * std::sin( DEG2RAD( 108.0 ) ), - inner_r * std::cos( DEG2RAD( 108.0 ) ) ) // 108
682 << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) ) // 72
683 << QPointF( inner_r * std::sin( DEG2RAD( 36.0 ) ), - inner_r * std::cos( DEG2RAD( 36.0 ) ) ) // 36
684 << QPointF( 0, -1 )
685 << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ); // 324; // 0
686 return true;
687 }
688
690 polygon << QPointF( 0, -1 )
691 << QPointF( 0.5, -0.5 )
692 << QPointF( 0.25, -0.5 )
693 << QPointF( 0.25, 1 )
694 << QPointF( -0.25, 1 )
695 << QPointF( -0.25, -0.5 )
696 << QPointF( -0.5, -0.5 )
697 << QPointF( 0, -1 );
698 return true;
699
701 polygon << QPointF( 0, 0 ) << QPointF( -1, 1 ) << QPointF( -1, -1 ) << QPointF( 0, 0 );
702 return true;
703
705 polygon << QPointF( -1, -0.2 )
706 << QPointF( -1, -0.2 )
707 << QPointF( -1, 0.2 )
708 << QPointF( -0.2, 0.2 )
709 << QPointF( -0.2, 1 )
710 << QPointF( 0.2, 1 )
711 << QPointF( 0.2, 0.2 )
712 << QPointF( 1, 0.2 )
713 << QPointF( 1, -0.2 )
714 << QPointF( 0.2, -0.2 )
715 << QPointF( 0.2, -1 )
716 << QPointF( -0.2, -1 )
717 << QPointF( -0.2, -0.2 )
718 << QPointF( -1, -0.2 );
719 return true;
720
722 {
723 static constexpr double THICKNESS = 0.3;
724 static constexpr double HALF_THICKNESS = THICKNESS / 2.0;
725 static constexpr double INTERSECTION_POINT = THICKNESS / M_SQRT2;
726 static constexpr double DIAGONAL1 = M_SQRT1_2 - INTERSECTION_POINT * 0.5;
727 static constexpr double DIAGONAL2 = M_SQRT1_2 + INTERSECTION_POINT * 0.5;
728
729 polygon << QPointF( -HALF_THICKNESS, -1 )
730 << QPointF( HALF_THICKNESS, -1 )
731 << QPointF( HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
732 << QPointF( DIAGONAL1, -DIAGONAL2 )
733 << QPointF( DIAGONAL2, -DIAGONAL1 )
734 << QPointF( HALF_THICKNESS + INTERSECTION_POINT, -HALF_THICKNESS )
735 << QPointF( 1, -HALF_THICKNESS )
736 << QPointF( 1, HALF_THICKNESS )
737 << QPointF( HALF_THICKNESS + INTERSECTION_POINT, HALF_THICKNESS )
738 << QPointF( DIAGONAL2, DIAGONAL1 )
739 << QPointF( DIAGONAL1, DIAGONAL2 )
740 << QPointF( HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
741 << QPointF( HALF_THICKNESS, 1 )
742 << QPointF( -HALF_THICKNESS, 1 )
743 << QPointF( -HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
744 << QPointF( -DIAGONAL1, DIAGONAL2 )
745 << QPointF( -DIAGONAL2, DIAGONAL1 )
746 << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, HALF_THICKNESS )
747 << QPointF( -1, HALF_THICKNESS )
748 << QPointF( -1, -HALF_THICKNESS )
749 << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, -HALF_THICKNESS )
750 << QPointF( -DIAGONAL2, -DIAGONAL1 )
751 << QPointF( -DIAGONAL1, -DIAGONAL2 )
752 << QPointF( -HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
753 << QPointF( -HALF_THICKNESS, -1 );
754 return true;
755 }
756
770 return false;
771 }
772
773 return false;
774}
775
777{
778 mPath = QPainterPath();
779
780 switch ( symbol )
781 {
783
784 mPath.addEllipse( QRectF( -1, -1, 2, 2 ) ); // x,y,w,h
785 return true;
786
788 mPath.moveTo( -1, -1 );
789 mPath.addRoundedRect( -1, -1, 2, 2, 0.25, 0.25 );
790 return true;
791
793 mPath.arcTo( -1, -1, 2, 2, 0, 180 );
794 mPath.lineTo( 0, 0 );
795 return true;
796
798 mPath.arcTo( -1, -1, 2, 2, 90, 120 );
799 mPath.lineTo( 0, 0 );
800 return true;
801
803 mPath.arcTo( -1, -1, 2, 2, 90, 90 );
804 mPath.lineTo( 0, 0 );
805 return true;
806
808 mPath.moveTo( 1, 0 );
809 mPath.arcTo( -1, -1, 2, 2, 0, 180 );
810 return true;
811
813 mPath.moveTo( 0, -1 );
814 mPath.arcTo( -1, -1, 2, 2, 90, 120 );
815 return true;
816
818 mPath.moveTo( 0, -1 );
819 mPath.arcTo( -1, -1, 2, 2, 90, 90 );
820 return true;
821
823 mPath.moveTo( -1, 0 );
824 mPath.lineTo( 1, 0 ); // horizontal
825 mPath.moveTo( 0, -1 );
826 mPath.lineTo( 0, 1 ); // vertical
827 return true;
828
830 mPath.moveTo( -1, -1 );
831 mPath.lineTo( 1, 1 );
832 mPath.moveTo( 1, -1 );
833 mPath.lineTo( -1, 1 );
834 return true;
835
837 mPath.moveTo( 0, -1 );
838 mPath.lineTo( 0, 1 ); // vertical line
839 return true;
840
842 mPath.moveTo( -1, -1 );
843 mPath.lineTo( 0, 0 );
844 mPath.lineTo( -1, 1 );
845 return true;
846
848 mPath.moveTo( 0, 0.75 );
849 mPath.arcTo( 0, -1, 1, 1, -45, 210 );
850 mPath.arcTo( -1, -1, 1, 1, 15, 210 );
851 mPath.lineTo( 0, 0.75 );
852 return true;
853
878 return false;
879 }
880 return false;
881}
882
883double QgsSimpleMarkerSymbolLayerBase::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
884{
885 double scaledSize = mSize;
886
888 bool ok = true;
889 if ( hasDataDefinedSize )
890 {
893 mSize, &ok );
894 }
895
896 if ( hasDataDefinedSize && ok )
897 {
898 switch ( mScaleMethod )
899 {
901 scaledSize = std::sqrt( scaledSize );
902 break;
904 break;
905 }
906 }
907
908 return scaledSize;
909}
910
911void QgsSimpleMarkerSymbolLayerBase::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle ) const
912{
913 //offset
914 double offsetX = 0;
915 double offsetY = 0;
916 markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
917 offset = QPointF( offsetX, offsetY );
918
919 hasDataDefinedRotation = false;
920 //angle
921 bool ok = true;
924 {
927
928 // If the expression evaluation was not successful, fallback to static value
929 if ( !ok )
931
932 hasDataDefinedRotation = true;
933 }
934
935 hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || hasDataDefinedRotation;
936
937 if ( hasDataDefinedRotation )
938 {
939 // For non-point markers, "dataDefinedRotation" means following the
940 // shape (shape-data defined). For them, "field-data defined" does
941 // not work at all. TODO: if "field-data defined" ever gets implemented
942 // we'll need a way to distinguish here between the two, possibly
943 // using another flag in renderHints()
944 const QgsFeature *f = context.feature();
945 if ( f )
946 {
947 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
948 {
949 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
950 angle += m2p.mapRotation();
951 }
952 }
953 }
954
955 if ( angle )
957}
958
959
960//
961// QgsSimpleMarkerSymbolLayer
962//
963
964QgsSimpleMarkerSymbolLayer::QgsSimpleMarkerSymbolLayer( Qgis::MarkerShape shape, double size, double angle, Qgis::ScaleMethod scaleMethod, const QColor &color, const QColor &strokeColor, Qt::PenJoinStyle penJoinStyle )
965 : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
966 , mStrokeColor( strokeColor )
967 , mPenJoinStyle( penJoinStyle )
968{
969 mColor = color;
970}
971
973
975{
983
984 if ( props.contains( QStringLiteral( "name" ) ) )
985 {
986 shape = decodeShape( props[QStringLiteral( "name" )].toString() );
987 }
988 if ( props.contains( QStringLiteral( "color" ) ) )
989 color = QgsColorUtils::colorFromString( props[QStringLiteral( "color" )].toString() );
990 if ( props.contains( QStringLiteral( "color_border" ) ) )
991 {
992 //pre 2.5 projects use "color_border"
993 strokeColor = QgsColorUtils::colorFromString( props[QStringLiteral( "color_border" )].toString() );
994 }
995 else if ( props.contains( QStringLiteral( "outline_color" ) ) )
996 {
997 strokeColor = QgsColorUtils::colorFromString( props[QStringLiteral( "outline_color" )].toString() );
998 }
999 else if ( props.contains( QStringLiteral( "line_color" ) ) )
1000 {
1001 strokeColor = QgsColorUtils::colorFromString( props[QStringLiteral( "line_color" )].toString() );
1002 }
1003 if ( props.contains( QStringLiteral( "joinstyle" ) ) )
1004 {
1005 penJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() );
1006 }
1007 if ( props.contains( QStringLiteral( "size" ) ) )
1008 size = props[QStringLiteral( "size" )].toDouble();
1009 if ( props.contains( QStringLiteral( "angle" ) ) )
1010 angle = props[QStringLiteral( "angle" )].toDouble();
1011 if ( props.contains( QStringLiteral( "scale_method" ) ) )
1012 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1013
1015 if ( props.contains( QStringLiteral( "offset" ) ) )
1016 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1017 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1018 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1019 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1020 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1021 if ( props.contains( QStringLiteral( "size_unit" ) ) )
1022 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1023 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1024 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1025
1026 if ( props.contains( QStringLiteral( "outline_style" ) ) )
1027 {
1028 m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "outline_style" )].toString() ) );
1029 }
1030 else if ( props.contains( QStringLiteral( "line_style" ) ) )
1031 {
1032 m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "line_style" )].toString() ) );
1033 }
1034 if ( props.contains( QStringLiteral( "outline_width" ) ) )
1035 {
1036 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
1037 }
1038 else if ( props.contains( QStringLiteral( "line_width" ) ) )
1039 {
1040 m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
1041 }
1042 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
1043 {
1044 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
1045 }
1046 if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
1047 {
1048 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
1049 }
1050 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
1051 {
1052 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
1053 }
1054
1055 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1056 {
1057 m->setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1058 }
1059 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1060 {
1061 m->setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1062 }
1063
1064 if ( props.contains( QStringLiteral( "cap_style" ) ) )
1065 {
1066 m->setPenCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( props[QStringLiteral( "cap_style" )].toString() ) );
1067 }
1068
1070
1071 return m;
1072}
1073
1074
1076{
1077 return QStringLiteral( "SimpleMarker" );
1078}
1079
1084
1086{
1088
1089 QColor brushColor = mColor;
1090 QColor penColor = mStrokeColor;
1091
1092 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
1093 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
1094
1095 mBrush = QBrush( brushColor );
1096 mPen = QPen( penColor );
1097 mPen.setStyle( mStrokeStyle );
1098 mPen.setCapStyle( mPenCapStyle );
1099 mPen.setJoinStyle( mPenJoinStyle );
1101
1102 QColor selBrushColor = context.renderContext().selectionColor();
1103 QColor selPenColor = selBrushColor == mColor ? selBrushColor : mStrokeColor;
1104 if ( context.opacity() < 1 && !SELECTION_IS_OPAQUE )
1105 {
1106 selBrushColor.setAlphaF( context.opacity() );
1107 selPenColor.setAlphaF( context.opacity() );
1108 }
1109 mSelBrush = QBrush( selBrushColor );
1110 mSelPen = QPen( selPenColor );
1111 mSelPen.setStyle( mStrokeStyle );
1113
1115 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
1116
1117 // use caching only when:
1118 // - size, rotation, shape, color, stroke color is not data-defined
1119 // - drawing to screen (not printer)
1120 mUsingCache = !hasDataDefinedRotation && !hasDataDefinedSize && !context.forceVectorRendering()
1124
1125 if ( mUsingCache )
1126 mCachedOpacity = context.opacity();
1127
1128 if ( !shapeIsFilled( mShape ) )
1129 {
1130 // some markers can't be drawn as a polygon (circle, cross)
1131 // For these set the selected stroke color to the selected color
1132 mSelPen.setColor( selBrushColor );
1133 }
1134
1135
1136 if ( mUsingCache )
1137 {
1138 if ( !prepareCache( context ) )
1139 {
1140 mUsingCache = false;
1141 }
1142 }
1143 else
1144 {
1145 mCache = QImage();
1146 mSelCache = QImage();
1147 }
1148}
1149
1150
1152{
1153 double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
1154 const double deviceRatio = context.renderContext().devicePixelRatio();
1156 {
1157 // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
1158 // and clamp it to a reasonable range. It's the best we can do in this situation!
1159 scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
1160 }
1161
1162 // take into account angle (which is not data-defined otherwise cache wouldn't be used)
1163 if ( !qgsDoubleNear( mAngle, 0.0 ) )
1164 {
1165 scaledSize = ( std::abs( std::sin( mAngle * M_PI / 180 ) ) + std::abs( std::cos( mAngle * M_PI / 180 ) ) ) * scaledSize;
1166 }
1167 // calculate necessary image size for the cache
1168 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
1169 const int imageSize = ( static_cast< int >( scaledSize ) + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width
1170 const double center = imageSize / 2.0;
1171 if ( imageSize * deviceRatio > MAXIMUM_CACHE_WIDTH )
1172 {
1173 return false;
1174 }
1175
1176 mCache = QImage( QSize( imageSize * deviceRatio,
1177 imageSize * deviceRatio ), QImage::Format_ARGB32_Premultiplied );
1178 mCache.setDevicePixelRatio( context.renderContext().devicePixelRatio() );
1179 mCache.setDotsPerMeterX( std::round( context.renderContext().scaleFactor() * 1000 ) );
1180 mCache.setDotsPerMeterY( std::round( context.renderContext().scaleFactor() * 1000 ) );
1181 mCache.fill( 0 );
1182
1183 const bool needsBrush = shapeIsFilled( mShape );
1184
1185 QPainter p;
1186 p.begin( &mCache );
1187 p.setRenderHint( QPainter::Antialiasing );
1188 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1189 p.setPen( mPen );
1190 p.translate( QPointF( center, center ) );
1191 drawMarker( &p, context );
1192 p.end();
1193
1194 // Construct the selected version of the Cache
1195
1196 const QColor selColor = context.renderContext().selectionColor();
1197
1198 mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
1199 mSelCache.fill( 0 );
1200
1201 p.begin( &mSelCache );
1202 p.setRenderHint( QPainter::Antialiasing );
1203 p.setBrush( needsBrush ? mSelBrush : Qt::NoBrush );
1204 p.setPen( mSelPen );
1205 p.translate( QPointF( center, center ) );
1206 drawMarker( &p, context );
1207 p.end();
1208
1209 // Check that the selected version is different. If not, then re-render,
1210 // filling the background with the selection color and using the normal
1211 // colors for the symbol .. could be ugly!
1212
1213 if ( mSelCache == mCache )
1214 {
1215 p.begin( &mSelCache );
1216 p.setRenderHint( QPainter::Antialiasing );
1217 p.fillRect( 0, 0, imageSize, imageSize, selColor );
1218 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1219 p.setPen( mPen );
1220 p.translate( QPointF( center, center ) );
1221 drawMarker( &p, context );
1222 p.end();
1223 }
1224
1225 return true;
1226}
1227
1228void QgsSimpleMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
1229{
1230 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1231 //of the rendered point!
1232
1233 QPainter *p = context.renderContext().painter();
1234 if ( !p )
1235 {
1236 return;
1237 }
1238
1239 QColor brushColor = mColor;
1240 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
1241 mBrush.setColor( brushColor );
1242
1243 QColor penColor = mStrokeColor;
1244 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
1245 mPen.setColor( penColor );
1246
1247 bool ok = true;
1249 {
1252 if ( ok )
1253 {
1254 c.setAlphaF( c.alphaF() * context.opacity() );
1255 mBrush.setColor( c );
1256 }
1257 }
1259 {
1262 if ( ok )
1263 {
1264 c.setAlphaF( c.alphaF() * context.opacity() );
1265 mPen.setColor( c );
1266 mSelPen.setColor( c );
1267 }
1268 }
1270 {
1273 if ( ok )
1274 {
1277 }
1278 }
1280 {
1283 if ( ok )
1284 {
1287 }
1288 }
1290 {
1293 if ( ok )
1294 {
1295 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1296 mSelPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1297 }
1298 }
1300 {
1303 if ( ok )
1304 {
1305 mPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1306 mSelPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1307 }
1308 }
1309
1310 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
1311 if ( shapeIsFilled( shape ) )
1312 {
1313 p->setBrush( useSelectedColor ? mSelBrush : mBrush );
1314 }
1315 else
1316 {
1317 p->setBrush( Qt::NoBrush );
1318 }
1319 p->setPen( useSelectedColor ? mSelPen : mPen );
1320
1321 if ( !polygon.isEmpty() )
1322 p->drawPolygon( polygon );
1323 else
1324 p->drawPath( path );
1325}
1326
1328{
1329 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1330 //of the rendered point!
1331
1332 QPainter *p = context.renderContext().painter();
1333 if ( !p )
1334 {
1335 return;
1336 }
1337
1338 if ( mUsingCache && qgsDoubleNear( mCachedOpacity, context.opacity() ) )
1339 {
1340 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
1341 const QImage &img = useSelectedColor ? mSelCache : mCache;
1342 const double s = img.width() / img.devicePixelRatioF();
1343
1344 bool hasDataDefinedSize = false;
1345 const double scaledSize = calculateSize( context, hasDataDefinedSize );
1346
1347 bool hasDataDefinedRotation = false;
1348 QPointF offset;
1349 double angle = 0;
1350 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
1351
1352 p->drawImage( QRectF( point.x() - s / 2.0 + offset.x(),
1353 point.y() - s / 2.0 + offset.y(),
1354 s, s ), img );
1355 }
1356 else
1357 {
1359 }
1360}
1361
1363{
1364 QVariantMap map;
1365 map[QStringLiteral( "name" )] = encodeShape( mShape );
1366 map[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mColor );
1367 map[QStringLiteral( "outline_color" )] = QgsColorUtils::colorToString( mStrokeColor );
1368 map[QStringLiteral( "size" )] = QString::number( mSize );
1369 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1370 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1371 map[QStringLiteral( "angle" )] = QString::number( mAngle );
1372 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1373 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1374 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1375 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1376 map[QStringLiteral( "outline_style" )] = QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle );
1377 map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
1378 map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
1379 map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
1380 map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
1381 map[QStringLiteral( "cap_style" )] = QgsSymbolLayerUtils::encodePenCapStyle( mPenCapStyle );
1382 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
1383 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
1384 return map;
1385}
1386
1406
1407void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1408{
1409 // <Graphic>
1410 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
1411 element.appendChild( graphicElem );
1412
1414 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
1416
1417 // <Rotation>
1418 QString angleFunc;
1419
1421 {
1423 }
1424 else
1425 {
1426 bool ok;
1427 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
1428 if ( !ok )
1429 {
1430 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
1431 }
1432 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
1433 {
1434 angleFunc = QString::number( angle + mAngle );
1435 }
1436 }
1437
1438 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
1439
1440 // <Displacement>
1441 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
1443}
1444
1445QString QgsSimpleMarkerSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
1446{
1447 Q_UNUSED( mmScaleFactor )
1448 Q_UNUSED( mapUnitScaleFactor )
1449#if 0
1450 QString ogrType = "3"; //default is circle
1451 if ( mName == "square" )
1452 {
1453 ogrType = "5";
1454 }
1455 else if ( mName == "triangle" )
1456 {
1457 ogrType = "7";
1458 }
1459 else if ( mName == "star" )
1460 {
1461 ogrType = "9";
1462 }
1463 else if ( mName == "circle" )
1464 {
1465 ogrType = "3";
1466 }
1467 else if ( mName == "cross" )
1468 {
1469 ogrType = "0";
1470 }
1471 else if ( mName == "x" || mName == "cross2" )
1472 {
1473 ogrType = "1";
1474 }
1475 else if ( mName == "line" )
1476 {
1477 ogrType = "10";
1478 }
1479
1480 QString ogrString;
1481 ogrString.append( "SYMBOL(" );
1482 ogrString.append( "id:" );
1483 ogrString.append( '\"' );
1484 ogrString.append( "ogr-sym-" );
1485 ogrString.append( ogrType );
1486 ogrString.append( '\"' );
1487 ogrString.append( ",c:" );
1488 ogrString.append( mColor.name() );
1489 ogrString.append( ",o:" );
1490 ogrString.append( mStrokeColor.name() );
1491 ogrString.append( QString( ",s:%1mm" ).arg( mSize ) );
1492 ogrString.append( ')' );
1493 return ogrString;
1494#endif //0
1495
1496 QString ogrString;
1497 ogrString.append( "PEN(" );
1498 ogrString.append( "c:" );
1499 ogrString.append( mColor.name() );
1500 ogrString.append( ",w:" );
1501 ogrString.append( QString::number( mSize ) );
1502 ogrString.append( "mm" );
1503 ogrString.append( ")" );
1504 return ogrString;
1505}
1506
1508{
1509 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
1510
1511 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
1512 if ( graphicElem.isNull() )
1513 return nullptr;
1514
1515 QString name = QStringLiteral( "square" );
1516 QColor color, strokeColor;
1517 double strokeWidth, size;
1518 Qt::PenStyle strokeStyle;
1519
1521 return nullptr;
1522
1523 double angle = 0.0;
1524 QString angleFunc;
1525 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
1526 {
1527 bool ok;
1528 const double d = angleFunc.toDouble( &ok );
1529 if ( ok )
1530 angle = d;
1531 }
1532
1533 QPointF offset;
1535
1536 const Qgis::MarkerShape shape = decodeShape( name );
1537
1538 double scaleFactor = 1.0;
1539 const QString uom = element.attribute( QStringLiteral( "uom" ) );
1540 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
1541 size = size * scaleFactor;
1542 offset.setX( offset.x() * scaleFactor );
1543 offset.setY( offset.y() * scaleFactor );
1544
1546 m->setOutputUnit( sldUnitSize );
1547 m->setColor( color );
1549 m->setAngle( angle );
1550 m->setOffset( offset );
1553 return m;
1554}
1555
1557{
1558 Q_UNUSED( context )
1559
1560 if ( mPolygon.count() != 0 )
1561 {
1562 p->drawPolygon( mPolygon );
1563 }
1564 else
1565 {
1566 p->drawPath( mPath );
1567 }
1568}
1569
1570bool QgsSimpleMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
1571{
1572 //data defined size?
1573 double size = mSize;
1574
1575 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
1576
1577 //data defined size
1578 bool ok = true;
1579 if ( hasDataDefinedSize )
1580 {
1582
1583 if ( ok )
1584 {
1585 switch ( mScaleMethod )
1586 {
1588 size = std::sqrt( size );
1589 break;
1591 break;
1592 }
1593 }
1594 }
1595
1597 {
1598 size *= mmMapUnitScaleFactor;
1599 }
1600
1602 {
1604 }
1605 const double halfSize = size / 2.0;
1606
1607 //strokeWidth
1608 double strokeWidth = mStrokeWidth;
1609
1611 {
1614 }
1617 {
1619 }
1620
1621 //color
1622 QColor pc = mPen.color();
1623 QColor bc = mBrush.color();
1625 {
1628 }
1630 {
1633 }
1634
1635 //offset
1636 double offsetX = 0;
1637 double offsetY = 0;
1638 markerOffset( context, offsetX, offsetY );
1639 offsetX *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1640 offsetY *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1641
1642
1643 QPointF off( offsetX, offsetY );
1644
1645 //angle
1646 double angle = mAngle + mLineAngle;
1648 {
1651 }
1652
1655 {
1657 const QString shapeName = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::Name, context.renderContext().expressionContext(), QString(), &ok );
1658 if ( ok )
1659 {
1660 shape = decodeShape( shapeName, &ok );
1661 if ( !ok )
1662 shape = mShape;
1663 }
1664 }
1665
1666 if ( angle )
1667 off = _rotatedOffset( off, angle );
1668
1670
1671 QTransform t;
1672 t.translate( shift.x() + off.x(), shift.y() - off.y() );
1673
1674 if ( !qgsDoubleNear( angle, 0.0 ) )
1675 t.rotate( -angle );
1676
1677 QPolygonF polygon;
1678 if ( shapeToPolygon( shape, polygon ) )
1679 {
1680 t.scale( halfSize, -halfSize );
1681
1682 polygon = t.map( polygon );
1683
1685 p.reserve( polygon.size() );
1686 for ( int i = 0; i < polygon.size(); i++ )
1687 {
1688 p << QgsPoint( polygon[i] );
1689 }
1690
1691 if ( mBrush.style() != Qt::NoBrush )
1692 e.writePolygon( QgsRingSequence() << p, layerName, QStringLiteral( "SOLID" ), bc );
1693 if ( mPen.style() != Qt::NoPen )
1694 e.writePolyline( p, layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1695 }
1696 else if ( shape == Qgis::MarkerShape::Circle )
1697 {
1698 shift += QPointF( off.x(), -off.y() );
1699 if ( mBrush.style() != Qt::NoBrush )
1700 e.writeFilledCircle( layerName, bc, QgsPoint( shift ), halfSize );
1701 if ( mPen.style() != Qt::NoPen )
1702 e.writeCircle( layerName, pc, QgsPoint( shift ), halfSize, QStringLiteral( "CONTINUOUS" ), strokeWidth );
1703 }
1704 else if ( shape == Qgis::MarkerShape::Line )
1705 {
1706 const QPointF pt1 = t.map( QPointF( 0, -halfSize ) );
1707 const QPointF pt2 = t.map( QPointF( 0, halfSize ) );
1708
1709 if ( mPen.style() != Qt::NoPen )
1710 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1711 }
1712 else if ( shape == Qgis::MarkerShape::Cross )
1713 {
1714 if ( mPen.style() != Qt::NoPen )
1715 {
1716 const QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
1717 const QPointF pt2 = t.map( QPointF( halfSize, 0 ) );
1718 const QPointF pt3 = t.map( QPointF( 0, -halfSize ) );
1719 const QPointF pt4 = t.map( QPointF( 0, halfSize ) );
1720
1721 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1722 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1723 }
1724 }
1725 else if ( shape == Qgis::MarkerShape::Cross2 )
1726 {
1727 if ( mPen.style() != Qt::NoPen )
1728 {
1729 const QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
1730 const QPointF pt2 = t.map( QPointF( halfSize, halfSize ) );
1731 const QPointF pt3 = t.map( QPointF( halfSize, -halfSize ) );
1732 const QPointF pt4 = t.map( QPointF( -halfSize, halfSize ) );
1733
1734 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1735 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1736 }
1737 }
1738 else if ( shape == Qgis::MarkerShape::ArrowHead )
1739 {
1740 if ( mPen.style() != Qt::NoPen )
1741 {
1742 const QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
1743 const QPointF pt2 = t.map( QPointF( 0, 0 ) );
1744 const QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
1745
1746 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1747 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1748 }
1749 }
1750 else
1751 {
1752 QgsDebugError( QStringLiteral( "Unsupported dxf marker name %1" ).arg( encodeShape( shape ) ) );
1753 return false;
1754 }
1755
1756 return true;
1757}
1758
1759
1765
1774
1780
1789
1796
1798{
1799 QRectF symbolBounds = QgsSimpleMarkerSymbolLayerBase::bounds( point, context );
1800
1801 // need to account for stroke width
1802 double penWidth = mStrokeWidth;
1803 bool ok = true;
1805 {
1808 if ( ok )
1809 {
1810 penWidth = strokeWidth;
1811 }
1812 }
1815 {
1818 if ( ok && strokeStyle == QLatin1String( "no" ) )
1819 {
1820 penWidth = 0.0;
1821 }
1822 }
1823 else if ( mStrokeStyle == Qt::NoPen )
1824 penWidth = 0;
1825
1826 //antialiasing, add 1 pixel
1827 penWidth += 1;
1828
1829 //extend bounds by pen width / 2.0
1830 symbolBounds.adjust( -penWidth / 2.0, -penWidth / 2.0,
1831 penWidth / 2.0, penWidth / 2.0 );
1832
1833 return symbolBounds;
1834}
1835
1836void QgsSimpleMarkerSymbolLayer::setColor( const QColor &color )
1837{
1838 if ( shapeIsFilled( mShape ) )
1839 {
1841 }
1842 else
1843 {
1845 }
1846}
1847
1849{
1850 if ( shapeIsFilled( mShape ) )
1851 {
1852 return fillColor();
1853 }
1854 else
1855 {
1856 return strokeColor();
1857 }
1858}
1859
1860
1861
1862
1863//
1864// QgsFilledMarkerSymbolLayer
1865//
1866
1868 : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
1869{
1870 mFill.reset( static_cast<QgsFillSymbol *>( QgsFillSymbol::createSimple( QVariantMap() ) ) );
1871}
1872
1874
1876{
1877 QString name = DEFAULT_SIMPLEMARKER_NAME;
1881
1882 if ( props.contains( QStringLiteral( "name" ) ) )
1883 name = props[QStringLiteral( "name" )].toString();
1884 if ( props.contains( QStringLiteral( "size" ) ) )
1885 size = props[QStringLiteral( "size" )].toDouble();
1886 if ( props.contains( QStringLiteral( "angle" ) ) )
1887 angle = props[QStringLiteral( "angle" )].toDouble();
1888 if ( props.contains( QStringLiteral( "scale_method" ) ) )
1889 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1890
1892 if ( props.contains( QStringLiteral( "offset" ) ) )
1893 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1894 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1895 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1896 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1897 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1898 if ( props.contains( QStringLiteral( "size_unit" ) ) )
1899 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1900 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1901 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1902 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1903 {
1904 m->setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1905 }
1906 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1907 {
1908 m->setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1909 }
1910
1912
1914
1915 return m;
1916}
1917
1919{
1920 return QStringLiteral( "FilledMarker" );
1921}
1922
1924{
1925 if ( mFill )
1926 {
1927 mFill->setRenderHints( mFill->renderHints() | Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol );
1928 mFill->startRender( context.renderContext(), context.fields() );
1929 }
1930
1932}
1933
1935{
1936 if ( mFill )
1937 {
1938 mFill->stopRender( context.renderContext() );
1939 }
1940}
1941
1943{
1944 installMasks( context, true );
1945
1946 // The base class version passes this on to the subsymbol, but we deliberately don't do that here.
1947}
1948
1950{
1951 removeMasks( context, true );
1952
1953 // The base class version passes this on to the subsymbol, but we deliberately don't do that here.
1954}
1955
1957{
1958 QVariantMap map;
1959 map[QStringLiteral( "name" )] = encodeShape( mShape );
1960 map[QStringLiteral( "size" )] = QString::number( mSize );
1961 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1962 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1963 map[QStringLiteral( "angle" )] = QString::number( mAngle );
1964 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1965 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1966 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1967 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1968 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
1969 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
1970
1971 if ( mFill )
1972 {
1973 map[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mFill->color() );
1974 }
1975 return map;
1976}
1977
1986
1988{
1989 return mFill.get();
1990}
1991
1993{
1994 if ( symbol && symbol->type() == Qgis::SymbolType::Fill )
1995 {
1996 mFill.reset( static_cast<QgsFillSymbol *>( symbol ) );
1997 return true;
1998 }
1999 else
2000 {
2001 delete symbol;
2002 return false;
2003 }
2004}
2005
2007{
2008 if ( mFill )
2009 {
2010 return QgsSymbolLayerUtils::estimateMaxSymbolBleed( mFill.get(), context );
2011 }
2012 return 0;
2013}
2014
2016{
2017 QSet<QString> attr = QgsSimpleMarkerSymbolLayerBase::usedAttributes( context );
2018 if ( mFill )
2019 attr.unite( mFill->usedAttributes( context ) );
2020 return attr;
2021}
2022
2024{
2026 return true;
2027 if ( mFill && mFill->hasDataDefinedProperties() )
2028 return true;
2029 return false;
2030}
2031
2033{
2034 mColor = c;
2035 if ( mFill )
2036 mFill->setColor( c );
2037}
2038
2040{
2041 return mFill ? mFill->color() : mColor;
2042}
2043
2050
2052{
2054 if ( mFill )
2055 mFill->setOutputUnit( unit );
2056}
2057
2058void QgsFilledMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
2059{
2060 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
2061 //of the rendered point!
2062
2063 QPainter *p = context.renderContext().painter();
2064 if ( !p )
2065 {
2066 return;
2067 }
2068
2069 const double prevOpacity = mFill->opacity();
2070 mFill->setOpacity( mFill->opacity() * context.opacity() );
2071
2072 if ( shapeIsFilled( shape ) )
2073 {
2074 p->setBrush( Qt::red );
2075 }
2076 else
2077 {
2078 p->setBrush( Qt::NoBrush );
2079 }
2080 p->setPen( Qt::black );
2081
2082 const bool prevIsSubsymbol = context.renderContext().flags() & Qgis::RenderContextFlag::RenderingSubSymbol;
2084
2085 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
2086 if ( !polygon.isEmpty() )
2087 {
2088 mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
2089 }
2090 else
2091 {
2092 const QPolygonF poly = path.toFillPolygon();
2093 mFill->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
2094 }
2095
2097
2098 mFill->setOpacity( prevOpacity );
2099}
2100
2101
2103
2104
2105QgsSvgMarkerSymbolLayer::QgsSvgMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
2106{
2107 mSize = size;
2108 mAngle = angle;
2109 mOffset = QPointF( 0, 0 );
2111 mStrokeWidth = 0.2;
2113 mColor = QColor( 35, 35, 35 );
2114 mStrokeColor = QColor( 35, 35, 35 );
2115 setPath( path );
2116}
2117
2119 : QgsMarkerSymbolLayer( other )
2120 , mPath( other.mPath )
2121 , mDefaultAspectRatio( other.mDefaultAspectRatio )
2122 , mFixedAspectRatio( other.mFixedAspectRatio )
2123 , mHasFillParam( other.mHasFillParam )
2124 , mStrokeColor( other.mStrokeColor )
2125 , mStrokeWidth( other.mStrokeWidth )
2126 , mParameters( other.mParameters )
2127 , mStrokeWidthUnit( other.mStrokeWidthUnit )
2128 , mStrokeWidthMapUnitScale( other.mStrokeWidthMapUnitScale )
2129{
2130}
2131
2133
2135{
2136 QString name;
2140
2141 if ( props.contains( QStringLiteral( "name" ) ) )
2142 name = props[QStringLiteral( "name" )].toString();
2143 if ( props.contains( QStringLiteral( "size" ) ) )
2144 size = props[QStringLiteral( "size" )].toDouble();
2145 if ( props.contains( QStringLiteral( "angle" ) ) )
2146 angle = props[QStringLiteral( "angle" )].toDouble();
2147 if ( props.contains( QStringLiteral( "scale_method" ) ) )
2148 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
2149
2151
2152 if ( props.contains( QStringLiteral( "size_unit" ) ) )
2153 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
2154 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
2155 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
2156 if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
2157 m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
2158 if ( props.contains( QStringLiteral( "offset" ) ) )
2159 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
2160 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
2161 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
2162 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2163 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
2164 if ( props.contains( QStringLiteral( "fill" ) ) )
2165 {
2166 //pre 2.5 projects used "fill"
2167 m->setFillColor( QgsColorUtils::colorFromString( props[QStringLiteral( "fill" )].toString() ) );
2168 }
2169 else if ( props.contains( QStringLiteral( "color" ) ) )
2170 {
2171 m->setFillColor( QgsColorUtils::colorFromString( props[QStringLiteral( "color" )].toString() ) );
2172 }
2173 if ( props.contains( QStringLiteral( "outline" ) ) )
2174 {
2175 //pre 2.5 projects used "outline"
2176 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "outline" )].toString() ) );
2177 }
2178 else if ( props.contains( QStringLiteral( "outline_color" ) ) )
2179 {
2180 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "outline_color" )].toString() ) );
2181 }
2182 else if ( props.contains( QStringLiteral( "line_color" ) ) )
2183 {
2184 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "line_color" )].toString() ) );
2185 }
2186
2187 if ( props.contains( QStringLiteral( "outline-width" ) ) )
2188 {
2189 //pre 2.5 projects used "outline-width"
2190 m->setStrokeWidth( props[QStringLiteral( "outline-width" )].toDouble() );
2191 }
2192 else if ( props.contains( QStringLiteral( "outline_width" ) ) )
2193 {
2194 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
2195 }
2196 else if ( props.contains( QStringLiteral( "line_width" ) ) )
2197 {
2198 m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
2199 }
2200
2201 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
2202 {
2203 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
2204 }
2205 else if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
2206 {
2207 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
2208 }
2209 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
2210 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
2211
2212 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2213 {
2214 m->setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2215 }
2216 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2217 {
2218 m->setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2219 }
2220
2222
2224
2225 if ( props.contains( QStringLiteral( "parameters" ) ) )
2226 {
2227 const QVariantMap parameters = props[QStringLiteral( "parameters" )].toMap();
2229 }
2230
2231 return m;
2232}
2233
2234void QgsSvgMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
2235{
2236 const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
2237 if ( it != properties.end() )
2238 {
2239 if ( saving )
2240 {
2241 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
2242 }
2243 else
2244 {
2245 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
2246 }
2247 }
2248}
2249
2250void QgsSvgMarkerSymbolLayer::setPath( const QString &path )
2251{
2253 mHasFillParam = false;
2254 mPath = path;
2255 QColor defaultFillColor, defaultStrokeColor;
2256 double strokeWidth, fillOpacity, strokeOpacity;
2257 bool hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
2258 bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
2259 QgsApplication::svgCache()->containsParams( path, mHasFillParam, hasDefaultFillColor, defaultFillColor,
2260 hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
2261 hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2262 hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
2263 hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
2264
2265 const double newFillOpacity = hasFillOpacityParam ? fillColor().alphaF() : 1.0;
2266 const double newStrokeOpacity = hasStrokeOpacityParam ? strokeColor().alphaF() : 1.0;
2267
2268 if ( hasDefaultFillColor )
2269 {
2270 defaultFillColor.setAlphaF( newFillOpacity );
2271 setFillColor( defaultFillColor );
2272 }
2273 if ( hasDefaultFillOpacity )
2274 {
2275 QColor c = fillColor();
2276 c.setAlphaF( fillOpacity );
2277 setFillColor( c );
2278 }
2279 if ( hasDefaultStrokeColor )
2280 {
2281 defaultStrokeColor.setAlphaF( newStrokeOpacity );
2282 setStrokeColor( defaultStrokeColor );
2283 }
2284 if ( hasDefaultStrokeWidth )
2285 {
2287 }
2288 if ( hasDefaultStrokeOpacity )
2289 {
2290 QColor c = strokeColor();
2291 c.setAlphaF( strokeOpacity );
2292 setStrokeColor( c );
2293 }
2294
2296}
2297
2299{
2300 if ( mDefaultAspectRatio == 0.0 )
2301 {
2302 //size
2303 const double size = mSize;
2304 //assume 88 dpi as standard value
2305 const double widthScaleFactor = 3.465;
2306 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( mPath, size, mColor, mStrokeColor, mStrokeWidth, widthScaleFactor );
2307 // set default aspect ratio
2308 mDefaultAspectRatio = svgViewbox.isValid() ? svgViewbox.height() / svgViewbox.width() : 0.0;
2309 }
2310 return mDefaultAspectRatio;
2311}
2312
2314{
2315 const bool aPreservedAspectRatio = preservedAspectRatio();
2316 if ( aPreservedAspectRatio && !par )
2317 {
2319 }
2320 else if ( !aPreservedAspectRatio && par )
2321 {
2322 mFixedAspectRatio = 0.0;
2323 }
2324 return preservedAspectRatio();
2325}
2326
2327void QgsSvgMarkerSymbolLayer::setParameters( const QMap<QString, QgsProperty> &parameters )
2328{
2330}
2331
2332
2334{
2335 return QStringLiteral( "SvgMarker" );
2336}
2337
2342
2344{
2345 QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
2346 Q_UNUSED( context )
2347}
2348
2350{
2351 Q_UNUSED( context )
2352}
2353
2355{
2356 QPainter *p = context.renderContext().painter();
2357 if ( !p )
2358 return;
2359
2360 bool hasDataDefinedSize = false;
2361 const double scaledWidth = calculateSize( context, hasDataDefinedSize );
2362 const double devicePixelRatio = context.renderContext().devicePixelRatio();
2363 const double width = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2364
2365 //don't render symbols with a width below one or above 10,000 pixels
2366 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2367 {
2368 return;
2369 }
2370
2371 const QgsScopedQPainterState painterState( p );
2372
2373 bool hasDataDefinedAspectRatio = false;
2374 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2375 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2376
2378
2379 double strokeWidth = mStrokeWidth;
2381 {
2384 }
2386
2387 QColor fillColor = mColor;
2388 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
2389 if ( useSelectedColor && mHasFillParam )
2390 {
2392 }
2394 {
2397 }
2398
2399 QColor strokeColor = mStrokeColor;
2401 {
2404 }
2405
2406 QString path = mPath;
2408 {
2411 context.renderContext().pathResolver() );
2413 {
2414 // adjust height of data defined path
2415 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2416 context.renderContext().scaleFactor(), aspectRatio,
2417 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2418 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2419 }
2420 }
2421
2422 QPointF outputOffset;
2423 double angle = 0.0;
2424 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2425
2426 p->translate( point + outputOffset );
2427
2428 const bool rotated = !qgsDoubleNear( angle, 0 );
2429 if ( rotated )
2430 p->rotate( angle );
2431
2432 bool fitsInCache = true;
2433 bool usePict = true;
2435 if ( ( !context.forceVectorRendering() && !rotated ) || ( useSelectedColor && rasterizeSelected ) )
2436 {
2437 QImage img = QgsApplication::svgCache()->svgAsImage( path, width * devicePixelRatio, fillColor, strokeColor, strokeWidth,
2438 context.renderContext().scaleFactor(), fitsInCache, aspectRatio,
2439 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2440 if ( fitsInCache && img.width() > 1 )
2441 {
2442 usePict = false;
2443
2444 if ( useSelectedColor )
2445 {
2447 }
2448
2449 //consider transparency
2450 if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2451 {
2452 QImage transparentImage = img.copy();
2453 QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2454 if ( devicePixelRatio == 1 )
2455 {
2456 p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
2457 }
2458 else
2459 {
2460 p->drawImage( QRectF( -transparentImage.width() / 2.0 / devicePixelRatio, -transparentImage.height() / 2.0 / devicePixelRatio,
2461 transparentImage.width() / devicePixelRatio, transparentImage.height() / devicePixelRatio
2462 ), transparentImage );
2463 }
2464 }
2465 else
2466 {
2467 if ( devicePixelRatio == 1 )
2468 {
2469 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2470 }
2471 else
2472 {
2473 p->drawImage( QRectF( -img.width() / 2.0 / devicePixelRatio, -img.height() / 2.0 / devicePixelRatio,
2474 img.width() / devicePixelRatio, img.height() / devicePixelRatio ), img );
2475 }
2476 }
2477 }
2478 }
2479
2480 if ( usePict || !fitsInCache )
2481 {
2482 p->setOpacity( context.opacity() );
2484 context.renderContext().scaleFactor(), context.forceVectorRendering(), aspectRatio,
2485 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2486 if ( pct.width() > 1 )
2487 {
2488 QgsPainting::drawPicture( p, QPointF( 0, 0 ), pct );
2489 }
2490 }
2491
2492 // workaround issue with nested QPictures forgetting antialiasing flag - see https://github.com/qgis/QGIS/issues/22909
2494}
2495
2496double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2497{
2498 double scaledSize = mSize;
2500
2501 bool ok = true;
2502 if ( hasDataDefinedSize )
2503 {
2506 }
2507 else
2508 {
2510 if ( hasDataDefinedSize )
2511 {
2514 }
2515 }
2516
2517 if ( hasDataDefinedSize && ok )
2518 {
2519 switch ( mScaleMethod )
2520 {
2522 scaledSize = std::sqrt( scaledSize );
2523 break;
2525 break;
2526 }
2527 }
2528
2529 return scaledSize;
2530}
2531
2532double QgsSvgMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2533{
2535 if ( !hasDataDefinedAspectRatio )
2536 return mFixedAspectRatio;
2537
2539 return 0.0;
2540
2541 double scaledAspectRatio = mDefaultAspectRatio;
2542 if ( mFixedAspectRatio > 0.0 )
2543 scaledAspectRatio = mFixedAspectRatio;
2544
2545 const double defaultHeight = mSize * scaledAspectRatio;
2546 scaledAspectRatio = defaultHeight / scaledSize;
2547
2548 bool ok = true;
2549 double scaledHeight = scaledSize * scaledAspectRatio;
2551 {
2552 context.setOriginalValueVariable( defaultHeight );
2554 }
2555
2556 if ( hasDataDefinedAspectRatio && ok )
2557 {
2558 switch ( mScaleMethod )
2559 {
2561 scaledHeight = sqrt( scaledHeight );
2562 break;
2564 break;
2565 }
2566 }
2567
2568 scaledAspectRatio = scaledHeight / scaledSize;
2569
2570 return scaledAspectRatio;
2571}
2572
2573void QgsSvgMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
2574{
2575 //offset
2576 double offsetX = 0;
2577 double offsetY = 0;
2578 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
2579 offset = QPointF( offsetX, offsetY );
2580
2583 {
2586 }
2587
2589 if ( hasDataDefinedRotation )
2590 {
2591 // For non-point markers, "dataDefinedRotation" means following the
2592 // shape (shape-data defined). For them, "field-data defined" does
2593 // not work at all. TODO: if "field-data defined" ever gets implemented
2594 // we'll need a way to distinguish here between the two, possibly
2595 // using another flag in renderHints()
2596 const QgsFeature *f = context.feature();
2597 if ( f )
2598 {
2599 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
2600 {
2601 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2602 angle += m2p.mapRotation();
2603 }
2604 }
2605 }
2606
2607 if ( angle )
2609}
2610
2611
2613{
2614 QVariantMap map;
2615 map[QStringLiteral( "name" )] = mPath;
2616 map[QStringLiteral( "size" )] = QString::number( mSize );
2617 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2618 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2619 map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2620 map[QStringLiteral( "angle" )] = QString::number( mAngle );
2621 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2622 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2623 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2624 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2625 map[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mColor );
2626 map[QStringLiteral( "outline_color" )] = QgsColorUtils::colorToString( mStrokeColor );
2627 map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
2628 map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2629 map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2630 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
2631 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
2632
2633 map[QStringLiteral( "parameters" )] = QgsProperty::propertyMapToVariantMap( mParameters );
2634
2635 return map;
2636}
2637
2644
2649
2655
2657{
2659 if ( unit != mStrokeWidthUnit )
2660 {
2662 }
2663 return unit;
2664}
2665
2671
2680
2681void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
2682{
2683 // <Graphic>
2684 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2685 element.appendChild( graphicElem );
2686
2687 // encode a parametric SVG reference
2688 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
2691
2692 // <Rotation>
2693 QString angleFunc;
2694 bool ok;
2695 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2696 if ( !ok )
2697 {
2698 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
2699 }
2700 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2701 {
2702 angleFunc = QString::number( angle + mAngle );
2703 }
2704
2705 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2706
2707 // <Displacement>
2708 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2710}
2711
2713{
2714 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
2715
2716 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
2717 if ( graphicElem.isNull() )
2718 return nullptr;
2719
2720 QString path, mimeType;
2721 // Unused and to be DEPRECATED in externalGraphicFromSld
2722 QColor fillColor_;
2723 double size;
2724
2725 if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor_, size ) )
2726 return nullptr;
2727
2728 double scaleFactor = 1.0;
2729 const QString uom = element.attribute( QStringLiteral( "uom" ) );
2730 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
2731 size = size * scaleFactor;
2732
2733 if ( mimeType != QLatin1String( "image/svg+xml" ) )
2734 return nullptr;
2735
2736 double angle = 0.0;
2737 QString angleFunc;
2738 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2739 {
2740 bool ok;
2741 const double d = angleFunc.toDouble( &ok );
2742 if ( ok )
2743 angle = d;
2744 }
2745
2746 QPointF offset;
2748
2749 // Extract parameters from URL
2750 QString realPath { path };
2751 QUrl svgUrl { path };
2752
2753 // Because color definition can start with '#', the url parsing won't recognize the query string entirely
2754 QUrlQuery queryString;
2755
2756 if ( svgUrl.hasQuery() && svgUrl.hasFragment() )
2757 {
2758 const QString queryPart { path.mid( path.indexOf( '?' ) + 1 ) };
2759 queryString.setQuery( queryPart );
2760 }
2761
2762 // Remove query for simple file paths
2763 if ( svgUrl.scheme().isEmpty() || svgUrl.isLocalFile() )
2764 {
2765 svgUrl.setQuery( QString() );
2766 realPath = svgUrl.path();
2767 }
2768
2770
2771 QMap<QString, QgsProperty> params;
2772
2773 bool ok;
2774
2775 if ( queryString.hasQueryItem( QStringLiteral( "fill" ) ) )
2776 {
2777 const QColor fillColor { queryString.queryItemValue( QStringLiteral( "fill" ) ) };
2778 m->setFillColor( fillColor );
2779 }
2780
2781 if ( queryString.hasQueryItem( QStringLiteral( "fill-opacity" ) ) )
2782 {
2783 const double alpha { queryString.queryItemValue( QStringLiteral( "fill-opacity" ) ).toDouble( &ok ) };
2784 if ( ok )
2785 {
2786 params.insert( QStringLiteral( "fill-opacity" ), QgsProperty::fromValue( alpha ) );
2787 }
2788 }
2789
2790 if ( queryString.hasQueryItem( QStringLiteral( "outline" ) ) )
2791 {
2792 const QColor strokeColor { queryString.queryItemValue( QStringLiteral( "outline" ) ) };
2794 }
2795
2796 if ( queryString.hasQueryItem( QStringLiteral( "outline-opacity" ) ) )
2797 {
2798 const double alpha { queryString.queryItemValue( QStringLiteral( "outline-opacity" ) ).toDouble( &ok ) };
2799 if ( ok )
2800 {
2801 params.insert( QStringLiteral( "outline-opacity" ), QgsProperty::fromValue( alpha ) );
2802 }
2803 }
2804
2805 if ( queryString.hasQueryItem( QStringLiteral( "outline-width" ) ) )
2806 {
2807 const int width { queryString.queryItemValue( QStringLiteral( "outline-width" ) ).toInt( &ok )};
2808 if ( ok )
2809 {
2810 m->setStrokeWidth( width );
2811 }
2812 }
2813
2814 if ( ! params.isEmpty() )
2815 {
2816 m->setParameters( params );
2817 }
2818
2819 m->setOutputUnit( sldUnitSize );
2820 m->setAngle( angle );
2821 m->setOffset( offset );
2822 return m;
2823}
2824
2825bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
2826{
2827 //size
2828 double size = mSize;
2829
2830 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
2831
2832 bool ok = true;
2833 if ( hasDataDefinedSize )
2834 {
2837 }
2838
2839 if ( hasDataDefinedSize && ok )
2840 {
2841 switch ( mScaleMethod )
2842 {
2844 size = std::sqrt( size );
2845 break;
2847 break;
2848 }
2849 }
2850
2852 {
2853 size *= mmMapUnitScaleFactor;
2854 }
2855
2856//offset, angle
2857 QPointF offset = mOffset;
2858
2860 {
2863 const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
2864 if ( ok )
2865 offset = res;
2866 }
2867 const double offsetX = offset.x();
2868 const double offsetY = offset.y();
2869
2870 QPointF outputOffset( offsetX, offsetY );
2871
2872 double angle = mAngle + mLineAngle;
2874 {
2877 }
2878
2879 if ( angle )
2880 outputOffset = _rotatedOffset( outputOffset, angle );
2881
2883
2884 QString path = mPath;
2886 {
2889 context.renderContext().pathResolver() );
2890 }
2891
2892 double strokeWidth = mStrokeWidth;
2894 {
2897 }
2899
2900 QColor fillColor = mColor;
2902 {
2905 }
2906
2907 QColor strokeColor = mStrokeColor;
2909 {
2912 }
2913
2915
2916 const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, size, fillColor, strokeColor, strokeWidth,
2918 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2919
2920 QSvgRenderer r( svgContent );
2921 if ( !r.isValid() )
2922 return false;
2923
2924 QgsDxfPaintDevice pd( &e );
2925 pd.setDrawingSize( QSizeF( r.defaultSize() ) );
2926
2927 QSizeF outSize( r.defaultSize() );
2928 outSize.scale( size, size, Qt::KeepAspectRatio );
2929
2930 QPainter p;
2931 p.begin( &pd );
2932 if ( !qgsDoubleNear( angle, 0.0 ) )
2933 {
2934 p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
2935 p.rotate( angle );
2936 p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
2937 }
2938 pd.setShift( shift + QPointF( outputOffset.x(), -outputOffset.y() ) );
2939 pd.setOutputSize( QRectF( -outSize.width() / 2.0, -outSize.height() / 2.0, outSize.width(), outSize.height() ) );
2940 pd.setLayer( layerName );
2941 r.render( &p );
2942 p.end();
2943 return true;
2944}
2945
2947{
2948 bool hasDataDefinedSize = false;
2949 double scaledWidth = calculateSize( context, hasDataDefinedSize );
2950
2951 bool hasDataDefinedAspectRatio = false;
2952 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2953 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2954
2955 scaledWidth = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2956 scaledHeight = context.renderContext().convertToPainterUnits( scaledHeight, mSizeUnit, mSizeMapUnitScale );
2957
2958 //don't render symbols with size below one or above 10,000 pixels
2959 if ( static_cast< int >( scaledWidth ) < 1 || 10000.0 < scaledWidth )
2960 {
2961 return QRectF();
2962 }
2963
2964 QPointF outputOffset;
2965 double angle = 0.0;
2966 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2967
2968 double strokeWidth = mStrokeWidth;
2970 {
2973 }
2975
2976 QString path = mPath;
2978 {
2981 context.renderContext().pathResolver() );
2983 {
2984 // need to get colors to take advantage of cached SVGs
2985 QColor fillColor = mColor;
2987 {
2990 }
2991
2992 const QColor strokeColor = mStrokeColor;
2994 {
2997 }
2998
3000
3001 // adjust height of data defined path
3002 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
3003 context.renderContext().scaleFactor(), aspectRatio,
3004 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
3005 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
3006 }
3007 }
3008
3009 QTransform transform;
3010 // move to the desired position
3011 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3012
3013 if ( !qgsDoubleNear( angle, 0.0 ) )
3014 transform.rotate( angle );
3015
3016 //antialiasing
3017 strokeWidth += 1.0 / 2.0;
3018
3019 QRectF symbolBounds = transform.mapRect( QRectF( -scaledWidth / 2.0,
3020 -scaledHeight / 2.0,
3021 scaledWidth,
3022 scaledHeight ) );
3023
3024 //extend bounds by pen width / 2.0
3025 symbolBounds.adjust( -strokeWidth / 2.0, -strokeWidth / 2.0,
3026 strokeWidth / 2.0, strokeWidth / 2.0 );
3027
3028 return symbolBounds;
3029}
3030
3032
3033QgsRasterMarkerSymbolLayer::QgsRasterMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
3034 : mPath( path )
3035{
3036 mSize = size;
3037 mAngle = angle;
3038 mOffset = QPointF( 0, 0 );
3041}
3042
3044
3046{
3047 QString path;
3051
3052 if ( props.contains( QStringLiteral( "imageFile" ) ) )
3053 path = props[QStringLiteral( "imageFile" )].toString();
3054 if ( props.contains( QStringLiteral( "size" ) ) )
3055 size = props[QStringLiteral( "size" )].toDouble();
3056 if ( props.contains( QStringLiteral( "angle" ) ) )
3057 angle = props[QStringLiteral( "angle" )].toDouble();
3058 if ( props.contains( QStringLiteral( "scale_method" ) ) )
3059 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
3060
3061 auto m = std::make_unique< QgsRasterMarkerSymbolLayer >( path, size, angle, scaleMethod );
3062 m->setCommonProperties( props );
3063 return m.release();
3064}
3065
3066void QgsRasterMarkerSymbolLayer::setCommonProperties( const QVariantMap &properties )
3067{
3068 if ( properties.contains( QStringLiteral( "alpha" ) ) )
3069 {
3070 setOpacity( properties[QStringLiteral( "alpha" )].toDouble() );
3071 }
3072
3073 if ( properties.contains( QStringLiteral( "size_unit" ) ) )
3074 setSizeUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "size_unit" )].toString() ) );
3075 if ( properties.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3076 setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3077 if ( properties.contains( QStringLiteral( "fixedAspectRatio" ) ) )
3078 setFixedAspectRatio( properties[QStringLiteral( "fixedAspectRatio" )].toDouble() );
3079
3080 if ( properties.contains( QStringLiteral( "offset" ) ) )
3081 setOffset( QgsSymbolLayerUtils::decodePoint( properties[QStringLiteral( "offset" )].toString() ) );
3082 if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
3083 setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )].toString() ) );
3084 if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3085 setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3086
3087 if ( properties.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3088 {
3089 setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( properties[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3090 }
3091 if ( properties.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3092 {
3093 setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( properties[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3094 }
3095
3098}
3099
3100void QgsRasterMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
3101{
3102 const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
3103 if ( it != properties.end() && it.value().userType() == QMetaType::Type::QString )
3104 {
3105 if ( saving )
3106 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
3107 else
3108 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
3109 }
3110}
3111
3112void QgsRasterMarkerSymbolLayer::setPath( const QString &path )
3113{
3114 mPath = path;
3116}
3117
3119{
3120 const bool aPreservedAspectRatio = preservedAspectRatio();
3121 if ( aPreservedAspectRatio && !par )
3122 {
3124 }
3125 else if ( !aPreservedAspectRatio && par )
3126 {
3127 mFixedAspectRatio = 0.0;
3128 }
3129 return preservedAspectRatio();
3130}
3131
3133{
3134 if ( mDefaultAspectRatio == 0.0 && !mPath.isEmpty() )
3135 {
3137 mDefaultAspectRatio = ( !size.isNull() && size.isValid() && size.width() > 0 ) ? static_cast< double >( size.height() ) / static_cast< double >( size.width() ) : 0.0;
3138 }
3139 return mDefaultAspectRatio;
3140}
3141
3143{
3144 return QStringLiteral( "RasterMarker" );
3145}
3146
3151
3153{
3154 QPainter *p = context.renderContext().painter();
3155 if ( !p )
3156 return;
3157
3158 QString path = mPath;
3160 {
3163 }
3164
3165 if ( path.isEmpty() )
3166 return;
3167
3168 double width = 0.0;
3169 double height = 0.0;
3170
3171 bool hasDataDefinedSize = false;
3172 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3173
3174 bool hasDataDefinedAspectRatio = false;
3175 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3176
3177 QPointF outputOffset;
3178 double angle = 0.0;
3179
3180 // RenderPercentage Unit Type takes original image size
3182 {
3184 if ( size.isEmpty() )
3185 return;
3186
3187 width = ( scaledSize * static_cast< double >( size.width() ) ) / 100.0;
3188 height = ( scaledSize * static_cast< double >( size.height() ) ) / 100.0;
3189
3190 // don't render symbols with size below one or above 10,000 pixels
3191 if ( static_cast< int >( width ) < 1 || 10000.0 < width || static_cast< int >( height ) < 1 || 10000.0 < height )
3192 return;
3193
3194 calculateOffsetAndRotation( context, width, height, outputOffset, angle );
3195 }
3196 else
3197 {
3198 width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3199 height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3200
3201 if ( preservedAspectRatio() && path != mPath )
3202 {
3204 if ( !size.isNull() && size.isValid() && size.width() > 0 )
3205 {
3206 height = width * ( static_cast< double >( size.height() ) / static_cast< double >( size.width() ) );
3207 }
3208 }
3209
3210 // don't render symbols with size below one or above 10,000 pixels
3211 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
3212 return;
3213
3214 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3215 }
3216
3217 const QgsScopedQPainterState painterState( p );
3218 p->translate( point + outputOffset );
3219
3220 const bool rotated = !qgsDoubleNear( angle, 0 );
3221 if ( rotated )
3222 p->rotate( angle );
3223
3224 double opacity = mOpacity;
3226 {
3229 }
3230 opacity *= context.opacity();
3231
3232 QImage img = fetchImage( context.renderContext(), path, QSize( width, preservedAspectRatio() ? 0 : width * aspectRatio ), preservedAspectRatio(), opacity );
3233 if ( !img.isNull() )
3234 {
3235 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
3236 if ( useSelectedColor )
3237 {
3239 }
3240
3241 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
3242 }
3243}
3244
3245QImage QgsRasterMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
3246{
3247 bool cached = false;
3248 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking );
3249}
3250
3251double QgsRasterMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
3252{
3253 double scaledSize = mSize;
3255
3256 bool ok = true;
3257 if ( hasDataDefinedSize )
3258 {
3261 }
3262 else
3263 {
3265 if ( hasDataDefinedSize )
3266 {
3269 }
3270 }
3271
3272 if ( hasDataDefinedSize && ok )
3273 {
3274 switch ( mScaleMethod )
3275 {
3277 scaledSize = std::sqrt( scaledSize );
3278 break;
3280 break;
3281 }
3282 }
3283
3284 return scaledSize;
3285}
3286
3287double QgsRasterMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
3288{
3290 if ( !hasDataDefinedAspectRatio )
3291 return mFixedAspectRatio;
3292
3294 return 0.0;
3295
3296 double scaledAspectRatio = mDefaultAspectRatio;
3297 if ( mFixedAspectRatio > 0.0 )
3298 scaledAspectRatio = mFixedAspectRatio;
3299
3300 const double defaultHeight = mSize * scaledAspectRatio;
3301 scaledAspectRatio = defaultHeight / scaledSize;
3302
3303 bool ok = true;
3304 double scaledHeight = scaledSize * scaledAspectRatio;
3306 {
3307 context.setOriginalValueVariable( defaultHeight );
3309 }
3310
3311 if ( hasDataDefinedAspectRatio && ok )
3312 {
3313 switch ( mScaleMethod )
3314 {
3316 scaledHeight = sqrt( scaledHeight );
3317 break;
3319 break;
3320 }
3321 }
3322
3323 scaledAspectRatio = scaledHeight / scaledSize;
3324
3325 return scaledAspectRatio;
3326}
3327
3328void QgsRasterMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
3329{
3330 //offset
3331 double offsetX = 0;
3332 double offsetY = 0;
3333 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
3334 offset = QPointF( offsetX, offsetY );
3335
3338 {
3341 }
3342
3344 if ( hasDataDefinedRotation )
3345 {
3346 const QgsFeature *f = context.feature();
3347 if ( f )
3348 {
3349 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3350 {
3351 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3352 angle += m2p.mapRotation();
3353 }
3354 }
3355 }
3356
3357 if ( angle )
3359}
3360
3361
3363{
3364 QVariantMap map;
3365 map[QStringLiteral( "imageFile" )] = mPath;
3366 map[QStringLiteral( "size" )] = QString::number( mSize );
3367 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3368 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3369 map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
3370 map[QStringLiteral( "angle" )] = QString::number( mAngle );
3371 map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
3372 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3373 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3374 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3375 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
3376 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
3377 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
3378 return map;
3379}
3380
3382{
3383 auto m = std::make_unique< QgsRasterMarkerSymbolLayer >();
3384 m->mPath = mPath;
3385 m->mDefaultAspectRatio = mDefaultAspectRatio;
3386 m->mSize = mSize;
3387 m->mAngle = mAngle;
3388 // other members are copied by:
3389 copyCommonProperties( m.get() );
3390 return m.release();
3391}
3392
3393
3408
3414
3416{
3417 return QColor();
3418}
3419
3424
3429
3431{
3432 bool hasDataDefinedSize = false;
3433 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3434 const double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3435 bool hasDataDefinedAspectRatio = false;
3436 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3437 const double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3438
3439 //don't render symbols with size below one or above 10,000 pixels
3440 if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
3441 {
3442 return QRectF();
3443 }
3444
3445 QPointF outputOffset;
3446 double angle = 0.0;
3447 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3448
3449 QTransform transform;
3450
3451 // move to the desired position
3452 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3453
3454 if ( !qgsDoubleNear( angle, 0.0 ) )
3455 transform.rotate( angle );
3456
3457 QRectF symbolBounds = transform.mapRect( QRectF( -width / 2.0,
3458 -height / 2.0,
3459 width,
3460 height ) );
3461
3462 return symbolBounds;
3463}
3464
3466
3467QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QString chr, double pointSize, const QColor &color, double angle )
3468{
3469 mFontFamily = fontFamily;
3470 mString = chr;
3471 mColor = color;
3472 mAngle = angle;
3473 mSize = pointSize;
3474 mOrigSize = pointSize;
3476 mOffset = QPointF( 0, 0 );
3478 mStrokeColor = DEFAULT_FONTMARKER_BORDERCOLOR;
3479 mStrokeWidth = 0.0;
3480 mStrokeWidthUnit = Qgis::RenderUnit::Millimeters;
3481 mPenJoinStyle = DEFAULT_FONTMARKER_JOINSTYLE;
3482}
3483
3485
3487{
3489 QString string = DEFAULT_FONTMARKER_CHR;
3490 double pointSize = DEFAULT_FONTMARKER_SIZE;
3493
3494 if ( props.contains( QStringLiteral( "font" ) ) )
3495 fontFamily = props[QStringLiteral( "font" )].toString();
3496 if ( props.contains( QStringLiteral( "chr" ) ) && props[QStringLiteral( "chr" )].toString().length() > 0 )
3497 {
3498 string = props["chr"].toString();
3499 const thread_local QRegularExpression charRegExp( QStringLiteral( "%1([0-9]+)%1" ).arg( FONTMARKER_CHR_FIX ) );
3500 QRegularExpressionMatch match = charRegExp.match( string );
3501 while ( match.hasMatch() )
3502 {
3503 QChar replacement = QChar( match.captured( 1 ).toUShort() );
3504 string = string.mid( 0, match.capturedStart( 0 ) ) + replacement + string.mid( match.capturedEnd( 0 ) );
3505 match = charRegExp.match( string );
3506 }
3507 }
3508
3509 if ( props.contains( QStringLiteral( "size" ) ) )
3510 pointSize = props[QStringLiteral( "size" )].toDouble();
3511 if ( props.contains( QStringLiteral( "color" ) ) )
3512 color = QgsColorUtils::colorFromString( props[QStringLiteral( "color" )].toString() );
3513 if ( props.contains( QStringLiteral( "angle" ) ) )
3514 angle = props[QStringLiteral( "angle" )].toDouble();
3515
3517
3518 if ( props.contains( QStringLiteral( "font_style" ) ) )
3519 m->setFontStyle( props[QStringLiteral( "font_style" )].toString() );
3520 if ( props.contains( QStringLiteral( "outline_color" ) ) )
3521 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "outline_color" )].toString() ) );
3522 if ( props.contains( QStringLiteral( "outline_width" ) ) )
3523 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
3524 if ( props.contains( QStringLiteral( "offset" ) ) )
3525 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
3526 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
3527 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
3528 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3529 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3530 if ( props.contains( QStringLiteral( "size_unit" ) ) )
3531 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
3532 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3533 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3534 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
3535 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
3536 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
3537 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
3538 if ( props.contains( QStringLiteral( "joinstyle" ) ) )
3539 m->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() ) );
3540 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3541 m->setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3542 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3543 m->setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3544
3546
3547 return m;
3548}
3549
3551{
3552 return QStringLiteral( "FontMarker" );
3553}
3554
3559
3561{
3562 QColor brushColor = mColor;
3563 QColor penColor = mStrokeColor;
3564
3565 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
3566 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
3567
3568 mBrush = QBrush( brushColor );
3569 mPen = QPen( penColor );
3570 mPen.setJoinStyle( mPenJoinStyle );
3571 mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
3572
3573 mFont = QgsFontUtils::createFont( QgsApplication::fontManager()->processFontFamilyName( mFontFamily ) );
3574 if ( !mFontStyle.isEmpty() )
3575 {
3576 mFont.setStyleName( QgsFontUtils::translateNamedStyle( mFontStyle ) );
3577 }
3578
3579 double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3580 mNonZeroFontSize = !qgsDoubleNear( sizePixels, 0.0 );
3581
3582 if ( mNonZeroFontSize && sizePixels > MAX_FONT_CHARACTER_SIZE_IN_PIXELS )
3583 {
3584 // if font is too large (e.g using map units and map is very zoomed in), then we limit
3585 // the font size and instead scale up the painter.
3586 // this avoids issues with massive font sizes (eg https://github.com/qgis/QGIS/issues/42270)
3587 mFontSizeScale = sizePixels / MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3588 sizePixels = MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3589 }
3590 else
3591 mFontSizeScale = 1.0;
3592
3593 // if a non zero, but small pixel size results, round up to 2 pixels so that a "dot" is at least visible
3594 // (if we set a <=1 pixel size here Qt will reset the font to a default size, leading to much too large symbols)
3595 mFont.setPixelSize( std::max( 2, static_cast< int >( std::round( sizePixels ) ) ) );
3596 mFontMetrics.reset( new QFontMetrics( mFont ) );
3597 mChrWidth = mFontMetrics->horizontalAdvance( mString );
3598 switch ( mVerticalAnchorPoint )
3599 {
3601 {
3602 mChrOffset = QPointF( mChrWidth / 2.0, -sizePixels / 2.0 );
3603 break;
3604 }
3608 {
3609 mChrOffset = QPointF( mChrWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3610 break;
3611 }
3612 }
3613 mOrigSize = mSize; // save in case the size would be data defined
3614
3615 // use caching only when not using a data defined character
3619 if ( mUseCachedPath )
3620 {
3621 QPointF chrOffset = mChrOffset;
3622 double chrWidth;
3623 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3624 mCachedPath = QPainterPath();
3625 mCachedPath.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3626 }
3627}
3628
3630{
3631 Q_UNUSED( context )
3632}
3633
3634QString QgsFontMarkerSymbolLayer::characterToRender( QgsSymbolRenderContext &context, QPointF &charOffset, double &charWidth )
3635{
3636 charOffset = mChrOffset;
3637 QString stringToRender = mString;
3639 {
3640 context.setOriginalValueVariable( mString );
3642 if ( stringToRender != mString )
3643 {
3644 charWidth = mFontMetrics->horizontalAdvance( stringToRender );
3645 switch ( mVerticalAnchorPoint )
3646 {
3648 {
3649 const double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3650 charOffset = QPointF( charWidth / 2.0, -sizePixels / 2.0 );
3651 break;
3652 }
3656 {
3657 charOffset = QPointF( charWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3658 break;
3659 }
3660 }
3661 }
3662 }
3663 return stringToRender;
3664}
3665
3666void QgsFontMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
3667 double scaledSize,
3668 bool &hasDataDefinedRotation,
3669 QPointF &offset,
3670 double &angle ) const
3671{
3672 //offset
3673 double offsetX = 0;
3674 double offsetY = 0;
3675 markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
3676 offset = QPointF( offsetX, offsetY );
3677
3678 //angle
3679 bool ok = true;
3682 {
3685
3686 // If the expression evaluation was not successful, fallback to static value
3687 if ( !ok )
3689 }
3690
3691 hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation;
3692 if ( hasDataDefinedRotation )
3693 {
3694 // For non-point markers, "dataDefinedRotation" means following the
3695 // shape (shape-data defined). For them, "field-data defined" does
3696 // not work at all. TODO: if "field-data defined" ever gets implemented
3697 // we'll need a way to distinguish here between the two, possibly
3698 // using another flag in renderHints()
3699 const QgsFeature *f = context.feature();
3700 if ( f )
3701 {
3702 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3703 {
3704 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3705 angle += m2p.mapRotation();
3706 }
3707 }
3708 }
3709
3710 if ( angle )
3712}
3713
3714double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context )
3715{
3716 double scaledSize = mSize;
3717 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
3718
3719 bool ok = true;
3720 if ( hasDataDefinedSize )
3721 {
3724 }
3725
3726 if ( hasDataDefinedSize && ok )
3727 {
3728 switch ( mScaleMethod )
3729 {
3731 scaledSize = std::sqrt( scaledSize );
3732 break;
3734 break;
3735 }
3736 }
3737 return scaledSize;
3738}
3739
3741{
3742 QPainter *p = context.renderContext().painter();
3743 if ( !p || !mNonZeroFontSize )
3744 return;
3745
3746 QTransform transform;
3747
3748 bool ok;
3749 QColor brushColor = mColor;
3751 {
3754 }
3755 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
3756 brushColor = useSelectedColor ? context.renderContext().selectionColor() : brushColor;
3757 if ( !useSelectedColor || !SELECTION_IS_OPAQUE )
3758 {
3759 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
3760 }
3761 mBrush.setColor( brushColor );
3762
3763 QColor penColor = mStrokeColor;
3765 {
3768 }
3769 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
3770
3771 double penWidth = context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3773 {
3774 context.setOriginalValueVariable( mStrokeWidth );
3776 if ( ok )
3777 {
3778 penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3779 }
3780 }
3781
3783 {
3786 if ( ok )
3787 {
3788 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
3789 }
3790 }
3791
3792 const QgsScopedQPainterState painterState( p );
3793 p->setBrush( mBrush );
3794 if ( !qgsDoubleNear( penWidth, 0.0 ) )
3795 {
3796 mPen.setColor( penColor );
3797 mPen.setWidthF( penWidth );
3798 p->setPen( mPen );
3799 }
3800 else
3801 {
3802 p->setPen( Qt::NoPen );
3803 }
3804
3806 {
3807 context.setOriginalValueVariable( mFontFamily );
3809 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( ok ? fontFamily : mFontFamily );
3810 QgsFontUtils::setFontFamily( mFont, processedFamily );
3811 }
3813 {
3814 context.setOriginalValueVariable( mFontStyle );
3817 }
3819 {
3820 mFontMetrics.reset( new QFontMetrics( mFont ) );
3821 }
3822
3823 QPointF chrOffset = mChrOffset;
3824 double chrWidth;
3825 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3826
3827 const double sizeToRender = calculateSize( context );
3828
3829 bool hasDataDefinedRotation = false;
3830 QPointF offset;
3831 double angle = 0;
3832 calculateOffsetAndRotation( context, sizeToRender, hasDataDefinedRotation, offset, angle );
3833
3834 p->translate( point.x() + offset.x(), point.y() + offset.y() );
3835
3836 if ( !qgsDoubleNear( angle, 0.0 ) )
3837 transform.rotate( angle );
3838
3839 if ( !qgsDoubleNear( sizeToRender, mOrigSize ) )
3840 {
3841 const double s = sizeToRender / mOrigSize;
3842 transform.scale( s, s );
3843 }
3844
3845 if ( !qgsDoubleNear( mFontSizeScale, 1.0 ) )
3846 transform.scale( mFontSizeScale, mFontSizeScale );
3847
3848 if ( mUseCachedPath )
3849 {
3850 p->drawPath( transform.map( mCachedPath ) );
3851 }
3852 else
3853 {
3854 QPainterPath path;
3855 path.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3856 p->drawPath( transform.map( path ) );
3857 }
3858}
3859
3861{
3862 QVariantMap props;
3863 props[QStringLiteral( "font" )] = mFontFamily;
3864 props[QStringLiteral( "font_style" )] = mFontStyle;
3865 QString chr = mString;
3866 for ( int i = 0; i < 32; i++ )
3867 {
3868 if ( i == 9 || i == 10 || i == 13 )
3869 {
3870 continue;
3871 }
3872 chr.replace( QChar( i ), QStringLiteral( "%1%2%1" ).arg( FONTMARKER_CHR_FIX, QString::number( i ) ) );
3873 }
3874 props[QStringLiteral( "chr" )] = chr;
3875 props[QStringLiteral( "size" )] = QString::number( mSize );
3876 props[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3877 props[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3878 props[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mColor );
3879 props[QStringLiteral( "outline_color" )] = QgsColorUtils::colorToString( mStrokeColor );
3880 props[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
3881 props[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
3882 props[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
3883 props[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
3884 props[QStringLiteral( "angle" )] = QString::number( mAngle );
3885 props[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3886 props[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3887 props[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3888 props[QStringLiteral( "horizontal_anchor_point" )] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
3889 props[QStringLiteral( "vertical_anchor_point" )] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
3890 return props;
3891}
3892
3894{
3895 QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( mFontFamily, mString, mSize, mColor, mAngle );
3896 m->setFontStyle( mFontStyle );
3897 m->setStrokeColor( mStrokeColor );
3898 m->setStrokeWidth( mStrokeWidth );
3899 m->setStrokeWidthUnit( mStrokeWidthUnit );
3900 m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
3901 m->setPenJoinStyle( mPenJoinStyle );
3902 m->setOffset( mOffset );
3905 m->setSizeUnit( mSizeUnit );
3910 copyPaintEffect( m );
3911 return m;
3912}
3913
3914void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
3915{
3916 // <Graphic>
3917 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
3918 element.appendChild( graphicElem );
3919
3920 const QString fontPath = QStringLiteral( "ttf://%1" ).arg( mFontFamily );
3921 int markIndex = !mString.isEmpty() ? mString.at( 0 ).unicode() : 0;
3922 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
3923 QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, QStringLiteral( "ttf" ), &markIndex, mColor, size );
3924
3925 // <Rotation>
3926 QString angleFunc;
3927 bool ok;
3928 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
3929 if ( !ok )
3930 {
3931 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
3932 }
3933 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
3934 {
3935 angleFunc = QString::number( angle + mAngle );
3936 }
3937 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
3938
3939 // <Displacement>
3940 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
3942}
3943
3950
3952{
3954 mStrokeWidthUnit = unit;
3955}
3956
3958{
3959 QPointF chrOffset = mChrOffset;
3960 double chrWidth = mChrWidth;
3961 //calculate width of rendered character
3962 ( void )characterToRender( context, chrOffset, chrWidth );
3963
3964 if ( !mFontMetrics )
3965 mFontMetrics.reset( new QFontMetrics( mFont ) );
3966
3967 double scaledSize = calculateSize( context );
3968 if ( !qgsDoubleNear( scaledSize, mOrigSize ) )
3969 {
3970 chrWidth *= scaledSize / mOrigSize;
3971 }
3972 chrWidth *= mFontSizeScale;
3973
3974 bool hasDataDefinedRotation = false;
3975 QPointF offset;
3976 double angle = 0;
3977 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
3978 scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3979
3980 QTransform transform;
3981
3982 // move to the desired position
3983 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
3984
3985 if ( !qgsDoubleNear( angle, 0.0 ) )
3986 transform.rotate( angle );
3987
3988 QRectF symbolBounds = transform.mapRect( QRectF( -chrWidth / 2.0,
3989 -scaledSize / 2.0,
3990 chrWidth,
3991 scaledSize ) );
3992 return symbolBounds;
3993}
3994
3996{
3997 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
3998
3999 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
4000 if ( graphicElem.isNull() )
4001 return nullptr;
4002
4003 QString name, format;
4004 QColor color;
4005 double size;
4006 int chr;
4007
4008 if ( !QgsSymbolLayerUtils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
4009 return nullptr;
4010
4011 if ( !name.startsWith( QLatin1String( "ttf://" ) ) || format != QLatin1String( "ttf" ) )
4012 return nullptr;
4013
4014 const QString fontFamily = name.mid( 6 );
4015
4016 double angle = 0.0;
4017 QString angleFunc;
4018 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
4019 {
4020 bool ok;
4021 const double d = angleFunc.toDouble( &ok );
4022 if ( ok )
4023 angle = d;
4024 }
4025
4026 QPointF offset;
4028
4029 double scaleFactor = 1.0;
4030 const QString uom = element.attribute( QStringLiteral( "uom" ) );
4031 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
4032 offset.setX( offset.x() * scaleFactor );
4033 offset.setY( offset.y() * scaleFactor );
4034 size = size * scaleFactor;
4035
4037 m->setOutputUnit( sldUnitSize );
4038 m->setAngle( angle );
4039 m->setOffset( offset );
4040 return m;
4041}
4042
4043void QgsFontMarkerSymbolLayer::resolveFonts( const QVariantMap &properties, const QgsReadWriteContext &context )
4044{
4045 const QString fontFamily = properties.value( QStringLiteral( "font" ), DEFAULT_FONTMARKER_FONT ).toString();
4046 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( fontFamily );
4047 QString matched;
4048 if ( !QgsFontUtils::fontFamilyMatchOnSystem( processedFamily )
4049 && !QgsApplication::fontManager()->tryToDownloadFontFamily( processedFamily, matched ) )
4050 {
4051 context.pushMessage( QObject::tr( "Font “%1” not available on system" ).arg( processedFamily ) );
4052 }
4053}
4054
4056{
4057 QMap<QString, QgsProperty>::iterator it = mParameters.begin();
4058 for ( ; it != mParameters.end(); ++it )
4059 it.value().prepare( context.renderContext().expressionContext() );
4060
4062}
4063
4064
4066{
4067 QSet<QString> attrs = QgsMarkerSymbolLayer::usedAttributes( context );
4068
4069 QMap<QString, QgsProperty>::const_iterator it = mParameters.constBegin();
4070 for ( ; it != mParameters.constEnd(); ++it )
4071 {
4072 attrs.unite( it.value().referencedFields( context.expressionContext(), true ) );
4073 }
4074
4075 return attrs;
4076}
4077
4078//
4079// QgsAnimatedMarkerSymbolLayer
4080//
4081
4082QgsAnimatedMarkerSymbolLayer::QgsAnimatedMarkerSymbolLayer( const QString &path, double size, double angle )
4083 : QgsRasterMarkerSymbolLayer( path, size, angle )
4084{
4085
4086}
4087
4089
4090QgsSymbolLayer *QgsAnimatedMarkerSymbolLayer::create( const QVariantMap &properties ) // cppcheck-suppress duplInheritedMember
4091{
4092 QString path;
4095
4096 if ( properties.contains( QStringLiteral( "imageFile" ) ) )
4097 path = properties[QStringLiteral( "imageFile" )].toString();
4098 if ( properties.contains( QStringLiteral( "size" ) ) )
4099 size = properties[QStringLiteral( "size" )].toDouble();
4100 if ( properties.contains( QStringLiteral( "angle" ) ) )
4101 angle = properties[QStringLiteral( "angle" )].toDouble();
4102
4103 auto m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( path, size, angle );
4104 m->setFrameRate( properties.value( QStringLiteral( "frameRate" ), QStringLiteral( "10" ) ).toDouble() );
4105
4106 m->setCommonProperties( properties );
4107 return m.release();
4108}
4109
4111{
4112 return QStringLiteral( "AnimatedMarker" );
4113}
4114
4116{
4117 QVariantMap res = QgsRasterMarkerSymbolLayer::properties();
4118 res.insert( QStringLiteral( "frameRate" ), mFrameRateFps );
4119 return res;
4120}
4121
4123{
4124 auto m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( mPath, mSize, mAngle );
4125 m->setFrameRate( mFrameRateFps );
4126 copyCommonProperties( m.get() );
4127 return m.release();
4128}
4129
4131{
4133
4134 mPreparedPaths.clear();
4136 {
4138 mStaticPath = true;
4139 }
4140 else
4141 {
4142 mStaticPath = false;
4143 }
4144}
4145
4146QImage QgsAnimatedMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
4147{
4148 if ( !mStaticPath && !mPreparedPaths.contains( path ) )
4149 {
4151 mPreparedPaths.insert( path );
4152 }
4153
4154 const long long mapFrameNumber = context.currentFrame();
4156 const double markerAnimationDuration = totalFrameCount / mFrameRateFps;
4157
4158 double animationTimeSeconds = 0;
4159 if ( mapFrameNumber >= 0 && context.frameRate() > 0 )
4160 {
4161 // render is part of an animation, so we base the calculated frame on that
4162 animationTimeSeconds = mapFrameNumber / context.frameRate();
4163 }
4164 else
4165 {
4166 // render is outside of animation, so base the calculated frame on the current epoch
4167 animationTimeSeconds = QDateTime::currentMSecsSinceEpoch() / 1000.0;
4168 }
4169
4170 const double markerAnimationProgressSeconds = std::fmod( animationTimeSeconds, markerAnimationDuration );
4171 const int movieFrame = static_cast< int >( std::floor( markerAnimationProgressSeconds * mFrameRateFps ) );
4172
4173 bool cached = false;
4174 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking, 96, movieFrame );
4175}
4176
@ DynamicRotation
Rotation of symbol may be changed during rendering and symbol should not be cached.
@ IsSymbolLayerSubSymbol
Symbol is being rendered as a sub-symbol of a QgsSymbolLayer.
@ CanCalculateMaskGeometryPerFeature
If present, indicates that mask geometry can safely be calculated per feature for the symbol layer....
ScaleMethod
Scale methods.
Definition qgis.h:588
@ ScaleDiameter
Calculate scale by the diameter.
@ ScaleArea
Calculate scale by the area.
QFlags< SymbolLayerFlag > SymbolLayerFlags
Symbol layer flags.
Definition qgis.h:851
MarkerShape
Marker shapes.
Definition qgis.h:2974
@ Pentagon
Pentagon.
@ EquilateralTriangle
Equilateral triangle.
@ SemiCircle
Semi circle (top half)
@ QuarterCircle
Quarter circle (top left quarter)
@ LeftHalfTriangle
Left half of triangle.
@ ArrowHead
Right facing arrow head (unfilled, lines only)
@ ParallelogramRight
Parallelogram that slants right.
@ AsteriskFill
A filled asterisk shape.
@ HalfArc
A line-only half arc.
@ Line
Vertical line.
@ QuarterSquare
Quarter square (top left quarter)
@ Triangle
Triangle.
@ Cross2
Rotated cross (lines only), 'x' shape.
@ Trapezoid
Trapezoid.
@ ArrowHeadFilled
Right facing filled arrow head.
@ Shield
A shape consisting of a triangle attached to a rectangle.
@ HalfSquare
Half square (left half)
@ CrossFill
Solid filled cross.
@ RoundedSquare
A square with rounded corners.
@ RightHalfTriangle
Right half of triangle.
@ ThirdCircle
One third circle (top left third)
@ ThirdArc
A line-only one third arc.
@ SquareWithCorners
A square with diagonal corners.
@ QuarterArc
A line-only one quarter arc.
@ DiamondStar
A 4-sided star.
@ Cross
Cross (lines only)
@ ParallelogramLeft
Parallelogram that slants left.
@ DiagonalHalfSquare
Diagonal half square (bottom left half)
VerticalAnchorPoint
Marker symbol vertical anchor points.
Definition qgis.h:778
@ Bottom
Align to bottom of symbol.
@ Center
Align to vertical center of symbol.
@ Baseline
Align to baseline of symbol, e.g. font baseline for font marker symbol layers. Treated as Bottom if n...
@ Top
Align to top of symbol.
RenderUnit
Rendering size units.
Definition qgis.h:5008
@ Percentage
Percentage of another measurement (e.g., canvas size, feature size)
@ Millimeters
Millimeters.
@ Unknown
Mixed or unknown units.
@ MapUnits
Map units.
@ MetersInMapUnits
Meters value as Map units.
@ RenderingSubSymbol
Set whenever a sub-symbol of a parent symbol is currently being rendered. Can be used during symbol a...
@ RenderSymbolPreview
The render is for a symbol preview only and map based properties may not be available,...
@ RenderBlocking
Render and load remote sources in the same thread to ensure rendering remote sources (svg and images)...
@ Fill
Fill symbol.
HorizontalAnchorPoint
Marker symbol horizontal anchor points.
Definition qgis.h:764
QColor valueAsColor(int key, const QgsExpressionContext &context, const QColor &defaultColor=QColor(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a color.
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double.
QString valueAsString(int key, const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a string.
Animated marker symbol layer class.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsAnimatedMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
~QgsAnimatedMarkerSymbolLayer() override
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
QImage fetchImage(QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity) const override
Fetches the image to render.
QgsAnimatedMarkerSymbolLayer(const QString &path=QString(), double size=DEFAULT_RASTERMARKER_SIZE, double angle=DEFAULT_RASTERMARKER_ANGLE)
Constructor for animated marker symbol layer using the specified source image path.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates an animated marker symbol layer from a string map of properties.
QString layerType() const override
Returns a string that represents this layer type.
static QgsImageCache * imageCache()
Returns the application's image cache, used for caching resampled versions of raster images.
static QgsSvgCache * svgCache()
Returns the application's SVG cache, used for caching SVG images and handling parameter replacement w...
static QgsFontManager * fontManager()
Returns the application font manager, which manages available fonts and font installation for the QGI...
static QColor colorFromString(const QString &string)
Decodes a string into a color value.
static QString colorToString(const QColor &color)
Encodes a color into a string value.
Exports QGIS layers to the DXF format.
void writeFilledCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius)
Write filled circle (as hatch)
void writeCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius, const QString &lineStyleName, double width)
Write circle (as polyline)
static double mapUnitScaleFactor(double scale, Qgis::RenderUnit symbolUnits, Qgis::DistanceUnit mapUnits, double mapUnitsPerPixel=1.0)
Returns scale factor for conversion to map units.
void writeLine(const QgsPoint &pt1, const QgsPoint &pt2, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Write line (as a polyline)
void writePolygon(const QgsRingSequence &polygon, const QString &layer, const QString &hatchPattern, const QColor &color)
Draw dxf filled polygon (HATCH)
Qgis::DistanceUnit mapUnits() const
Retrieve map units.
double symbologyScale() const
Returns the reference scale for output.
void clipValueToMapUnitScale(double &value, const QgsMapUnitScale &scale, double pixelToMMFactor) const
Clips value to scale minimum/maximum.
void writePolyline(const QgsPointSequence &line, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Draw dxf primitives (LWPOLYLINE)
A paint device for drawing into dxf files.
void setShift(QPointF shift)
void setLayer(const QString &layer)
void setOutputSize(const QRectF &r)
void setDrawingSize(QSizeF size)
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
QgsGeometry geometry
Definition qgsfeature.h:69
bool hasGeometry() const
Returns true if the feature has an associated geometry.
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
static QgsFillSymbol * createSimple(const QVariantMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties.
Filled marker symbol layer, consisting of a shape which is rendered using a QgsFillSymbol.
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QColor color() const override
Returns the "representative" color of the symbol layer.
QgsFilledMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QgsSymbol * subSymbol() override
Returns the symbol's sub symbol, if present.
void stopFeatureRender(const QgsFeature &feature, QgsRenderContext &context) override
Called after the layer has been rendered for a particular feature.
void startFeatureRender(const QgsFeature &feature, QgsRenderContext &context) override
Called before the layer will be rendered for a particular feature.
~QgsFilledMarkerSymbolLayer() override
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer's subsymbol. takes ownership of the passed symbol.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
void setColor(const QColor &c) override
Sets the "representative" color for the symbol layer.
QString layerType() const override
Returns a string that represents this layer type.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsFilledMarkerSymbolLayer.
QgsFilledMarkerSymbolLayer(Qgis::MarkerShape shape=Qgis::MarkerShape::Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructor for QgsFilledMarkerSymbolLayer.
QString processFontFamilyName(const QString &name) const
Processes a font family name, applying any matching fontFamilyReplacements() to the name.
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.
void setVerticalAnchorPoint(Qgis::VerticalAnchorPoint v)
Sets the vertical anchor point for positioning the symbol.
Qgis::RenderUnit mOffsetUnit
Offset units.
QPointF offset() const
Returns the marker's offset, which is the horizontal and vertical displacement which the rendered mar...
double mLineAngle
Line rotation angle (see setLineAngle() for details)
void setOffsetUnit(Qgis::RenderUnit unit)
Sets the units for the symbol's offset.
void setAngle(double angle)
Sets the rotation angle for the marker.
Qgis::ScaleMethod scaleMethod() const
Returns the method to use for scaling the marker's size.
Qgis::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
QPointF mOffset
Marker offset.
QgsMapUnitScale mapUnitScale() const override
void setOffset(QPointF offset)
Sets the marker's offset, which is the horizontal and vertical displacement which the rendered marker...
void setSizeMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol's size.
double size() const
Returns the symbol size.
QgsMapUnitScale mOffsetMapUnitScale
Offset map unit scale.
Qgis::VerticalAnchorPoint mVerticalAnchorPoint
Vertical anchor point.
static QPointF _rotatedOffset(QPointF offset, double angle)
Adjusts a marker offset to account for rotation.
void setHorizontalAnchorPoint(Qgis::HorizontalAnchorPoint h)
Sets the horizontal anchor point for positioning the symbol.
Qgis::ScaleMethod mScaleMethod
Marker size scaling method.
QgsMapUnitScale mSizeMapUnitScale
Marker size map unit scale.
Qgis::RenderUnit mSizeUnit
Marker size unit.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units for the symbol's size.
void markerOffset(QgsSymbolRenderContext &context, double &offsetX, double &offsetY) const
Calculates the required marker offset, including both the symbol offset and any displacement required...
Qgis::HorizontalAnchorPoint mHorizontalAnchorPoint
Horizontal anchor point.
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol's offset.
double mAngle
Marker rotation angle, in degrees clockwise from north.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
double angle() const
Returns the rotation angle for the marker, in degrees clockwise from north.
void setMapUnitScale(const QgsMapUnitScale &scale) override
static void drawPicture(QPainter *painter, const QPointF &point, const QPicture &picture)
Draws a picture onto a painter, correctly applying workarounds to avoid issues with incorrect scaling...
Resolves relative paths into absolute paths and vice versa.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const final
Returns the calculated value of the property with the specified key from within the collection.
bool isActive(int key) const final
Returns true if the collection contains an active property with the specified key.
QgsProperty property(int key) const final
Returns a matching property from the collection, if one exists.
QString asExpression() const
Returns an expression string representing the state of the property, or an empty string if the proper...
static QVariantMap propertyMapToVariantMap(const QMap< QString, QgsProperty > &propertyMap)
Convert a map of QgsProperty to a map of QVariant This is useful to save a map of properties.
static QMap< QString, QgsProperty > variantMapToPropertyMap(const QVariantMap &variantMap)
Convert a map of QVariant to a map of QgsProperty This is useful to restore a map of properties.
static QgsProperty fromValue(const QVariant &value, bool isActive=true)
Returns a new StaticProperty created from the specified value.
Raster marker symbol layer class.
double mFixedAspectRatio
The marker fixed aspect ratio.
QColor color() const override
Returns the "representative" color of the symbol layer.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsMapUnitScale mapUnitScale() const override
void copyCommonProperties(QgsRasterMarkerSymbolLayer *other) const
Copies common properties to another layer.
void setOpacity(double opacity)
Set the marker opacity.
QString path() const
Returns the marker raster image path.
double calculateAspectRatio(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio) const
Calculates the marker aspect ratio between width and height.
QgsRasterMarkerSymbolLayer(const QString &path=QString(), double size=DEFAULT_SVGMARKER_SIZE, double angle=DEFAULT_SVGMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructs raster marker symbol layer with picture from given absolute path to a raster image file.
void setPath(const QString &path)
Set the marker raster image path.
virtual QImage fetchImage(QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity) const
Fetches the image to render.
double defaultAspectRatio() const
Returns the default marker aspect ratio between width and height, 0 if not yet calculated.
Qgis::SymbolLayerFlags flags() const override
Returns flags which control the symbol layer's behavior.
void setFixedAspectRatio(double ratio)
Set the marker aspect ratio between width and height to be used in rendering, if the value set is low...
void setMapUnitScale(const QgsMapUnitScale &scale) override
void setCommonProperties(const QVariantMap &properties)
Sets common class properties from a properties map.
QgsRasterMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
bool preservedAspectRatio() const
Returns the preserved aspect ratio value, true if fixed aspect ratio has been lower or equal to 0.
static void resolvePaths(QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving)
Turns relative paths in properties map to absolute when reading and vice versa when writing.
~QgsRasterMarkerSymbolLayer() override
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a raster marker symbol layer from a string map of properties.
double mOpacity
The marker default opacity.
double updateDefaultAspectRatio()
Calculates the default marker aspect ratio between width and height.
double mDefaultAspectRatio
The marker default aspect ratio.
bool setPreservedAspectRatio(bool par)
Set preserved the marker aspect ratio between width and height.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QString layerType() const override
Returns a string that represents this layer type.
double opacity() const
Returns the marker opacity.
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.
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.
bool installMasks(QgsRenderContext &context, bool recursive, const QRectF &rect=QRectF())
When rendering, install masks on context painter.
void removeMasks(QgsRenderContext &context, bool recursive)
When rendering, remove previously installed masks from context painter if recursive is true masks are...
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns the set of attributes referenced by the layer.
void copyDataDefinedProperties(QgsSymbolLayer *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
@ StrokeStyle
Stroke style (eg solid, dashed)
@ Name
Name, eg shape name for simple markers.
@ Character
Character, eg for font marker symbol layers.
@ StrokeColor
Stroke color.
@ CapStyle
Line cap style.
@ JoinStyle
Line join style.
@ StrokeWidth
Stroke width.
@ FontFamily
Font family.
@ Offset
Symbol offset.
@ Height
Symbol height.
void restoreOldDataDefinedProperties(const QVariantMap &stringMap)
Restores older data defined properties from string map.
virtual void startRender(QgsSymbolRenderContext &context)=0
Called before a set of rendering operations commences on the supplied render context.
virtual void prepareExpressions(const QgsSymbolRenderContext &context)
Prepares all data defined property expressions for evaluation.
virtual 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.
bool forceVectorRendering() const
Returns true if symbol must be rendered using vector methods, and optimisations like pre-rendered ima...
qreal opacity() const
Returns the opacity for the symbol.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
Abstract base class for all rendered symbols.
Definition qgssymbol.h:231
Qgis::SymbolType type() const
Returns the symbol's type.
Definition qgssymbol.h:294
static Q_INVOKABLE Qgis::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:6219
QMap< QString, QString > QgsStringMap
Definition qgis.h:6757
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
#define DEG2RAD(x)
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:41
#define QgsDebugError(str)
Definition qgslogger.h:40
#define DEFAULT_FONTMARKER_JOINSTYLE
#define DEFAULT_RASTERMARKER_ANGLE
#define DEFAULT_RASTERMARKER_SIZE
#define DEFAULT_SVGMARKER_ANGLE
#define DEFAULT_SIMPLEMARKER_JOINSTYLE
#define DEFAULT_FONTMARKER_CHR
#define DEFAULT_SIMPLEMARKER_BORDERCOLOR
#define DEFAULT_SIMPLEMARKER_SIZE
#define DEFAULT_SIMPLEMARKER_NAME
#define DEFAULT_SIMPLEMARKER_ANGLE
#define DEFAULT_SVGMARKER_SIZE
#define DEFAULT_FONTMARKER_FONT
#define DEFAULT_FONTMARKER_BORDERCOLOR
#define DEFAULT_FONTMARKER_ANGLE
#define DEFAULT_FONTMARKER_COLOR
#define DEFAULT_FONTMARKER_SIZE
#define DEFAULT_SIMPLEMARKER_COLOR
#define DEFAULT_SCALE_METHOD
#define FONTMARKER_CHR_FIX