QGIS API Documentation 3.32.0-Lima (311a8cb8a6)
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
32#include <QPainter>
33#include <QSvgRenderer>
34#include <QFileInfo>
35#include <QDir>
36#include <QDomDocument>
37#include <QDomElement>
38#include <QUrlQuery>
39
40#include <cmath>
41
42Q_GUI_EXPORT extern int qt_defaultDpiX();
43Q_GUI_EXPORT extern int qt_defaultDpiY();
44
45static constexpr int MAX_FONT_CHARACTER_SIZE_IN_PIXELS = 500;
46
47static void _fixQPictureDPI( QPainter *p )
48{
49 // QPicture makes an assumption that we drawing to it with system DPI.
50 // Then when being drawn, it scales the painter. The following call
51 // negates the effect. There is no way of setting QPicture's DPI.
52 // See QTBUG-20361
53 p->scale( static_cast< double >( qt_defaultDpiX() ) / p->device()->logicalDpiX(),
54 static_cast< double >( qt_defaultDpiY() ) / p->device()->logicalDpiY() );
55}
56
57
59
60
61//
62// QgsSimpleMarkerSymbolLayerBase
63//
64
66{
67 QList< Qgis::MarkerShape > shapes;
105
106 return shapes;
107}
108
110 : mShape( shape )
111{
112 mSize = size;
113 mAngle = angle;
114 mOffset = QPointF( 0, 0 );
116 mSizeUnit = Qgis::RenderUnit::Millimeters;
117 mOffsetUnit = Qgis::RenderUnit::Millimeters;
118}
119
121
123{
124 switch ( shape )
125 {
156 return true;
157
165 return false;
166 }
167 return true;
168}
169
171{
172 const bool hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation
174 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
175
176 // use either QPolygonF or QPainterPath for drawing
177 if ( !prepareMarkerShape( mShape ) ) // drawing as a polygon
178 {
179 prepareMarkerPath( mShape ); // drawing as a painter path
180 }
181
182 QTransform transform;
183
184 // scale the shape (if the size is not going to be modified)
185 if ( !hasDataDefinedSize )
186 {
187 double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
188 if ( mSizeUnit == Qgis::RenderUnit::MetersInMapUnits && context.renderContext().flags() & Qgis::RenderContextFlag::RenderSymbolPreview )
189 {
190 // rendering for symbol previews -- an size in meters in map units can't be calculated, so treat the size as millimeters
191 // and clamp it to a reasonable range. It's the best we can do in this situation!
192 scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
193 }
194
195 const double half = scaledSize / 2.0;
196 transform.scale( half, half );
197 }
198
199 // rotate if the rotation is not going to be changed during the rendering
200 if ( !hasDataDefinedRotation && !qgsDoubleNear( mAngle, 0.0 ) )
201 {
202 transform.rotate( mAngle );
203 }
204
205 if ( !mPolygon.isEmpty() )
206 mPolygon = transform.map( mPolygon );
207 else
208 mPath = transform.map( mPath );
209
211}
212
214{
215 Q_UNUSED( context )
216}
217
219{
220 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
221 //of the rendered point!
222
223 QPainter *p = context.renderContext().painter();
224 if ( !p )
225 {
226 return;
227 }
228
229 bool hasDataDefinedSize = false;
230 const double scaledSize = calculateSize( context, hasDataDefinedSize );
231
232 bool hasDataDefinedRotation = false;
233 QPointF offset;
234 double angle = 0;
235 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
236
237 //data defined shape?
238 bool createdNewPath = false;
239 bool ok = true;
240 Qgis::MarkerShape symbol = mShape;
242 {
243 context.setOriginalValueVariable( encodeShape( symbol ) );
245 if ( !QgsVariantUtils::isNull( exprVal ) )
246 {
247 const Qgis::MarkerShape decoded = decodeShape( exprVal.toString(), &ok );
248 if ( ok )
249 {
250 symbol = decoded;
251
252 if ( !prepareMarkerShape( symbol ) ) // drawing as a polygon
253 {
254 prepareMarkerPath( symbol ); // drawing as a painter path
255 }
256 createdNewPath = true;
257 }
258 }
259 else
260 {
261 symbol = mShape;
262 }
263 }
264
265 QTransform transform;
266
267 // move to the desired position
268 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
269
270 // resize if necessary
271 if ( hasDataDefinedSize || createdNewPath )
272 {
273 double s = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
274 if ( mSizeUnit == Qgis::RenderUnit::MetersInMapUnits && context.renderContext().flags() & Qgis::RenderContextFlag::RenderSymbolPreview )
275 {
276 // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
277 // and clamp it to a reasonable range. It's the best we can do in this situation!
278 s = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
279 }
280 const double half = s / 2.0;
281 transform.scale( half, half );
282 }
283
284 if ( !qgsDoubleNear( angle, 0.0 ) && ( hasDataDefinedRotation || createdNewPath ) )
285 {
286 transform.rotate( angle );
287 }
288
289 //need to pass: symbol, polygon, path
290
291 QPolygonF polygon;
292 QPainterPath path;
293 if ( !mPolygon.isEmpty() )
294 {
295 polygon = transform.map( mPolygon );
296 }
297 else
298 {
299 path = transform.map( mPath );
300 }
301 draw( context, symbol, polygon, path );
302}
303
305{
306 bool hasDataDefinedSize = false;
307 double scaledSize = calculateSize( context, hasDataDefinedSize );
308
309 bool hasDataDefinedRotation = false;
310 QPointF offset;
311 double angle = 0;
312 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
313
314 scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
315
316 QTransform transform;
317
318 // move to the desired position
319 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
320
321 if ( !qgsDoubleNear( angle, 0.0 ) )
322 transform.rotate( angle );
323
324 return transform.mapRect( QRectF( -scaledSize / 2.0,
325 -scaledSize / 2.0,
326 scaledSize,
327 scaledSize ) );
328}
329
331{
332 if ( ok )
333 *ok = true;
334 const QString cleaned = name.toLower().trimmed();
335
336 if ( cleaned == QLatin1String( "square" ) || cleaned == QLatin1String( "rectangle" ) )
338 else if ( cleaned == QLatin1String( "trapezoid" ) )
340 else if ( cleaned == QLatin1String( "parallelogram_right" ) )
342 else if ( cleaned == QLatin1String( "parallelogram_left" ) )
344 else if ( cleaned == QLatin1String( "square_with_corners" ) )
346 else if ( cleaned == QLatin1String( "rounded_square" ) )
348 else if ( cleaned == QLatin1String( "diamond" ) )
350 else if ( cleaned == QLatin1String( "shield" ) )
352 else if ( cleaned == QLatin1String( "pentagon" ) )
354 else if ( cleaned == QLatin1String( "hexagon" ) )
356 else if ( cleaned == QLatin1String( "octagon" ) )
358 else if ( cleaned == QLatin1String( "decagon" ) )
360 else if ( cleaned == QLatin1String( "triangle" ) )
362 else if ( cleaned == QLatin1String( "equilateral_triangle" ) )
364 else if ( cleaned == QLatin1String( "star_diamond" ) )
366 else if ( cleaned == QLatin1String( "star" ) || cleaned == QLatin1String( "regular_star" ) )
368 else if ( cleaned == QLatin1String( "heart" ) )
370 else if ( cleaned == QLatin1String( "arrow" ) )
372 else if ( cleaned == QLatin1String( "circle" ) )
374 else if ( cleaned == QLatin1String( "cross" ) )
376 else if ( cleaned == QLatin1String( "cross_fill" ) )
378 else if ( cleaned == QLatin1String( "cross2" ) || cleaned == QLatin1String( "x" ) )
380 else if ( cleaned == QLatin1String( "line" ) )
382 else if ( cleaned == QLatin1String( "arrowhead" ) )
384 else if ( cleaned == QLatin1String( "filled_arrowhead" ) )
386 else if ( cleaned == QLatin1String( "semi_circle" ) )
388 else if ( cleaned == QLatin1String( "third_circle" ) )
390 else if ( cleaned == QLatin1String( "quarter_circle" ) )
392 else if ( cleaned == QLatin1String( "quarter_square" ) )
394 else if ( cleaned == QLatin1String( "half_square" ) )
396 else if ( cleaned == QLatin1String( "diagonal_half_square" ) )
398 else if ( cleaned == QLatin1String( "right_half_triangle" ) )
400 else if ( cleaned == QLatin1String( "left_half_triangle" ) )
402 else if ( cleaned == QLatin1String( "asterisk_fill" ) )
404 else if ( cleaned == QLatin1String( "half_arc" ) )
406 else if ( cleaned == QLatin1String( "third_arc" ) )
408 else if ( cleaned == QLatin1String( "quarter_arc" ) )
410
411 if ( ok )
412 *ok = false;
414}
415
417{
418 switch ( shape )
419 {
421 return QStringLiteral( "square" );
423 return QStringLiteral( "quarter_square" );
425 return QStringLiteral( "half_square" );
427 return QStringLiteral( "diagonal_half_square" );
429 return QStringLiteral( "parallelogram_right" );
431 return QStringLiteral( "parallelogram_left" );
433 return QStringLiteral( "trapezoid" );
435 return QStringLiteral( "shield" );
437 return QStringLiteral( "diamond" );
439 return QStringLiteral( "pentagon" );
441 return QStringLiteral( "hexagon" );
443 return QStringLiteral( "octagon" );
445 return QStringLiteral( "decagon" );
447 return QStringLiteral( "square_with_corners" );
449 return QStringLiteral( "rounded_square" );
451 return QStringLiteral( "triangle" );
453 return QStringLiteral( "equilateral_triangle" );
455 return QStringLiteral( "left_half_triangle" );
457 return QStringLiteral( "right_half_triangle" );
459 return QStringLiteral( "star_diamond" );
461 return QStringLiteral( "star" );
463 return QStringLiteral( "heart" );
465 return QStringLiteral( "arrow" );
467 return QStringLiteral( "filled_arrowhead" );
469 return QStringLiteral( "cross_fill" );
471 return QStringLiteral( "circle" );
473 return QStringLiteral( "cross" );
475 return QStringLiteral( "cross2" );
477 return QStringLiteral( "line" );
479 return QStringLiteral( "arrowhead" );
481 return QStringLiteral( "semi_circle" );
483 return QStringLiteral( "third_circle" );
485 return QStringLiteral( "quarter_circle" );
487 return QStringLiteral( "asterisk_fill" );
489 return QStringLiteral( "half_arc" );
491 return QStringLiteral( "third_arc" );
493 return QStringLiteral( "quarter_arc" );
494 }
495 return QString();
496}
497
499{
500 return shapeToPolygon( shape, mPolygon );
501}
502
504{
505 polygon.clear();
506
507 switch ( shape )
508 {
510 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 1, 1 ) ) );
511 return true;
512
514 {
515 static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 0.6072;
516
517 polygon << QPointF( - VERTEX_OFFSET_FROM_ORIGIN, 1 )
518 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, 1 )
519 << QPointF( 1, VERTEX_OFFSET_FROM_ORIGIN )
520 << QPointF( 1, -VERTEX_OFFSET_FROM_ORIGIN )
521 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, -1 )
522 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, -1 )
523 << QPointF( -1, -VERTEX_OFFSET_FROM_ORIGIN )
524 << QPointF( -1, VERTEX_OFFSET_FROM_ORIGIN )
525 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 );
526 return true;
527 }
528
530 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 0 ) ) );
531 return true;
532
534 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 1 ) ) );
535 return true;
536
538 polygon << QPointF( -1, -1 ) << QPointF( 1, 1 ) << QPointF( -1, 1 ) << QPointF( -1, -1 );
539 return true;
540
542 polygon << QPointF( 0.5, -0.5 )
543 << QPointF( 1, 0.5 )
544 << QPointF( -1, 0.5 )
545 << QPointF( -0.5, -0.5 )
546 << QPointF( 0.5, -0.5 );
547 return true;
548
550 polygon << QPointF( 0.5, 0.5 )
551 << QPointF( 1, -0.5 )
552 << QPointF( -0.5, -0.5 )
553 << QPointF( -1, 0.5 )
554 << QPointF( 0.5, 0.5 );
555 return true;
556
558 polygon << QPointF( 1, 0.5 )
559 << QPointF( 0.5, -0.5 )
560 << QPointF( -1, -0.5 )
561 << QPointF( -0.5, 0.5 )
562 << QPointF( 1, 0.5 );
563 return true;
564
566 polygon << QPointF( -1, 0 ) << QPointF( 0, 1 )
567 << QPointF( 1, 0 ) << QPointF( 0, -1 ) << QPointF( -1, 0 );
568 return true;
569
571 polygon << QPointF( 1, 0.5 )
572 << QPointF( 1, -1 )
573 << QPointF( -1, -1 )
574 << QPointF( -1, 0.5 )
575 << QPointF( 0, 1 )
576 << QPointF( 1, 0.5 );
577 return true;
578
580 /* angular-representation of hardcoded values used
581 polygon << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288.0 ) ) )
582 << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) )
583 << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) )
584 << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) )
585 << QPointF( 0, -1 ); */
586 polygon << QPointF( -0.9511, -0.3090 )
587 << QPointF( -0.5878, 0.8090 )
588 << QPointF( 0.5878, 0.8090 )
589 << QPointF( 0.9511, -0.3090 )
590 << QPointF( 0, -1 )
591 << QPointF( -0.9511, -0.3090 );
592 return true;
593
595 /* angular-representation of hardcoded values used
596 polygon << QPointF( std::sin( DEG2RAD( 300.0 ) ), - std::cos( DEG2RAD( 300.0 ) ) )
597 << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
598 << QPointF( std::sin( DEG2RAD( 180.0 ) ), - std::cos( DEG2RAD( 180.0 ) ) )
599 << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
600 << QPointF( std::sin( DEG2RAD( 60.0 ) ), - std::cos( DEG2RAD( 60.0 ) ) )
601 << QPointF( 0, -1 ); */
602 polygon << QPointF( -0.8660, -0.5 )
603 << QPointF( -0.8660, 0.5 )
604 << QPointF( 0, 1 )
605 << QPointF( 0.8660, 0.5 )
606 << QPointF( 0.8660, -0.5 )
607 << QPointF( 0, -1 )
608 << QPointF( -0.8660, -0.5 );
609 return true;
610
612 {
613 static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 1.0 / ( 1 + M_SQRT2 );
614
615 polygon << QPointF( - VERTEX_OFFSET_FROM_ORIGIN, 1 )
616 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, 1 )
617 << QPointF( 1, VERTEX_OFFSET_FROM_ORIGIN )
618 << QPointF( 1, -VERTEX_OFFSET_FROM_ORIGIN )
619 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, -1 )
620 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, -1 )
621 << QPointF( -1, -VERTEX_OFFSET_FROM_ORIGIN )
622 << QPointF( -1, VERTEX_OFFSET_FROM_ORIGIN )
623 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 );
624 return true;
625 }
626
628 {
629
630 polygon << QPointF( 0.587785252, 0.809016994 )
631 << QPointF( 0.951056516, 0.309016994 )
632 << QPointF( 0.951056516, -0.309016994 )
633 << QPointF( 0.587785252, -0.809016994 )
634 << QPointF( 0, -1 )
635 << QPointF( -0.587785252, -0.809016994 )
636 << QPointF( -0.951056516, -0.309016994 )
637 << QPointF( -0.951056516, 0.309016994 )
638 << QPointF( -0.587785252, 0.809016994 )
639 << QPointF( 0, 1 )
640 << QPointF( 0.587785252, 0.809016994 );
641 return true;
642 }
643
645 polygon << QPointF( -1, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
646 return true;
647
649 /* angular-representation of hardcoded values used
650 polygon << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
651 << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
652 << QPointF( 0, -1 ); */
653 polygon << QPointF( -0.8660, 0.5 )
654 << QPointF( 0.8660, 0.5 )
655 << QPointF( 0, -1 )
656 << QPointF( -0.8660, 0.5 );
657 return true;
658
660 polygon << QPointF( 0, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( 0, 1 );
661 return true;
662
664 polygon << QPointF( -1, 1 ) << QPointF( 0, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
665 return true;
666
668 {
669 const double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
670
671 polygon << QPointF( inner_r * std::sin( DEG2RAD( 315.0 ) ), - inner_r * std::cos( DEG2RAD( 315.0 ) ) )
672 << QPointF( std::sin( DEG2RAD( 270 ) ), - std::cos( DEG2RAD( 270 ) ) )
673 << QPointF( inner_r * std::sin( DEG2RAD( 225.0 ) ), - inner_r * std::cos( DEG2RAD( 225.0 ) ) )
674 << QPointF( std::sin( DEG2RAD( 180 ) ), - std::cos( DEG2RAD( 180 ) ) )
675 << QPointF( inner_r * std::sin( DEG2RAD( 135.0 ) ), - inner_r * std::cos( DEG2RAD( 135.0 ) ) )
676 << QPointF( std::sin( DEG2RAD( 90 ) ), - std::cos( DEG2RAD( 90 ) ) )
677 << QPointF( inner_r * std::sin( DEG2RAD( 45.0 ) ), - inner_r * std::cos( DEG2RAD( 45.0 ) ) )
678 << QPointF( std::sin( DEG2RAD( 0 ) ), - std::cos( DEG2RAD( 0 ) ) );
679 return true;
680 }
681
683 {
684 const double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
685
686 polygon << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ) // 324
687 << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288 ) ) ) // 288
688 << QPointF( inner_r * std::sin( DEG2RAD( 252.0 ) ), - inner_r * std::cos( DEG2RAD( 252.0 ) ) ) // 252
689 << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) ) // 216
690 << QPointF( 0, inner_r ) // 180
691 << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) ) // 144
692 << QPointF( inner_r * std::sin( DEG2RAD( 108.0 ) ), - inner_r * std::cos( DEG2RAD( 108.0 ) ) ) // 108
693 << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) ) // 72
694 << QPointF( inner_r * std::sin( DEG2RAD( 36.0 ) ), - inner_r * std::cos( DEG2RAD( 36.0 ) ) ) // 36
695 << QPointF( 0, -1 )
696 << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ); // 324; // 0
697 return true;
698 }
699
701 polygon << QPointF( 0, -1 )
702 << QPointF( 0.5, -0.5 )
703 << QPointF( 0.25, -0.5 )
704 << QPointF( 0.25, 1 )
705 << QPointF( -0.25, 1 )
706 << QPointF( -0.25, -0.5 )
707 << QPointF( -0.5, -0.5 )
708 << QPointF( 0, -1 );
709 return true;
710
712 polygon << QPointF( 0, 0 ) << QPointF( -1, 1 ) << QPointF( -1, -1 ) << QPointF( 0, 0 );
713 return true;
714
716 polygon << QPointF( -1, -0.2 )
717 << QPointF( -1, -0.2 )
718 << QPointF( -1, 0.2 )
719 << QPointF( -0.2, 0.2 )
720 << QPointF( -0.2, 1 )
721 << QPointF( 0.2, 1 )
722 << QPointF( 0.2, 0.2 )
723 << QPointF( 1, 0.2 )
724 << QPointF( 1, -0.2 )
725 << QPointF( 0.2, -0.2 )
726 << QPointF( 0.2, -1 )
727 << QPointF( -0.2, -1 )
728 << QPointF( -0.2, -0.2 )
729 << QPointF( -1, -0.2 );
730 return true;
731
733 {
734 static constexpr double THICKNESS = 0.3;
735 static constexpr double HALF_THICKNESS = THICKNESS / 2.0;
736 static constexpr double INTERSECTION_POINT = THICKNESS / M_SQRT2;
737 static constexpr double DIAGONAL1 = M_SQRT1_2 - INTERSECTION_POINT * 0.5;
738 static constexpr double DIAGONAL2 = M_SQRT1_2 + INTERSECTION_POINT * 0.5;
739
740 polygon << QPointF( -HALF_THICKNESS, -1 )
741 << QPointF( HALF_THICKNESS, -1 )
742 << QPointF( HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
743 << QPointF( DIAGONAL1, -DIAGONAL2 )
744 << QPointF( DIAGONAL2, -DIAGONAL1 )
745 << QPointF( HALF_THICKNESS + INTERSECTION_POINT, -HALF_THICKNESS )
746 << QPointF( 1, -HALF_THICKNESS )
747 << QPointF( 1, HALF_THICKNESS )
748 << QPointF( HALF_THICKNESS + INTERSECTION_POINT, HALF_THICKNESS )
749 << QPointF( DIAGONAL2, DIAGONAL1 )
750 << QPointF( DIAGONAL1, DIAGONAL2 )
751 << QPointF( HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
752 << QPointF( HALF_THICKNESS, 1 )
753 << QPointF( -HALF_THICKNESS, 1 )
754 << QPointF( -HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
755 << QPointF( -DIAGONAL1, DIAGONAL2 )
756 << QPointF( -DIAGONAL2, DIAGONAL1 )
757 << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, HALF_THICKNESS )
758 << QPointF( -1, HALF_THICKNESS )
759 << QPointF( -1, -HALF_THICKNESS )
760 << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, -HALF_THICKNESS )
761 << QPointF( -DIAGONAL2, -DIAGONAL1 )
762 << QPointF( -DIAGONAL1, -DIAGONAL2 )
763 << QPointF( -HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
764 << QPointF( -HALF_THICKNESS, -1 );
765 return true;
766 }
767
781 return false;
782 }
783
784 return false;
785}
786
788{
789 mPath = QPainterPath();
790
791 switch ( symbol )
792 {
794
795 mPath.addEllipse( QRectF( -1, -1, 2, 2 ) ); // x,y,w,h
796 return true;
797
799 mPath.moveTo( -1, -1 );
800 mPath.addRoundedRect( -1, -1, 2, 2, 0.25, 0.25 );
801 return true;
802
804 mPath.arcTo( -1, -1, 2, 2, 0, 180 );
805 mPath.lineTo( 0, 0 );
806 return true;
807
809 mPath.arcTo( -1, -1, 2, 2, 90, 120 );
810 mPath.lineTo( 0, 0 );
811 return true;
812
814 mPath.arcTo( -1, -1, 2, 2, 90, 90 );
815 mPath.lineTo( 0, 0 );
816 return true;
817
819 mPath.moveTo( 1, 0 );
820 mPath.arcTo( -1, -1, 2, 2, 0, 180 );
821 return true;
822
824 mPath.moveTo( 0, -1 );
825 mPath.arcTo( -1, -1, 2, 2, 90, 120 );
826 return true;
827
829 mPath.moveTo( 0, -1 );
830 mPath.arcTo( -1, -1, 2, 2, 90, 90 );
831 return true;
832
834 mPath.moveTo( -1, 0 );
835 mPath.lineTo( 1, 0 ); // horizontal
836 mPath.moveTo( 0, -1 );
837 mPath.lineTo( 0, 1 ); // vertical
838 return true;
839
841 mPath.moveTo( -1, -1 );
842 mPath.lineTo( 1, 1 );
843 mPath.moveTo( 1, -1 );
844 mPath.lineTo( -1, 1 );
845 return true;
846
848 mPath.moveTo( 0, -1 );
849 mPath.lineTo( 0, 1 ); // vertical line
850 return true;
851
853 mPath.moveTo( -1, -1 );
854 mPath.lineTo( 0, 0 );
855 mPath.lineTo( -1, 1 );
856 return true;
857
859 mPath.moveTo( 0, 0.75 );
860 mPath.arcTo( 0, -1, 1, 1, -45, 210 );
861 mPath.arcTo( -1, -1, 1, 1, 15, 210 );
862 mPath.lineTo( 0, 0.75 );
863 return true;
864
889 return false;
890 }
891 return false;
892}
893
894double QgsSimpleMarkerSymbolLayerBase::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
895{
896 double scaledSize = mSize;
897
899 bool ok = true;
900 if ( hasDataDefinedSize )
901 {
904 mSize, &ok );
905 }
906
907 if ( hasDataDefinedSize && ok )
908 {
909 switch ( mScaleMethod )
910 {
912 scaledSize = std::sqrt( scaledSize );
913 break;
915 break;
916 }
917 }
918
919 return scaledSize;
920}
921
922void QgsSimpleMarkerSymbolLayerBase::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle ) const
923{
924 //offset
925 double offsetX = 0;
926 double offsetY = 0;
927 markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
928 offset = QPointF( offsetX, offsetY );
929
930 hasDataDefinedRotation = false;
931 //angle
932 bool ok = true;
935 {
938
939 // If the expression evaluation was not successful, fallback to static value
940 if ( !ok )
942
943 hasDataDefinedRotation = true;
944 }
945
946 hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || hasDataDefinedRotation;
947
948 if ( hasDataDefinedRotation )
949 {
950 // For non-point markers, "dataDefinedRotation" means following the
951 // shape (shape-data defined). For them, "field-data defined" does
952 // not work at all. TODO: if "field-data defined" ever gets implemented
953 // we'll need a way to distinguish here between the two, possibly
954 // using another flag in renderHints()
955 const QgsFeature *f = context.feature();
956 if ( f )
957 {
958 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
959 {
960 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
961 angle += m2p.mapRotation();
962 }
963 }
964 }
965
966 if ( angle )
968}
969
970
971//
972// QgsSimpleMarkerSymbolLayer
973//
974
975QgsSimpleMarkerSymbolLayer::QgsSimpleMarkerSymbolLayer( Qgis::MarkerShape shape, double size, double angle, Qgis::ScaleMethod scaleMethod, const QColor &color, const QColor &strokeColor, Qt::PenJoinStyle penJoinStyle )
976 : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
977 , mStrokeColor( strokeColor )
978 , mPenJoinStyle( penJoinStyle )
979{
980 mColor = color;
981}
982
984
986{
994
995 if ( props.contains( QStringLiteral( "name" ) ) )
996 {
997 shape = decodeShape( props[QStringLiteral( "name" )].toString() );
998 }
999 if ( props.contains( QStringLiteral( "color" ) ) )
1000 color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )].toString() );
1001 if ( props.contains( QStringLiteral( "color_border" ) ) )
1002 {
1003 //pre 2.5 projects use "color_border"
1004 strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color_border" )].toString() );
1005 }
1006 else if ( props.contains( QStringLiteral( "outline_color" ) ) )
1007 {
1008 strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )].toString() );
1009 }
1010 else if ( props.contains( QStringLiteral( "line_color" ) ) )
1011 {
1012 strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )].toString() );
1013 }
1014 if ( props.contains( QStringLiteral( "joinstyle" ) ) )
1015 {
1016 penJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() );
1017 }
1018 if ( props.contains( QStringLiteral( "size" ) ) )
1019 size = props[QStringLiteral( "size" )].toDouble();
1020 if ( props.contains( QStringLiteral( "angle" ) ) )
1021 angle = props[QStringLiteral( "angle" )].toDouble();
1022 if ( props.contains( QStringLiteral( "scale_method" ) ) )
1023 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1024
1026 if ( props.contains( QStringLiteral( "offset" ) ) )
1027 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1028 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1029 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1030 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1031 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1032 if ( props.contains( QStringLiteral( "size_unit" ) ) )
1033 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1034 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1035 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1036
1037 if ( props.contains( QStringLiteral( "outline_style" ) ) )
1038 {
1039 m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "outline_style" )].toString() ) );
1040 }
1041 else if ( props.contains( QStringLiteral( "line_style" ) ) )
1042 {
1043 m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "line_style" )].toString() ) );
1044 }
1045 if ( props.contains( QStringLiteral( "outline_width" ) ) )
1046 {
1047 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
1048 }
1049 else if ( props.contains( QStringLiteral( "line_width" ) ) )
1050 {
1051 m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
1052 }
1053 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
1054 {
1055 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
1056 }
1057 if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
1058 {
1059 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
1060 }
1061 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
1062 {
1063 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
1064 }
1065
1066 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1067 {
1068 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1069 }
1070 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1071 {
1072 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1073 }
1074
1075 if ( props.contains( QStringLiteral( "cap_style" ) ) )
1076 {
1077 m->setPenCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( props[QStringLiteral( "cap_style" )].toString() ) );
1078 }
1079
1081
1082 return m;
1083}
1084
1085
1087{
1088 return QStringLiteral( "SimpleMarker" );
1089}
1090
1092{
1094
1095 QColor brushColor = mColor;
1096 QColor penColor = mStrokeColor;
1097
1098 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
1099 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
1100
1101 mBrush = QBrush( brushColor );
1102 mPen = QPen( penColor );
1103 mPen.setStyle( mStrokeStyle );
1104 mPen.setCapStyle( mPenCapStyle );
1105 mPen.setJoinStyle( mPenJoinStyle );
1107
1108 QColor selBrushColor = context.renderContext().selectionColor();
1109 QColor selPenColor = selBrushColor == mColor ? selBrushColor : mStrokeColor;
1110 if ( context.opacity() < 1 && !SELECTION_IS_OPAQUE )
1111 {
1112 selBrushColor.setAlphaF( context.opacity() );
1113 selPenColor.setAlphaF( context.opacity() );
1114 }
1115 mSelBrush = QBrush( selBrushColor );
1116 mSelPen = QPen( selPenColor );
1117 mSelPen.setStyle( mStrokeStyle );
1119
1121 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
1122
1123 // use caching only when:
1124 // - size, rotation, shape, color, stroke color is not data-defined
1125 // - drawing to screen (not printer)
1126 mUsingCache = !hasDataDefinedRotation && !hasDataDefinedSize && !context.renderContext().forceVectorOutput()
1130
1131 if ( mUsingCache )
1132 mCachedOpacity = context.opacity();
1133
1134 if ( !shapeIsFilled( mShape ) )
1135 {
1136 // some markers can't be drawn as a polygon (circle, cross)
1137 // For these set the selected stroke color to the selected color
1138 mSelPen.setColor( selBrushColor );
1139 }
1140
1141
1142 if ( mUsingCache )
1143 {
1144 if ( !prepareCache( context ) )
1145 {
1146 mUsingCache = false;
1147 }
1148 }
1149 else
1150 {
1151 mCache = QImage();
1152 mSelCache = QImage();
1153 }
1154}
1155
1156
1158{
1159 double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
1160 const double deviceRatio = context.renderContext().devicePixelRatio();
1161 if ( mSizeUnit == Qgis::RenderUnit::MetersInMapUnits && context.renderContext().flags() & Qgis::RenderContextFlag::RenderSymbolPreview )
1162 {
1163 // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
1164 // and clamp it to a reasonable range. It's the best we can do in this situation!
1165 scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
1166 }
1167
1168 // take into account angle (which is not data-defined otherwise cache wouldn't be used)
1169 if ( !qgsDoubleNear( mAngle, 0.0 ) )
1170 {
1171 scaledSize = ( std::abs( std::sin( mAngle * M_PI / 180 ) ) + std::abs( std::cos( mAngle * M_PI / 180 ) ) ) * scaledSize;
1172 }
1173 // calculate necessary image size for the cache
1174 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
1175 const int imageSize = ( static_cast< int >( scaledSize ) + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width
1176 const double center = imageSize / 2.0;
1177 if ( imageSize * deviceRatio > MAXIMUM_CACHE_WIDTH )
1178 {
1179 return false;
1180 }
1181
1182 mCache = QImage( QSize( imageSize * deviceRatio,
1183 imageSize * deviceRatio ), QImage::Format_ARGB32_Premultiplied );
1184 mCache.setDevicePixelRatio( context.renderContext().devicePixelRatio() );
1185 mCache.setDotsPerMeterX( std::round( context.renderContext().scaleFactor() * 1000 ) );
1186 mCache.setDotsPerMeterY( std::round( context.renderContext().scaleFactor() * 1000 ) );
1187 mCache.fill( 0 );
1188
1189 const bool needsBrush = shapeIsFilled( mShape );
1190
1191 QPainter p;
1192 p.begin( &mCache );
1193 p.setRenderHint( QPainter::Antialiasing );
1194 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1195 p.setPen( mPen );
1196 p.translate( QPointF( center, center ) );
1197 drawMarker( &p, context );
1198 p.end();
1199
1200 // Construct the selected version of the Cache
1201
1202 const QColor selColor = context.renderContext().selectionColor();
1203
1204 mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
1205 mSelCache.fill( 0 );
1206
1207 p.begin( &mSelCache );
1208 p.setRenderHint( QPainter::Antialiasing );
1209 p.setBrush( needsBrush ? mSelBrush : Qt::NoBrush );
1210 p.setPen( mSelPen );
1211 p.translate( QPointF( center, center ) );
1212 drawMarker( &p, context );
1213 p.end();
1214
1215 // Check that the selected version is different. If not, then re-render,
1216 // filling the background with the selection color and using the normal
1217 // colors for the symbol .. could be ugly!
1218
1219 if ( mSelCache == mCache )
1220 {
1221 p.begin( &mSelCache );
1222 p.setRenderHint( QPainter::Antialiasing );
1223 p.fillRect( 0, 0, imageSize, imageSize, selColor );
1224 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1225 p.setPen( mPen );
1226 p.translate( QPointF( center, center ) );
1227 drawMarker( &p, context );
1228 p.end();
1229 }
1230
1231 return true;
1232}
1233
1234void QgsSimpleMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
1235{
1236 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1237 //of the rendered point!
1238
1239 QPainter *p = context.renderContext().painter();
1240 if ( !p )
1241 {
1242 return;
1243 }
1244
1245 QColor brushColor = mColor;
1246 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
1247 mBrush.setColor( brushColor );
1248
1249 QColor penColor = mStrokeColor;
1250 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
1251 mPen.setColor( penColor );
1252
1253 bool ok = true;
1255 {
1258 if ( ok )
1259 {
1260 c.setAlphaF( c.alphaF() * context.opacity() );
1261 mBrush.setColor( c );
1262 }
1263 }
1265 {
1268 if ( ok )
1269 {
1270 c.setAlphaF( c.alphaF() * context.opacity() );
1271 mPen.setColor( c );
1272 mSelPen.setColor( c );
1273 }
1274 }
1276 {
1279 if ( ok )
1280 {
1283 }
1284 }
1286 {
1289 if ( ok )
1290 {
1293 }
1294 }
1296 {
1299 if ( ok )
1300 {
1301 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1302 mSelPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1303 }
1304 }
1306 {
1308 const QString style = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyCapStyle, context.renderContext().expressionContext(), QString(), &ok );
1309 if ( ok )
1310 {
1311 mPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1312 mSelPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1313 }
1314 }
1315
1316 if ( shapeIsFilled( shape ) )
1317 {
1318 p->setBrush( context.selected() ? mSelBrush : mBrush );
1319 }
1320 else
1321 {
1322 p->setBrush( Qt::NoBrush );
1323 }
1324 p->setPen( context.selected() ? mSelPen : mPen );
1325
1326 if ( !polygon.isEmpty() )
1327 p->drawPolygon( polygon );
1328 else
1329 p->drawPath( path );
1330}
1331
1333{
1334 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1335 //of the rendered point!
1336
1337 QPainter *p = context.renderContext().painter();
1338 if ( !p )
1339 {
1340 return;
1341 }
1342
1343 if ( mUsingCache && qgsDoubleNear( mCachedOpacity, context.opacity() ) )
1344 {
1345 const QImage &img = context.selected() ? mSelCache : mCache;
1346 const double s = img.width() / img.devicePixelRatioF();
1347
1348 bool hasDataDefinedSize = false;
1349 const double scaledSize = calculateSize( context, hasDataDefinedSize );
1350
1351 bool hasDataDefinedRotation = false;
1352 QPointF offset;
1353 double angle = 0;
1354 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
1355
1356 p->drawImage( QRectF( point.x() - s / 2.0 + offset.x(),
1357 point.y() - s / 2.0 + offset.y(),
1358 s, s ), img );
1359 }
1360 else
1361 {
1363 }
1364}
1365
1367{
1368 QVariantMap map;
1369 map[QStringLiteral( "name" )] = encodeShape( mShape );
1370 map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
1371 map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
1372 map[QStringLiteral( "size" )] = QString::number( mSize );
1373 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1374 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1375 map[QStringLiteral( "angle" )] = QString::number( mAngle );
1376 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1377 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1378 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1379 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1380 map[QStringLiteral( "outline_style" )] = QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle );
1381 map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
1382 map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
1383 map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
1384 map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
1385 map[QStringLiteral( "cap_style" )] = QgsSymbolLayerUtils::encodePenCapStyle( mPenCapStyle );
1386 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1387 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1388 return map;
1389}
1390
1392{
1394 m->setOffset( mOffset );
1395 m->setSizeUnit( mSizeUnit );
1407 copyPaintEffect( m );
1408 return m;
1409}
1410
1411void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1412{
1413 // <Graphic>
1414 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
1415 element.appendChild( graphicElem );
1416
1418 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
1420
1421 // <Rotation>
1422 QString angleFunc;
1423
1424 if ( mDataDefinedProperties.isActive( QgsSymbolLayer::Property::PropertyAngle ) )
1425 {
1426 angleFunc = mDataDefinedProperties.property( QgsSymbolLayer::Property::PropertyAngle ).asExpression();
1427 }
1428 else
1429 {
1430 bool ok;
1431 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
1432 if ( !ok )
1433 {
1434 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
1435 }
1436 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
1437 {
1438 angleFunc = QString::number( angle + mAngle );
1439 }
1440 }
1441
1442 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
1443
1444 // <Displacement>
1445 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
1447}
1448
1449QString QgsSimpleMarkerSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
1450{
1451 Q_UNUSED( mmScaleFactor )
1452 Q_UNUSED( mapUnitScaleFactor )
1453#if 0
1454 QString ogrType = "3"; //default is circle
1455 if ( mName == "square" )
1456 {
1457 ogrType = "5";
1458 }
1459 else if ( mName == "triangle" )
1460 {
1461 ogrType = "7";
1462 }
1463 else if ( mName == "star" )
1464 {
1465 ogrType = "9";
1466 }
1467 else if ( mName == "circle" )
1468 {
1469 ogrType = "3";
1470 }
1471 else if ( mName == "cross" )
1472 {
1473 ogrType = "0";
1474 }
1475 else if ( mName == "x" || mName == "cross2" )
1476 {
1477 ogrType = "1";
1478 }
1479 else if ( mName == "line" )
1480 {
1481 ogrType = "10";
1482 }
1483
1484 QString ogrString;
1485 ogrString.append( "SYMBOL(" );
1486 ogrString.append( "id:" );
1487 ogrString.append( '\"' );
1488 ogrString.append( "ogr-sym-" );
1489 ogrString.append( ogrType );
1490 ogrString.append( '\"' );
1491 ogrString.append( ",c:" );
1492 ogrString.append( mColor.name() );
1493 ogrString.append( ",o:" );
1494 ogrString.append( mStrokeColor.name() );
1495 ogrString.append( QString( ",s:%1mm" ).arg( mSize ) );
1496 ogrString.append( ')' );
1497 return ogrString;
1498#endif //0
1499
1500 QString ogrString;
1501 ogrString.append( "PEN(" );
1502 ogrString.append( "c:" );
1503 ogrString.append( mColor.name() );
1504 ogrString.append( ",w:" );
1505 ogrString.append( QString::number( mSize ) );
1506 ogrString.append( "mm" );
1507 ogrString.append( ")" );
1508 return ogrString;
1509}
1510
1512{
1513 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
1514
1515 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
1516 if ( graphicElem.isNull() )
1517 return nullptr;
1518
1519 QString name = QStringLiteral( "square" );
1520 QColor color, strokeColor;
1521 double strokeWidth, size;
1522 Qt::PenStyle strokeStyle;
1523
1525 return nullptr;
1526
1527 double angle = 0.0;
1528 QString angleFunc;
1529 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
1530 {
1531 bool ok;
1532 const double d = angleFunc.toDouble( &ok );
1533 if ( ok )
1534 angle = d;
1535 }
1536
1537 QPointF offset;
1539
1540 const Qgis::MarkerShape shape = decodeShape( name );
1541
1542 double scaleFactor = 1.0;
1543 const QString uom = element.attribute( QStringLiteral( "uom" ) );
1544 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
1545 size = size * scaleFactor;
1546 offset.setX( offset.x() * scaleFactor );
1547 offset.setY( offset.y() * scaleFactor );
1548
1550 m->setOutputUnit( sldUnitSize );
1551 m->setColor( color );
1553 m->setAngle( angle );
1554 m->setOffset( offset );
1557 return m;
1558}
1559
1561{
1562 Q_UNUSED( context )
1563
1564 if ( mPolygon.count() != 0 )
1565 {
1566 p->drawPolygon( mPolygon );
1567 }
1568 else
1569 {
1570 p->drawPath( mPath );
1571 }
1572}
1573
1574bool QgsSimpleMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
1575{
1576 //data defined size?
1577 double size = mSize;
1578
1579 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
1580
1581 //data defined size
1582 bool ok = true;
1583 if ( hasDataDefinedSize )
1584 {
1586
1587 if ( ok )
1588 {
1589 switch ( mScaleMethod )
1590 {
1592 size = std::sqrt( size );
1593 break;
1595 break;
1596 }
1597 }
1598 }
1599
1600 if ( mSizeUnit == Qgis::RenderUnit::Millimeters )
1601 {
1602 size *= mmMapUnitScaleFactor;
1603 }
1604
1605 if ( mSizeUnit == Qgis::RenderUnit::MapUnits )
1606 {
1608 }
1609 const double halfSize = size / 2.0;
1610
1611 //strokeWidth
1612 double strokeWidth = mStrokeWidth;
1613
1615 {
1618 }
1620 if ( mSizeUnit == Qgis::RenderUnit::MapUnits )
1621 {
1623 }
1624
1625 //color
1626 QColor pc = mPen.color();
1627 QColor bc = mBrush.color();
1629 {
1632 }
1634 {
1637 }
1638
1639 //offset
1640 double offsetX = 0;
1641 double offsetY = 0;
1642 markerOffset( context, offsetX, offsetY );
1643 offsetX *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1644 offsetY *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1645
1646
1647 QPointF off( offsetX, offsetY );
1648
1649 //angle
1650 double angle = mAngle + mLineAngle;
1652 {
1655 }
1656
1659 {
1661 const QString shapeName = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyName, context.renderContext().expressionContext(), QString(), &ok );
1662 if ( ok )
1663 {
1664 shape = decodeShape( shapeName, &ok );
1665 if ( !ok )
1666 shape = mShape;
1667 }
1668 }
1669
1670 if ( angle )
1671 off = _rotatedOffset( off, angle );
1672
1674
1675 QTransform t;
1676 t.translate( shift.x() + off.x(), shift.y() - off.y() );
1677
1678 if ( !qgsDoubleNear( angle, 0.0 ) )
1679 t.rotate( -angle );
1680
1681 QPolygonF polygon;
1682 if ( shapeToPolygon( shape, polygon ) )
1683 {
1684 t.scale( halfSize, -halfSize );
1685
1686 polygon = t.map( polygon );
1687
1689 p.reserve( polygon.size() );
1690 for ( int i = 0; i < polygon.size(); i++ )
1691 {
1692 p << QgsPoint( polygon[i] );
1693 }
1694
1695 if ( mBrush.style() != Qt::NoBrush )
1696 e.writePolygon( QgsRingSequence() << p, layerName, QStringLiteral( "SOLID" ), bc );
1697 if ( mPen.style() != Qt::NoPen )
1698 e.writePolyline( p, layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1699 }
1700 else if ( shape == Qgis::MarkerShape::Circle )
1701 {
1702 shift += QPointF( off.x(), -off.y() );
1703 if ( mBrush.style() != Qt::NoBrush )
1704 e.writeFilledCircle( layerName, bc, QgsPoint( shift ), halfSize );
1705 if ( mPen.style() != Qt::NoPen )
1706 e.writeCircle( layerName, pc, QgsPoint( shift ), halfSize, QStringLiteral( "CONTINUOUS" ), strokeWidth );
1707 }
1708 else if ( shape == Qgis::MarkerShape::Line )
1709 {
1710 const QPointF pt1 = t.map( QPointF( 0, -halfSize ) );
1711 const QPointF pt2 = t.map( QPointF( 0, halfSize ) );
1712
1713 if ( mPen.style() != Qt::NoPen )
1714 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1715 }
1716 else if ( shape == Qgis::MarkerShape::Cross )
1717 {
1718 if ( mPen.style() != Qt::NoPen )
1719 {
1720 const QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
1721 const QPointF pt2 = t.map( QPointF( halfSize, 0 ) );
1722 const QPointF pt3 = t.map( QPointF( 0, -halfSize ) );
1723 const QPointF pt4 = t.map( QPointF( 0, halfSize ) );
1724
1725 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1726 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1727 }
1728 }
1729 else if ( shape == Qgis::MarkerShape::Cross2 )
1730 {
1731 if ( mPen.style() != Qt::NoPen )
1732 {
1733 const QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
1734 const QPointF pt2 = t.map( QPointF( halfSize, halfSize ) );
1735 const QPointF pt3 = t.map( QPointF( halfSize, -halfSize ) );
1736 const QPointF pt4 = t.map( QPointF( -halfSize, halfSize ) );
1737
1738 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1739 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1740 }
1741 }
1742 else if ( shape == Qgis::MarkerShape::ArrowHead )
1743 {
1744 if ( mPen.style() != Qt::NoPen )
1745 {
1746 const QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
1747 const QPointF pt2 = t.map( QPointF( 0, 0 ) );
1748 const QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
1749
1750 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1751 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1752 }
1753 }
1754 else
1755 {
1756 QgsDebugError( QStringLiteral( "Unsupported dxf marker name %1" ).arg( encodeShape( shape ) ) );
1757 return false;
1758 }
1759
1760 return true;
1761}
1762
1763
1765{
1767 mStrokeWidthUnit = unit;
1768}
1769
1771{
1773 {
1774 return mStrokeWidthUnit;
1775 }
1776 return Qgis::RenderUnit::Unknown;
1777}
1778
1780{
1783}
1784
1786{
1788 {
1790 }
1791 return QgsMapUnitScale();
1792}
1793
1795{
1796 return mSizeUnit == Qgis::RenderUnit::MapUnits || mSizeUnit == Qgis::RenderUnit::MetersInMapUnits
1797 || mOffsetUnit == Qgis::RenderUnit::MapUnits || mOffsetUnit == Qgis::RenderUnit::MetersInMapUnits
1798 || mStrokeWidthUnit == Qgis::RenderUnit::MapUnits || mStrokeWidthUnit == Qgis::RenderUnit::MetersInMapUnits;
1799}
1800
1802{
1803 QRectF symbolBounds = QgsSimpleMarkerSymbolLayerBase::bounds( point, context );
1804
1805 // need to account for stroke width
1806 double penWidth = mStrokeWidth;
1807 bool ok = true;
1809 {
1812 if ( ok )
1813 {
1814 penWidth = strokeWidth;
1815 }
1816 }
1819 {
1822 if ( ok && strokeStyle == QLatin1String( "no" ) )
1823 {
1824 penWidth = 0.0;
1825 }
1826 }
1827 else if ( mStrokeStyle == Qt::NoPen )
1828 penWidth = 0;
1829
1830 //antialiasing, add 1 pixel
1831 penWidth += 1;
1832
1833 //extend bounds by pen width / 2.0
1834 symbolBounds.adjust( -penWidth / 2.0, -penWidth / 2.0,
1835 penWidth / 2.0, penWidth / 2.0 );
1836
1837 return symbolBounds;
1838}
1839
1840void QgsSimpleMarkerSymbolLayer::setColor( const QColor &color )
1841{
1842 if ( shapeIsFilled( mShape ) )
1843 {
1845 }
1846 else
1847 {
1849 }
1850}
1851
1853{
1854 if ( shapeIsFilled( mShape ) )
1855 {
1856 return fillColor();
1857 }
1858 else
1859 {
1860 return strokeColor();
1861 }
1862}
1863
1864
1865
1866
1867//
1868// QgsFilledMarkerSymbolLayer
1869//
1870
1872 : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
1873{
1874 mFill.reset( static_cast<QgsFillSymbol *>( QgsFillSymbol::createSimple( QVariantMap() ) ) );
1875}
1876
1878
1880{
1881 QString name = DEFAULT_SIMPLEMARKER_NAME;
1885
1886 if ( props.contains( QStringLiteral( "name" ) ) )
1887 name = props[QStringLiteral( "name" )].toString();
1888 if ( props.contains( QStringLiteral( "size" ) ) )
1889 size = props[QStringLiteral( "size" )].toDouble();
1890 if ( props.contains( QStringLiteral( "angle" ) ) )
1891 angle = props[QStringLiteral( "angle" )].toDouble();
1892 if ( props.contains( QStringLiteral( "scale_method" ) ) )
1893 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1894
1896 if ( props.contains( QStringLiteral( "offset" ) ) )
1897 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1898 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1899 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1900 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1901 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1902 if ( props.contains( QStringLiteral( "size_unit" ) ) )
1903 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1904 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1905 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1906 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1907 {
1908 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1909 }
1910 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1911 {
1912 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1913 }
1914
1916
1918
1919 return m;
1920}
1921
1923{
1924 return QStringLiteral( "FilledMarker" );
1925}
1926
1928{
1929 if ( mFill )
1930 {
1931 mFill->startRender( context.renderContext(), context.fields() );
1932 }
1933
1935}
1936
1938{
1939 if ( mFill )
1940 {
1941 mFill->stopRender( context.renderContext() );
1942 }
1943}
1944
1946{
1947 QVariantMap map;
1948 map[QStringLiteral( "name" )] = encodeShape( mShape );
1949 map[QStringLiteral( "size" )] = QString::number( mSize );
1950 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1951 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1952 map[QStringLiteral( "angle" )] = QString::number( mAngle );
1953 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1954 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1955 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1956 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1957 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1958 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1959
1960 if ( mFill )
1961 {
1962 map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mFill->color() );
1963 }
1964 return map;
1965}
1966
1968{
1970 copyPaintEffect( m );
1972 m->setSubSymbol( mFill->clone() );
1973 return m;
1974}
1975
1977{
1978 return mFill.get();
1979}
1980
1982{
1983 if ( symbol && symbol->type() == Qgis::SymbolType::Fill )
1984 {
1985 mFill.reset( static_cast<QgsFillSymbol *>( symbol ) );
1986 return true;
1987 }
1988 else
1989 {
1990 delete symbol;
1991 return false;
1992 }
1993}
1994
1996{
1997 if ( mFill )
1998 {
1999 return QgsSymbolLayerUtils::estimateMaxSymbolBleed( mFill.get(), context );
2000 }
2001 return 0;
2002}
2003
2005{
2006 QSet<QString> attr = QgsSimpleMarkerSymbolLayerBase::usedAttributes( context );
2007 if ( mFill )
2008 attr.unite( mFill->usedAttributes( context ) );
2009 return attr;
2010}
2011
2013{
2015 return true;
2016 if ( mFill && mFill->hasDataDefinedProperties() )
2017 return true;
2018 return false;
2019}
2020
2022{
2023 mColor = c;
2024 if ( mFill )
2025 mFill->setColor( c );
2026}
2027
2029{
2030 return mFill ? mFill->color() : mColor;
2031}
2032
2034{
2035 return mSizeUnit == Qgis::RenderUnit::MapUnits || mSizeUnit == Qgis::RenderUnit::MetersInMapUnits
2036 || mOffsetUnit == Qgis::RenderUnit::MapUnits || mOffsetUnit == Qgis::RenderUnit::MetersInMapUnits
2037 || ( mFill && mFill->usesMapUnits() );
2038}
2039
2041{
2043 if ( mFill )
2044 mFill->setOutputUnit( unit );
2045}
2046
2047void QgsFilledMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
2048{
2049 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
2050 //of the rendered point!
2051
2052 QPainter *p = context.renderContext().painter();
2053 if ( !p )
2054 {
2055 return;
2056 }
2057
2058 const double prevOpacity = mFill->opacity();
2059 mFill->setOpacity( mFill->opacity() * context.opacity() );
2060
2061 if ( shapeIsFilled( shape ) )
2062 {
2063 p->setBrush( Qt::red );
2064 }
2065 else
2066 {
2067 p->setBrush( Qt::NoBrush );
2068 }
2069 p->setPen( Qt::black );
2070
2071 const bool prevIsSubsymbol = context.renderContext().flags() & Qgis::RenderContextFlag::RenderingSubSymbol;
2073
2074 if ( !polygon.isEmpty() )
2075 {
2076 mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
2077 }
2078 else
2079 {
2080 const QPolygonF poly = path.toFillPolygon();
2081 mFill->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
2082 }
2083
2085
2086 mFill->setOpacity( prevOpacity );
2087}
2088
2089
2091
2092
2093QgsSvgMarkerSymbolLayer::QgsSvgMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
2094{
2095 mSize = size;
2096 mAngle = angle;
2097 mOffset = QPointF( 0, 0 );
2099 mStrokeWidth = 0.2;
2100 mStrokeWidthUnit = Qgis::RenderUnit::Millimeters;
2101 mColor = QColor( 35, 35, 35 );
2102 mStrokeColor = QColor( 35, 35, 35 );
2103 setPath( path );
2104}
2105
2107
2109{
2110 QString name;
2114
2115 if ( props.contains( QStringLiteral( "name" ) ) )
2116 name = props[QStringLiteral( "name" )].toString();
2117 if ( props.contains( QStringLiteral( "size" ) ) )
2118 size = props[QStringLiteral( "size" )].toDouble();
2119 if ( props.contains( QStringLiteral( "angle" ) ) )
2120 angle = props[QStringLiteral( "angle" )].toDouble();
2121 if ( props.contains( QStringLiteral( "scale_method" ) ) )
2122 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
2123
2125
2126 if ( props.contains( QStringLiteral( "size_unit" ) ) )
2127 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
2128 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
2129 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
2130 if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
2131 m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
2132 if ( props.contains( QStringLiteral( "offset" ) ) )
2133 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
2134 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
2135 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
2136 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2137 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
2138 if ( props.contains( QStringLiteral( "fill" ) ) )
2139 {
2140 //pre 2.5 projects used "fill"
2141 m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "fill" )].toString() ) );
2142 }
2143 else if ( props.contains( QStringLiteral( "color" ) ) )
2144 {
2145 m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )].toString() ) );
2146 }
2147 if ( props.contains( QStringLiteral( "outline" ) ) )
2148 {
2149 //pre 2.5 projects used "outline"
2150 m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline" )].toString() ) );
2151 }
2152 else if ( props.contains( QStringLiteral( "outline_color" ) ) )
2153 {
2154 m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )].toString() ) );
2155 }
2156 else if ( props.contains( QStringLiteral( "line_color" ) ) )
2157 {
2158 m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )].toString() ) );
2159 }
2160
2161 if ( props.contains( QStringLiteral( "outline-width" ) ) )
2162 {
2163 //pre 2.5 projects used "outline-width"
2164 m->setStrokeWidth( props[QStringLiteral( "outline-width" )].toDouble() );
2165 }
2166 else if ( props.contains( QStringLiteral( "outline_width" ) ) )
2167 {
2168 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
2169 }
2170 else if ( props.contains( QStringLiteral( "line_width" ) ) )
2171 {
2172 m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
2173 }
2174
2175 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
2176 {
2177 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
2178 }
2179 else if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
2180 {
2181 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
2182 }
2183 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
2184 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
2185
2186 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2187 {
2188 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2189 }
2190 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2191 {
2192 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2193 }
2194
2196
2198
2199 if ( props.contains( QStringLiteral( "parameters" ) ) )
2200 {
2201 const QVariantMap parameters = props[QStringLiteral( "parameters" )].toMap();
2203 }
2204
2205 return m;
2206}
2207
2208void QgsSvgMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
2209{
2210 const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
2211 if ( it != properties.end() )
2212 {
2213 if ( saving )
2214 {
2215 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
2216 }
2217 else
2218 {
2219 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
2220 }
2221 }
2222}
2223
2224void QgsSvgMarkerSymbolLayer::setPath( const QString &path )
2225{
2227 mHasFillParam = false;
2228 mPath = path;
2229 QColor defaultFillColor, defaultStrokeColor;
2230 double strokeWidth, fillOpacity, strokeOpacity;
2231 bool hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
2232 bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
2233 QgsApplication::svgCache()->containsParams( path, mHasFillParam, hasDefaultFillColor, defaultFillColor,
2234 hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
2235 hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2236 hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
2237 hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
2238
2239 const double newFillOpacity = hasFillOpacityParam ? fillColor().alphaF() : 1.0;
2240 const double newStrokeOpacity = hasStrokeOpacityParam ? strokeColor().alphaF() : 1.0;
2241
2242 if ( hasDefaultFillColor )
2243 {
2244 defaultFillColor.setAlphaF( newFillOpacity );
2245 setFillColor( defaultFillColor );
2246 }
2247 if ( hasDefaultFillOpacity )
2248 {
2249 QColor c = fillColor();
2250 c.setAlphaF( fillOpacity );
2251 setFillColor( c );
2252 }
2253 if ( hasDefaultStrokeColor )
2254 {
2255 defaultStrokeColor.setAlphaF( newStrokeOpacity );
2256 setStrokeColor( defaultStrokeColor );
2257 }
2258 if ( hasDefaultStrokeWidth )
2259 {
2261 }
2262 if ( hasDefaultStrokeOpacity )
2263 {
2264 QColor c = strokeColor();
2265 c.setAlphaF( strokeOpacity );
2266 setStrokeColor( c );
2267 }
2268
2270}
2271
2273{
2274 if ( mDefaultAspectRatio == 0.0 )
2275 {
2276 //size
2277 const double size = mSize;
2278 //assume 88 dpi as standard value
2279 const double widthScaleFactor = 3.465;
2280 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( mPath, size, mColor, mStrokeColor, mStrokeWidth, widthScaleFactor );
2281 // set default aspect ratio
2282 mDefaultAspectRatio = svgViewbox.isValid() ? svgViewbox.height() / svgViewbox.width() : 0.0;
2283 }
2284 return mDefaultAspectRatio;
2285}
2286
2288{
2289 const bool aPreservedAspectRatio = preservedAspectRatio();
2290 if ( aPreservedAspectRatio && !par )
2291 {
2293 }
2294 else if ( !aPreservedAspectRatio && par )
2295 {
2296 mFixedAspectRatio = 0.0;
2297 }
2298 return preservedAspectRatio();
2299}
2300
2301void QgsSvgMarkerSymbolLayer::setParameters( const QMap<QString, QgsProperty> &parameters )
2302{
2304}
2305
2306
2308{
2309 return QStringLiteral( "SvgMarker" );
2310}
2311
2313{
2314 QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
2315 Q_UNUSED( context )
2316}
2317
2319{
2320 Q_UNUSED( context )
2321}
2322
2324{
2325 QPainter *p = context.renderContext().painter();
2326 if ( !p )
2327 return;
2328
2329 bool hasDataDefinedSize = false;
2330 const double scaledWidth = calculateSize( context, hasDataDefinedSize );
2331 const double devicePixelRatio = context.renderContext().devicePixelRatio();
2332 const double width = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2333
2334 //don't render symbols with a width below one or above 10,000 pixels
2335 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2336 {
2337 return;
2338 }
2339
2340 const QgsScopedQPainterState painterState( p );
2341
2342 bool hasDataDefinedAspectRatio = false;
2343 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2344 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2345
2347
2348 double strokeWidth = mStrokeWidth;
2350 {
2353 }
2355
2356 QColor fillColor = mColor;
2357 if ( context.selected() && mHasFillParam )
2358 {
2360 }
2362 {
2365 }
2366
2367 QColor strokeColor = mStrokeColor;
2369 {
2372 }
2373
2374 QString path = mPath;
2376 {
2379 context.renderContext().pathResolver() );
2381 {
2382 // adjust height of data defined path
2383 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2384 context.renderContext().scaleFactor(), aspectRatio,
2385 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2386 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2387 }
2388 }
2389
2390 QPointF outputOffset;
2391 double angle = 0.0;
2392 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2393
2394 p->translate( point + outputOffset );
2395
2396 const bool rotated = !qgsDoubleNear( angle, 0 );
2397 if ( rotated )
2398 p->rotate( angle );
2399
2400 bool fitsInCache = true;
2401 bool usePict = true;
2402 const bool rasterizeSelected = !mHasFillParam || mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyName );
2403 if ( ( !context.renderContext().forceVectorOutput() && !rotated ) || ( context.selected() && rasterizeSelected ) )
2404 {
2405 QImage img = QgsApplication::svgCache()->svgAsImage( path, width * devicePixelRatio, fillColor, strokeColor, strokeWidth,
2406 context.renderContext().scaleFactor(), fitsInCache, aspectRatio,
2407 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2408 if ( fitsInCache && img.width() > 1 )
2409 {
2410 usePict = false;
2411
2412 if ( context.selected() )
2413 {
2415 }
2416
2417 //consider transparency
2418 if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2419 {
2420 QImage transparentImage = img.copy();
2421 QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2422 if ( devicePixelRatio == 1 )
2423 {
2424 p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
2425 }
2426 else
2427 {
2428 p->drawImage( QRectF( -transparentImage.width() / 2.0 / devicePixelRatio, -transparentImage.height() / 2.0 / devicePixelRatio,
2429 transparentImage.width() / devicePixelRatio, transparentImage.height() / devicePixelRatio
2430 ), transparentImage );
2431 }
2432 }
2433 else
2434 {
2435 if ( devicePixelRatio == 1 )
2436 {
2437 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2438 }
2439 else
2440 {
2441 p->drawImage( QRectF( -img.width() / 2.0 / devicePixelRatio, -img.height() / 2.0 / devicePixelRatio,
2442 img.width() / devicePixelRatio, img.height() / devicePixelRatio ), img );
2443 }
2444 }
2445 }
2446 }
2447
2448 if ( usePict || !fitsInCache )
2449 {
2450 p->setOpacity( context.opacity() );
2452 context.renderContext().scaleFactor(), context.renderContext().forceVectorOutput(), aspectRatio,
2453 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2454 if ( pct.width() > 1 )
2455 {
2456 const QgsScopedQPainterState painterPictureState( p );
2457 _fixQPictureDPI( p );
2458 p->drawPicture( 0, 0, pct );
2459 }
2460 }
2461
2462 // workaround issue with nested QPictures forgetting antialiasing flag - see https://github.com/qgis/QGIS/issues/22909
2464}
2465
2466double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2467{
2468 double scaledSize = mSize;
2470
2471 bool ok = true;
2472 if ( hasDataDefinedSize )
2473 {
2476 }
2477 else
2478 {
2480 if ( hasDataDefinedSize )
2481 {
2484 }
2485 }
2486
2487 if ( hasDataDefinedSize && ok )
2488 {
2489 switch ( mScaleMethod )
2490 {
2492 scaledSize = std::sqrt( scaledSize );
2493 break;
2495 break;
2496 }
2497 }
2498
2499 return scaledSize;
2500}
2501
2502double QgsSvgMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2503{
2505 if ( !hasDataDefinedAspectRatio )
2506 return mFixedAspectRatio;
2507
2509 return 0.0;
2510
2511 double scaledAspectRatio = mDefaultAspectRatio;
2512 if ( mFixedAspectRatio > 0.0 )
2513 scaledAspectRatio = mFixedAspectRatio;
2514
2515 const double defaultHeight = mSize * scaledAspectRatio;
2516 scaledAspectRatio = defaultHeight / scaledSize;
2517
2518 bool ok = true;
2519 double scaledHeight = scaledSize * scaledAspectRatio;
2521 {
2522 context.setOriginalValueVariable( defaultHeight );
2524 }
2525
2526 if ( hasDataDefinedAspectRatio && ok )
2527 {
2528 switch ( mScaleMethod )
2529 {
2531 scaledHeight = sqrt( scaledHeight );
2532 break;
2534 break;
2535 }
2536 }
2537
2538 scaledAspectRatio = scaledHeight / scaledSize;
2539
2540 return scaledAspectRatio;
2541}
2542
2543void QgsSvgMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
2544{
2545 //offset
2546 double offsetX = 0;
2547 double offsetY = 0;
2548 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
2549 offset = QPointF( offsetX, offsetY );
2550
2553 {
2556 }
2557
2559 if ( hasDataDefinedRotation )
2560 {
2561 // For non-point markers, "dataDefinedRotation" means following the
2562 // shape (shape-data defined). For them, "field-data defined" does
2563 // not work at all. TODO: if "field-data defined" ever gets implemented
2564 // we'll need a way to distinguish here between the two, possibly
2565 // using another flag in renderHints()
2566 const QgsFeature *f = context.feature();
2567 if ( f )
2568 {
2569 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
2570 {
2571 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2572 angle += m2p.mapRotation();
2573 }
2574 }
2575 }
2576
2577 if ( angle )
2579}
2580
2581
2583{
2584 QVariantMap map;
2585 map[QStringLiteral( "name" )] = mPath;
2586 map[QStringLiteral( "size" )] = QString::number( mSize );
2587 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2588 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2589 map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2590 map[QStringLiteral( "angle" )] = QString::number( mAngle );
2591 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2592 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2593 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2594 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2595 map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
2596 map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
2597 map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
2598 map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2599 map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2600 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
2601 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
2602
2603 map[QStringLiteral( "parameters" )] = QgsProperty::propertyMapToVariantMap( mParameters );
2604
2605 return map;
2606}
2607
2609{
2610 return mSizeUnit == Qgis::RenderUnit::MapUnits || mSizeUnit == Qgis::RenderUnit::MetersInMapUnits
2611 || mOffsetUnit == Qgis::RenderUnit::MapUnits || mOffsetUnit == Qgis::RenderUnit::MetersInMapUnits
2612 || mStrokeWidthUnit == Qgis::RenderUnit::MapUnits || mStrokeWidthUnit == Qgis::RenderUnit::MetersInMapUnits;
2613}
2614
2616{
2619 m->setColor( mColor );
2624 m->setOffset( mOffset );
2627 m->setSizeUnit( mSizeUnit );
2632
2634 copyPaintEffect( m );
2635 return m;
2636}
2637
2639{
2641 mStrokeWidthUnit = unit;
2642}
2643
2645{
2647 if ( unit != mStrokeWidthUnit )
2648 {
2649 return Qgis::RenderUnit::Unknown;
2650 }
2651 return unit;
2652}
2653
2655{
2658}
2659
2661{
2663 {
2665 }
2666 return QgsMapUnitScale();
2667}
2668
2669void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
2670{
2671 // <Graphic>
2672 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2673 element.appendChild( graphicElem );
2674
2675 // encode a parametric SVG reference
2676 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
2679
2680 // <Rotation>
2681 QString angleFunc;
2682 bool ok;
2683 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2684 if ( !ok )
2685 {
2686 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
2687 }
2688 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2689 {
2690 angleFunc = QString::number( angle + mAngle );
2691 }
2692
2693 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2694
2695 // <Displacement>
2696 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2698}
2699
2701{
2702 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
2703
2704 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
2705 if ( graphicElem.isNull() )
2706 return nullptr;
2707
2708 QString path, mimeType;
2709 // Unused and to be DEPRECATED in externalGraphicFromSld
2710 QColor fillColor_;
2711 double size;
2712
2713 if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor_, size ) )
2714 return nullptr;
2715
2716 double scaleFactor = 1.0;
2717 const QString uom = element.attribute( QStringLiteral( "uom" ) );
2718 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
2719 size = size * scaleFactor;
2720
2721 if ( mimeType != QLatin1String( "image/svg+xml" ) )
2722 return nullptr;
2723
2724 double angle = 0.0;
2725 QString angleFunc;
2726 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2727 {
2728 bool ok;
2729 const double d = angleFunc.toDouble( &ok );
2730 if ( ok )
2731 angle = d;
2732 }
2733
2734 QPointF offset;
2736
2737 // Extract parameters from URL
2738 QString realPath { path };
2739 QUrl svgUrl { path };
2740
2741 // Because color definition can start with '#', the url parsing won't recognize the query string entirely
2742 QUrlQuery queryString;
2743
2744 if ( svgUrl.hasQuery() && svgUrl.hasFragment() )
2745 {
2746 const QString queryPart { path.mid( path.indexOf( '?' ) + 1 ) };
2747 queryString.setQuery( queryPart );
2748 }
2749
2750 // Remove query for simple file paths
2751 if ( svgUrl.scheme().isEmpty() || svgUrl.isLocalFile() )
2752 {
2753 svgUrl.setQuery( QString() );
2754 realPath = svgUrl.path();
2755 }
2756
2758
2759 QMap<QString, QgsProperty> params;
2760
2761 bool ok;
2762
2763 if ( queryString.hasQueryItem( QStringLiteral( "fill" ) ) )
2764 {
2765 const QColor fillColor { queryString.queryItemValue( QStringLiteral( "fill" ) ) };
2766 m->setFillColor( fillColor );
2767 }
2768
2769 if ( queryString.hasQueryItem( QStringLiteral( "fill-opacity" ) ) )
2770 {
2771 const double alpha { queryString.queryItemValue( QStringLiteral( "fill-opacity" ) ).toDouble( &ok ) };
2772 if ( ok )
2773 {
2774 params.insert( QStringLiteral( "fill-opacity" ), QgsProperty::fromValue( alpha ) );
2775 }
2776 }
2777
2778 if ( queryString.hasQueryItem( QStringLiteral( "outline" ) ) )
2779 {
2780 const QColor strokeColor { queryString.queryItemValue( QStringLiteral( "outline" ) ) };
2782 }
2783
2784 if ( queryString.hasQueryItem( QStringLiteral( "outline-opacity" ) ) )
2785 {
2786 const double alpha { queryString.queryItemValue( QStringLiteral( "outline-opacity" ) ).toDouble( &ok ) };
2787 if ( ok )
2788 {
2789 params.insert( QStringLiteral( "outline-opacity" ), QgsProperty::fromValue( alpha ) );
2790 }
2791 }
2792
2793 if ( queryString.hasQueryItem( QStringLiteral( "outline-width" ) ) )
2794 {
2795 const int width { queryString.queryItemValue( QStringLiteral( "outline-width" ) ).toInt( &ok )};
2796 if ( ok )
2797 {
2798 m->setStrokeWidth( width );
2799 }
2800 }
2801
2802 if ( ! params.isEmpty() )
2803 {
2804 m->setParameters( params );
2805 }
2806
2807 m->setOutputUnit( sldUnitSize );
2808 m->setAngle( angle );
2809 m->setOffset( offset );
2810 return m;
2811}
2812
2813bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
2814{
2815 //size
2816 double size = mSize;
2817
2818 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
2819
2820 bool ok = true;
2821 if ( hasDataDefinedSize )
2822 {
2825 }
2826
2827 if ( hasDataDefinedSize && ok )
2828 {
2829 switch ( mScaleMethod )
2830 {
2832 size = std::sqrt( size );
2833 break;
2835 break;
2836 }
2837 }
2838
2839 if ( mSizeUnit == Qgis::RenderUnit::Millimeters )
2840 {
2841 size *= mmMapUnitScaleFactor;
2842 }
2843
2844 //offset, angle
2845 QPointF offset = mOffset;
2846
2848 {
2850 const QVariant val = mDataDefinedProperties.value( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext(), QString() );
2851 const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
2852 if ( ok )
2853 offset = res;
2854 }
2855 const double offsetX = offset.x();
2856 const double offsetY = offset.y();
2857
2858 QPointF outputOffset( offsetX, offsetY );
2859
2860 double angle = mAngle + mLineAngle;
2862 {
2865 }
2866
2867 if ( angle )
2868 outputOffset = _rotatedOffset( outputOffset, angle );
2869
2871
2872 QString path = mPath;
2874 {
2877 context.renderContext().pathResolver() );
2878 }
2879
2880 double strokeWidth = mStrokeWidth;
2882 {
2885 }
2887
2888 QColor fillColor = mColor;
2890 {
2893 }
2894
2895 QColor strokeColor = mStrokeColor;
2897 {
2900 }
2901
2903
2904 const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, size, fillColor, strokeColor, strokeWidth,
2906 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2907
2908 QSvgRenderer r( svgContent );
2909 if ( !r.isValid() )
2910 return false;
2911
2912 QgsDxfPaintDevice pd( &e );
2913 pd.setDrawingSize( QSizeF( r.defaultSize() ) );
2914
2915 QSizeF outSize( r.defaultSize() );
2916 outSize.scale( size, size, Qt::KeepAspectRatio );
2917
2918 QPainter p;
2919 p.begin( &pd );
2920 if ( !qgsDoubleNear( angle, 0.0 ) )
2921 {
2922 p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
2923 p.rotate( angle );
2924 p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
2925 }
2926 pd.setShift( shift + QPointF( outputOffset.x(), -outputOffset.y() ) );
2927 pd.setOutputSize( QRectF( -outSize.width() / 2.0, -outSize.height() / 2.0, outSize.width(), outSize.height() ) );
2928 pd.setLayer( layerName );
2929 r.render( &p );
2930 p.end();
2931 return true;
2932}
2933
2935{
2936 bool hasDataDefinedSize = false;
2937 double scaledWidth = calculateSize( context, hasDataDefinedSize );
2938
2939 bool hasDataDefinedAspectRatio = false;
2940 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2941 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2942
2943 scaledWidth = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2944 scaledHeight = context.renderContext().convertToPainterUnits( scaledHeight, mSizeUnit, mSizeMapUnitScale );
2945
2946 //don't render symbols with size below one or above 10,000 pixels
2947 if ( static_cast< int >( scaledWidth ) < 1 || 10000.0 < scaledWidth )
2948 {
2949 return QRectF();
2950 }
2951
2952 QPointF outputOffset;
2953 double angle = 0.0;
2954 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2955
2956 double strokeWidth = mStrokeWidth;
2958 {
2961 }
2963
2964 QString path = mPath;
2966 {
2969 context.renderContext().pathResolver() );
2971 {
2972 // need to get colors to take advantage of cached SVGs
2973 QColor fillColor = mColor;
2975 {
2978 }
2979
2980 const QColor strokeColor = mStrokeColor;
2982 {
2985 }
2986
2988
2989 // adjust height of data defined path
2990 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2991 context.renderContext().scaleFactor(), aspectRatio,
2992 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2993 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2994 }
2995 }
2996
2997 QTransform transform;
2998 // move to the desired position
2999 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3000
3001 if ( !qgsDoubleNear( angle, 0.0 ) )
3002 transform.rotate( angle );
3003
3004 //antialiasing
3005 strokeWidth += 1.0 / 2.0;
3006
3007 QRectF symbolBounds = transform.mapRect( QRectF( -scaledWidth / 2.0,
3008 -scaledHeight / 2.0,
3009 scaledWidth,
3010 scaledHeight ) );
3011
3012 //extend bounds by pen width / 2.0
3013 symbolBounds.adjust( -strokeWidth / 2.0, -strokeWidth / 2.0,
3014 strokeWidth / 2.0, strokeWidth / 2.0 );
3015
3016 return symbolBounds;
3017}
3018
3020
3021QgsRasterMarkerSymbolLayer::QgsRasterMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
3022 : mPath( path )
3023{
3024 mSize = size;
3025 mAngle = angle;
3026 mOffset = QPointF( 0, 0 );
3029}
3030
3032
3034{
3035 QString path;
3039
3040 if ( props.contains( QStringLiteral( "imageFile" ) ) )
3041 path = props[QStringLiteral( "imageFile" )].toString();
3042 if ( props.contains( QStringLiteral( "size" ) ) )
3043 size = props[QStringLiteral( "size" )].toDouble();
3044 if ( props.contains( QStringLiteral( "angle" ) ) )
3045 angle = props[QStringLiteral( "angle" )].toDouble();
3046 if ( props.contains( QStringLiteral( "scale_method" ) ) )
3047 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
3048
3049 std::unique_ptr< QgsRasterMarkerSymbolLayer > m = std::make_unique< QgsRasterMarkerSymbolLayer >( path, size, angle, scaleMethod );
3050 m->setCommonProperties( props );
3051 return m.release();
3052}
3053
3054void QgsRasterMarkerSymbolLayer::setCommonProperties( const QVariantMap &properties )
3055{
3056 if ( properties.contains( QStringLiteral( "alpha" ) ) )
3057 {
3058 setOpacity( properties[QStringLiteral( "alpha" )].toDouble() );
3059 }
3060
3061 if ( properties.contains( QStringLiteral( "size_unit" ) ) )
3062 setSizeUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "size_unit" )].toString() ) );
3063 if ( properties.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3064 setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3065 if ( properties.contains( QStringLiteral( "fixedAspectRatio" ) ) )
3066 setFixedAspectRatio( properties[QStringLiteral( "fixedAspectRatio" )].toDouble() );
3067
3068 if ( properties.contains( QStringLiteral( "offset" ) ) )
3069 setOffset( QgsSymbolLayerUtils::decodePoint( properties[QStringLiteral( "offset" )].toString() ) );
3070 if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
3071 setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )].toString() ) );
3072 if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3073 setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3074
3075 if ( properties.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3076 {
3077 setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( properties[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3078 }
3079 if ( properties.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3080 {
3081 setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( properties[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3082 }
3083
3086}
3087
3088void QgsRasterMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
3089{
3090 const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
3091 if ( it != properties.end() && it.value().type() == QVariant::String )
3092 {
3093 if ( saving )
3094 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
3095 else
3096 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
3097 }
3098}
3099
3100void QgsRasterMarkerSymbolLayer::setPath( const QString &path )
3101{
3102 mPath = path;
3104}
3105
3107{
3108 const bool aPreservedAspectRatio = preservedAspectRatio();
3109 if ( aPreservedAspectRatio && !par )
3110 {
3112 }
3113 else if ( !aPreservedAspectRatio && par )
3114 {
3115 mFixedAspectRatio = 0.0;
3116 }
3117 return preservedAspectRatio();
3118}
3119
3121{
3122 if ( mDefaultAspectRatio == 0.0 )
3123 {
3125 mDefaultAspectRatio = ( !size.isNull() && size.isValid() && size.width() > 0 ) ? static_cast< double >( size.height() ) / static_cast< double >( size.width() ) : 0.0;
3126 }
3127 return mDefaultAspectRatio;
3128}
3129
3131{
3132 return QStringLiteral( "RasterMarker" );
3133}
3134
3136{
3137 QPainter *p = context.renderContext().painter();
3138 if ( !p )
3139 return;
3140
3141 QString path = mPath;
3143 {
3146 }
3147
3148 if ( path.isEmpty() )
3149 return;
3150
3151 double width = 0.0;
3152 double height = 0.0;
3153
3154 bool hasDataDefinedSize = false;
3155 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3156
3157 bool hasDataDefinedAspectRatio = false;
3158 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3159
3160 QPointF outputOffset;
3161 double angle = 0.0;
3162
3163 // RenderPercentage Unit Type takes original image size
3164 if ( mSizeUnit == Qgis::RenderUnit::Percentage )
3165 {
3167 if ( size.isEmpty() )
3168 return;
3169
3170 width = ( scaledSize * static_cast< double >( size.width() ) ) / 100.0;
3171 height = ( scaledSize * static_cast< double >( size.height() ) ) / 100.0;
3172
3173 // don't render symbols with size below one or above 10,000 pixels
3174 if ( static_cast< int >( width ) < 1 || 10000.0 < width || static_cast< int >( height ) < 1 || 10000.0 < height )
3175 return;
3176
3177 calculateOffsetAndRotation( context, width, height, outputOffset, angle );
3178 }
3179 else
3180 {
3181 width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3182 height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3183
3184 if ( preservedAspectRatio() && path != mPath )
3185 {
3187 if ( !size.isNull() && size.isValid() && size.width() > 0 )
3188 {
3189 height = width * ( static_cast< double >( size.height() ) / static_cast< double >( size.width() ) );
3190 }
3191 }
3192
3193 // don't render symbols with size below one or above 10,000 pixels
3194 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
3195 return;
3196
3197 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3198 }
3199
3200 const QgsScopedQPainterState painterState( p );
3201 p->translate( point + outputOffset );
3202
3203 const bool rotated = !qgsDoubleNear( angle, 0 );
3204 if ( rotated )
3205 p->rotate( angle );
3206
3207 double opacity = mOpacity;
3209 {
3212 }
3213 opacity *= context.opacity();
3214
3215 QImage img = fetchImage( context.renderContext(), path, QSize( width, preservedAspectRatio() ? 0 : width * aspectRatio ), preservedAspectRatio(), opacity );
3216 if ( !img.isNull() )
3217 {
3218 if ( context.selected() )
3219 {
3221 }
3222
3223 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
3224 }
3225}
3226
3227QImage QgsRasterMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
3228{
3229 bool cached = false;
3230 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking );
3231}
3232
3233double QgsRasterMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
3234{
3235 double scaledSize = mSize;
3237
3238 bool ok = true;
3239 if ( hasDataDefinedSize )
3240 {
3243 }
3244 else
3245 {
3247 if ( hasDataDefinedSize )
3248 {
3251 }
3252 }
3253
3254 if ( hasDataDefinedSize && ok )
3255 {
3256 switch ( mScaleMethod )
3257 {
3259 scaledSize = std::sqrt( scaledSize );
3260 break;
3262 break;
3263 }
3264 }
3265
3266 return scaledSize;
3267}
3268
3269double QgsRasterMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
3270{
3272 if ( !hasDataDefinedAspectRatio )
3273 return mFixedAspectRatio;
3274
3276 return 0.0;
3277
3278 double scaledAspectRatio = mDefaultAspectRatio;
3279 if ( mFixedAspectRatio > 0.0 )
3280 scaledAspectRatio = mFixedAspectRatio;
3281
3282 const double defaultHeight = mSize * scaledAspectRatio;
3283 scaledAspectRatio = defaultHeight / scaledSize;
3284
3285 bool ok = true;
3286 double scaledHeight = scaledSize * scaledAspectRatio;
3288 {
3289 context.setOriginalValueVariable( defaultHeight );
3291 }
3292
3293 if ( hasDataDefinedAspectRatio && ok )
3294 {
3295 switch ( mScaleMethod )
3296 {
3298 scaledHeight = sqrt( scaledHeight );
3299 break;
3301 break;
3302 }
3303 }
3304
3305 scaledAspectRatio = scaledHeight / scaledSize;
3306
3307 return scaledAspectRatio;
3308}
3309
3310void QgsRasterMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
3311{
3312 //offset
3313 double offsetX = 0;
3314 double offsetY = 0;
3315 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
3316 offset = QPointF( offsetX, offsetY );
3317
3320 {
3323 }
3324
3326 if ( hasDataDefinedRotation )
3327 {
3328 const QgsFeature *f = context.feature();
3329 if ( f )
3330 {
3331 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3332 {
3333 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3334 angle += m2p.mapRotation();
3335 }
3336 }
3337 }
3338
3339 if ( angle )
3341}
3342
3343
3345{
3346 QVariantMap map;
3347 map[QStringLiteral( "imageFile" )] = mPath;
3348 map[QStringLiteral( "size" )] = QString::number( mSize );
3349 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3350 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3351 map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
3352 map[QStringLiteral( "angle" )] = QString::number( mAngle );
3353 map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
3354 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3355 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3356 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3357 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
3358 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3359 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3360 return map;
3361}
3362
3364{
3365 std::unique_ptr< QgsRasterMarkerSymbolLayer > m = std::make_unique< QgsRasterMarkerSymbolLayer >( mPath, mSize, mAngle );
3366 copyCommonProperties( m.get() );
3367 return m.release();
3368}
3369
3370
3372{
3374 other->setOpacity( mOpacity );
3375 other->setOffset( mOffset );
3376 other->setOffsetUnit( mOffsetUnit );
3378 other->setSizeUnit( mSizeUnit );
3383 copyPaintEffect( other );
3384}
3385
3387{
3388 return mSizeUnit == Qgis::RenderUnit::MapUnits || mSizeUnit == Qgis::RenderUnit::MetersInMapUnits
3389 || mOffsetUnit == Qgis::RenderUnit::MapUnits || mOffsetUnit == Qgis::RenderUnit::MetersInMapUnits;
3390}
3391
3393{
3394 return QColor();
3395}
3396
3398{
3400}
3401
3403{
3405}
3406
3408{
3409 bool hasDataDefinedSize = false;
3410 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3411 const double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3412 bool hasDataDefinedAspectRatio = false;
3413 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3414 const double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3415
3416 //don't render symbols with size below one or above 10,000 pixels
3417 if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
3418 {
3419 return QRectF();
3420 }
3421
3422 QPointF outputOffset;
3423 double angle = 0.0;
3424 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3425
3426 QTransform transform;
3427
3428 // move to the desired position
3429 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3430
3431 if ( !qgsDoubleNear( angle, 0.0 ) )
3432 transform.rotate( angle );
3433
3434 QRectF symbolBounds = transform.mapRect( QRectF( -width / 2.0,
3435 -height / 2.0,
3436 width,
3437 height ) );
3438
3439 return symbolBounds;
3440}
3441
3443
3444QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QString chr, double pointSize, const QColor &color, double angle )
3445{
3446 mFontFamily = fontFamily;
3447 mString = chr;
3448 mColor = color;
3449 mAngle = angle;
3450 mSize = pointSize;
3451 mOrigSize = pointSize;
3452 mSizeUnit = Qgis::RenderUnit::Millimeters;
3453 mOffset = QPointF( 0, 0 );
3454 mOffsetUnit = Qgis::RenderUnit::Millimeters;
3455 mStrokeColor = DEFAULT_FONTMARKER_BORDERCOLOR;
3456 mStrokeWidth = 0.0;
3457 mStrokeWidthUnit = Qgis::RenderUnit::Millimeters;
3458 mPenJoinStyle = DEFAULT_FONTMARKER_JOINSTYLE;
3459}
3460
3462
3464{
3466 QString string = DEFAULT_FONTMARKER_CHR;
3467 double pointSize = DEFAULT_FONTMARKER_SIZE;
3470
3471 if ( props.contains( QStringLiteral( "font" ) ) )
3472 fontFamily = props[QStringLiteral( "font" )].toString();
3473 if ( props.contains( QStringLiteral( "chr" ) ) && props[QStringLiteral( "chr" )].toString().length() > 0 )
3474 string = props[QStringLiteral( "chr" )].toString();
3475 if ( props.contains( QStringLiteral( "size" ) ) )
3476 pointSize = props[QStringLiteral( "size" )].toDouble();
3477 if ( props.contains( QStringLiteral( "color" ) ) )
3478 color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )].toString() );
3479 if ( props.contains( QStringLiteral( "angle" ) ) )
3480 angle = props[QStringLiteral( "angle" )].toDouble();
3481
3483
3484 if ( props.contains( QStringLiteral( "font_style" ) ) )
3485 m->setFontStyle( props[QStringLiteral( "font_style" )].toString() );
3486 if ( props.contains( QStringLiteral( "outline_color" ) ) )
3487 m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )].toString() ) );
3488 if ( props.contains( QStringLiteral( "outline_width" ) ) )
3489 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
3490 if ( props.contains( QStringLiteral( "offset" ) ) )
3491 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
3492 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
3493 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
3494 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3495 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3496 if ( props.contains( QStringLiteral( "size_unit" ) ) )
3497 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
3498 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3499 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3500 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
3501 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
3502 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
3503 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
3504 if ( props.contains( QStringLiteral( "joinstyle" ) ) )
3505 m->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() ) );
3506 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3507 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3508 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3509 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3510
3512
3513 return m;
3514}
3515
3517{
3518 return QStringLiteral( "FontMarker" );
3519}
3520
3522{
3523 QColor brushColor = mColor;
3524 QColor penColor = mStrokeColor;
3525
3526 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
3527 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
3528
3529 mBrush = QBrush( brushColor );
3530 mPen = QPen( penColor );
3531 mPen.setJoinStyle( mPenJoinStyle );
3532 mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
3533
3534 mFont = QFont( QgsApplication::fontManager()->processFontFamilyName( mFontFamily ) );
3535 if ( !mFontStyle.isEmpty() )
3536 {
3537 mFont.setStyleName( QgsFontUtils::translateNamedStyle( mFontStyle ) );
3538 }
3539
3540 double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3541 mNonZeroFontSize = !qgsDoubleNear( sizePixels, 0.0 );
3542
3543 if ( mNonZeroFontSize && sizePixels > MAX_FONT_CHARACTER_SIZE_IN_PIXELS )
3544 {
3545 // if font is too large (e.g using map units and map is very zoomed in), then we limit
3546 // the font size and instead scale up the painter.
3547 // this avoids issues with massive font sizes (eg https://github.com/qgis/QGIS/issues/42270)
3548 mFontSizeScale = sizePixels / MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3549 sizePixels = MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3550 }
3551 else
3552 mFontSizeScale = 1.0;
3553
3554 // if a non zero, but small pixel size results, round up to 2 pixels so that a "dot" is at least visible
3555 // (if we set a <=1 pixel size here Qt will reset the font to a default size, leading to much too large symbols)
3556 mFont.setPixelSize( std::max( 2, static_cast< int >( std::round( sizePixels ) ) ) );
3557 mFontMetrics.reset( new QFontMetrics( mFont ) );
3558 mChrWidth = mFontMetrics->horizontalAdvance( mString );
3559 mChrOffset = QPointF( mChrWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3560 mOrigSize = mSize; // save in case the size would be data defined
3561
3562 // use caching only when not using a data defined character
3566 if ( mUseCachedPath )
3567 {
3568 QPointF chrOffset = mChrOffset;
3569 double chrWidth;
3570 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3571 mCachedPath = QPainterPath();
3572 mCachedPath.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3573 }
3574}
3575
3577{
3578 Q_UNUSED( context )
3579}
3580
3581QString QgsFontMarkerSymbolLayer::characterToRender( QgsSymbolRenderContext &context, QPointF &charOffset, double &charWidth )
3582{
3583 charOffset = mChrOffset;
3584 QString stringToRender = mString;
3586 {
3587 context.setOriginalValueVariable( mString );
3589 if ( stringToRender != mString )
3590 {
3591 charWidth = mFontMetrics->horizontalAdvance( stringToRender );
3592 charOffset = QPointF( charWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3593 }
3594 }
3595 return stringToRender;
3596}
3597
3598void QgsFontMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
3599 double scaledSize,
3600 bool &hasDataDefinedRotation,
3601 QPointF &offset,
3602 double &angle ) const
3603{
3604 //offset
3605 double offsetX = 0;
3606 double offsetY = 0;
3607 markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
3608 offset = QPointF( offsetX, offsetY );
3609
3610 //angle
3611 bool ok = true;
3614 {
3617
3618 // If the expression evaluation was not successful, fallback to static value
3619 if ( !ok )
3621 }
3622
3623 hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation;
3624 if ( hasDataDefinedRotation )
3625 {
3626 // For non-point markers, "dataDefinedRotation" means following the
3627 // shape (shape-data defined). For them, "field-data defined" does
3628 // not work at all. TODO: if "field-data defined" ever gets implemented
3629 // we'll need a way to distinguish here between the two, possibly
3630 // using another flag in renderHints()
3631 const QgsFeature *f = context.feature();
3632 if ( f )
3633 {
3634 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3635 {
3636 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3637 angle += m2p.mapRotation();
3638 }
3639 }
3640 }
3641
3642 if ( angle )
3644}
3645
3646double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context )
3647{
3648 double scaledSize = mSize;
3649 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
3650
3651 bool ok = true;
3652 if ( hasDataDefinedSize )
3653 {
3656 }
3657
3658 if ( hasDataDefinedSize && ok )
3659 {
3660 switch ( mScaleMethod )
3661 {
3663 scaledSize = std::sqrt( scaledSize );
3664 break;
3666 break;
3667 }
3668 }
3669 return scaledSize;
3670}
3671
3673{
3674 QPainter *p = context.renderContext().painter();
3675 if ( !p || !mNonZeroFontSize )
3676 return;
3677
3678 QTransform transform;
3679
3680 bool ok;
3681 QColor brushColor = mColor;
3683 {
3686 }
3687 brushColor = context.selected() ? context.renderContext().selectionColor() : brushColor;
3688 if ( !context.selected() || !SELECTION_IS_OPAQUE )
3689 {
3690 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
3691 }
3692 mBrush.setColor( brushColor );
3693
3694 QColor penColor = mStrokeColor;
3696 {
3699 }
3700 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
3701
3702 double penWidth = context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3704 {
3705 context.setOriginalValueVariable( mStrokeWidth );
3707 if ( ok )
3708 {
3709 penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3710 }
3711 }
3712
3714 {
3717 if ( ok )
3718 {
3719 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
3720 }
3721 }
3722
3723 const QgsScopedQPainterState painterState( p );
3724 p->setBrush( mBrush );
3725 if ( !qgsDoubleNear( penWidth, 0.0 ) )
3726 {
3727 mPen.setColor( penColor );
3728 mPen.setWidthF( penWidth );
3729 p->setPen( mPen );
3730 }
3731 else
3732 {
3733 p->setPen( Qt::NoPen );
3734 }
3735
3737 {
3738 context.setOriginalValueVariable( mFontFamily );
3740 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( ok ? fontFamily : mFontFamily );
3741 mFont.setFamily( processedFamily );
3742 }
3744 {
3745 context.setOriginalValueVariable( mFontStyle );
3748 }
3750 {
3751 mFontMetrics.reset( new QFontMetrics( mFont ) );
3752 }
3753
3754 QPointF chrOffset = mChrOffset;
3755 double chrWidth;
3756 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3757
3758 const double sizeToRender = calculateSize( context );
3759
3760 bool hasDataDefinedRotation = false;
3761 QPointF offset;
3762 double angle = 0;
3763 calculateOffsetAndRotation( context, sizeToRender, hasDataDefinedRotation, offset, angle );
3764
3765 p->translate( point.x() + offset.x(), point.y() + offset.y() );
3766
3767 if ( !qgsDoubleNear( angle, 0.0 ) )
3768 transform.rotate( angle );
3769
3770 if ( !qgsDoubleNear( sizeToRender, mOrigSize ) )
3771 {
3772 const double s = sizeToRender / mOrigSize;
3773 transform.scale( s, s );
3774 }
3775
3776 if ( !qgsDoubleNear( mFontSizeScale, 1.0 ) )
3777 transform.scale( mFontSizeScale, mFontSizeScale );
3778
3779 if ( mUseCachedPath )
3780 {
3781 p->drawPath( transform.map( mCachedPath ) );
3782 }
3783 else
3784 {
3785 QPainterPath path;
3786 path.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3787 p->drawPath( transform.map( path ) );
3788 }
3789}
3790
3792{
3793 QVariantMap props;
3794 props[QStringLiteral( "font" )] = mFontFamily;
3795 props[QStringLiteral( "font_style" )] = mFontStyle;
3796 props[QStringLiteral( "chr" )] = mString;
3797 props[QStringLiteral( "size" )] = QString::number( mSize );
3798 props[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3799 props[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3800 props[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
3801 props[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
3802 props[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
3803 props[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
3804 props[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
3805 props[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
3806 props[QStringLiteral( "angle" )] = QString::number( mAngle );
3807 props[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3808 props[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3809 props[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3810 props[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3811 props[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3812 return props;
3813}
3814
3816{
3817 QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( mFontFamily, mString, mSize, mColor, mAngle );
3818 m->setFontStyle( mFontStyle );
3819 m->setStrokeColor( mStrokeColor );
3820 m->setStrokeWidth( mStrokeWidth );
3821 m->setStrokeWidthUnit( mStrokeWidthUnit );
3822 m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
3823 m->setPenJoinStyle( mPenJoinStyle );
3824 m->setOffset( mOffset );
3827 m->setSizeUnit( mSizeUnit );
3832 copyPaintEffect( m );
3833 return m;
3834}
3835
3836void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
3837{
3838 // <Graphic>
3839 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
3840 element.appendChild( graphicElem );
3841
3842 const QString fontPath = QStringLiteral( "ttf://%1" ).arg( mFontFamily );
3843 int markIndex = !mString.isEmpty() ? mString.at( 0 ).unicode() : 0;
3844 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
3845 QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, QStringLiteral( "ttf" ), &markIndex, mColor, size );
3846
3847 // <Rotation>
3848 QString angleFunc;
3849 bool ok;
3850 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
3851 if ( !ok )
3852 {
3853 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
3854 }
3855 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
3856 {
3857 angleFunc = QString::number( angle + mAngle );
3858 }
3859 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
3860
3861 // <Displacement>
3862 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
3864}
3865
3867{
3868 return mSizeUnit == Qgis::RenderUnit::MapUnits || mSizeUnit == Qgis::RenderUnit::MetersInMapUnits
3869 || mStrokeWidthUnit == Qgis::RenderUnit::MapUnits || mStrokeWidthUnit == Qgis::RenderUnit::MetersInMapUnits
3870 || mOffsetUnit == Qgis::RenderUnit::MapUnits || mOffsetUnit == Qgis::RenderUnit::MetersInMapUnits;
3871}
3872
3874{
3876 mStrokeWidthUnit = unit;
3877}
3878
3880{
3881 QPointF chrOffset = mChrOffset;
3882 double chrWidth = mChrWidth;
3883 //calculate width of rendered character
3884 ( void )characterToRender( context, chrOffset, chrWidth );
3885
3886 if ( !mFontMetrics )
3887 mFontMetrics.reset( new QFontMetrics( mFont ) );
3888
3889 double scaledSize = calculateSize( context );
3890 if ( !qgsDoubleNear( scaledSize, mOrigSize ) )
3891 {
3892 chrWidth *= scaledSize / mOrigSize;
3893 }
3894 chrWidth *= mFontSizeScale;
3895
3896 bool hasDataDefinedRotation = false;
3897 QPointF offset;
3898 double angle = 0;
3899 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
3900 scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3901
3902 QTransform transform;
3903
3904 // move to the desired position
3905 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
3906
3907 if ( !qgsDoubleNear( angle, 0.0 ) )
3908 transform.rotate( angle );
3909
3910 QRectF symbolBounds = transform.mapRect( QRectF( -chrWidth / 2.0,
3911 -scaledSize / 2.0,
3912 chrWidth,
3913 scaledSize ) );
3914 return symbolBounds;
3915}
3916
3918{
3919 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
3920
3921 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
3922 if ( graphicElem.isNull() )
3923 return nullptr;
3924
3925 QString name, format;
3926 QColor color;
3927 double size;
3928 int chr;
3929
3930 if ( !QgsSymbolLayerUtils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
3931 return nullptr;
3932
3933 if ( !name.startsWith( QLatin1String( "ttf://" ) ) || format != QLatin1String( "ttf" ) )
3934 return nullptr;
3935
3936 const QString fontFamily = name.mid( 6 );
3937
3938 double angle = 0.0;
3939 QString angleFunc;
3940 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
3941 {
3942 bool ok;
3943 const double d = angleFunc.toDouble( &ok );
3944 if ( ok )
3945 angle = d;
3946 }
3947
3948 QPointF offset;
3950
3951 double scaleFactor = 1.0;
3952 const QString uom = element.attribute( QStringLiteral( "uom" ) );
3953 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
3954 offset.setX( offset.x() * scaleFactor );
3955 offset.setY( offset.y() * scaleFactor );
3956 size = size * scaleFactor;
3957
3959 m->setOutputUnit( sldUnitSize );
3960 m->setAngle( angle );
3961 m->setOffset( offset );
3962 return m;
3963}
3964
3965void QgsFontMarkerSymbolLayer::resolveFonts( const QVariantMap &properties, const QgsReadWriteContext &context )
3966{
3967 const QString fontFamily = properties.value( QStringLiteral( "font" ), DEFAULT_FONTMARKER_FONT ).toString();
3968 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( fontFamily );
3969 QString matched;
3970 if ( !QgsFontUtils::fontFamilyMatchOnSystem( processedFamily )
3971 && !QgsApplication::fontManager()->tryToDownloadFontFamily( processedFamily, matched ) )
3972 {
3973 context.pushMessage( QObject::tr( "Font “%1” not available on system" ).arg( processedFamily ) );
3974 }
3975}
3976
3978{
3979 QMap<QString, QgsProperty>::iterator it = mParameters.begin();
3980 for ( ; it != mParameters.end(); ++it )
3981 it.value().prepare( context.renderContext().expressionContext() );
3982
3984}
3985
3986
3988{
3989 QSet<QString> attrs = QgsMarkerSymbolLayer::usedAttributes( context );
3990
3991 QMap<QString, QgsProperty>::const_iterator it = mParameters.constBegin();
3992 for ( ; it != mParameters.constEnd(); ++it )
3993 {
3994 attrs.unite( it.value().referencedFields( context.expressionContext(), true ) );
3995 }
3996
3997 return attrs;
3998}
3999
4000//
4001// QgsAnimatedMarkerSymbolLayer
4002//
4003
4005 : QgsRasterMarkerSymbolLayer( path, size, angle )
4006{
4007
4008}
4009
4011
4013{
4014 QString path;
4017
4018 if ( properties.contains( QStringLiteral( "imageFile" ) ) )
4019 path = properties[QStringLiteral( "imageFile" )].toString();
4020 if ( properties.contains( QStringLiteral( "size" ) ) )
4021 size = properties[QStringLiteral( "size" )].toDouble();
4022 if ( properties.contains( QStringLiteral( "angle" ) ) )
4023 angle = properties[QStringLiteral( "angle" )].toDouble();
4024
4025 std::unique_ptr< QgsAnimatedMarkerSymbolLayer > m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( path, size, angle );
4026 m->setFrameRate( properties.value( QStringLiteral( "frameRate" ), QStringLiteral( "10" ) ).toDouble() );
4027
4028 m->setCommonProperties( properties );
4029 return m.release();
4030}
4031
4033{
4034 return QStringLiteral( "AnimatedMarker" );
4035}
4036
4038{
4039 QVariantMap res = QgsRasterMarkerSymbolLayer::properties();
4040 res.insert( QStringLiteral( "frameRate" ), mFrameRateFps );
4041 return res;
4042}
4043
4045{
4046 std::unique_ptr< QgsAnimatedMarkerSymbolLayer > m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( mPath, mSize, mAngle );
4047 m->setFrameRate( mFrameRateFps );
4048 copyCommonProperties( m.get() );
4049 return m.release();
4050}
4051
4053{
4055
4056 mPreparedPaths.clear();
4058 {
4060 mStaticPath = true;
4061 }
4062 else
4063 {
4064 mStaticPath = false;
4065 }
4066}
4067
4068QImage QgsAnimatedMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
4069{
4070 if ( !mStaticPath && !mPreparedPaths.contains( path ) )
4071 {
4073 mPreparedPaths.insert( path );
4074 }
4075
4076 const long long mapFrameNumber = context.currentFrame();
4078 const double markerAnimationDuration = totalFrameCount / mFrameRateFps;
4079
4080 double animationTimeSeconds = 0;
4081 if ( mapFrameNumber >= 0 && context.frameRate() > 0 )
4082 {
4083 // render is part of an animation, so we base the calculated frame on that
4084 animationTimeSeconds = mapFrameNumber / context.frameRate();
4085 }
4086 else
4087 {
4088 // render is outside of animation, so base the calculated frame on the current epoch
4089 animationTimeSeconds = QDateTime::currentMSecsSinceEpoch() / 1000.0;
4090 }
4091
4092 const double markerAnimationProgressSeconds = std::fmod( animationTimeSeconds, markerAnimationDuration );
4093 const int movieFrame = static_cast< int >( std::floor( markerAnimationProgressSeconds * mFrameRateFps ) );
4094
4095 bool cached = false;
4096 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking, 96, movieFrame );
4097}
4098
@ DynamicRotation
Rotation of symbol may be changed during rendering and symbol should not be cached.
ScaleMethod
Scale methods.
Definition: qgis.h:354
@ ScaleDiameter
Calculate scale by the diameter.
@ ScaleArea
Calculate scale by the area.
MarkerShape
Marker shapes.
Definition: qgis.h:2067
@ Pentagon
Pentagon.
@ EquilateralTriangle
Equilateral triangle.
@ SemiCircle
Semi circle (top half)
@ QuarterCircle
Quarter circle (top left quarter)
@ LeftHalfTriangle
Left half of triangle.
@ ArrowHead
Right facing arrow head (unfilled, lines only)
@ ParallelogramRight
Parallelogram that slants right (since QGIS 3.28)
@ AsteriskFill
A filled asterisk shape (since QGIS 3.18)
@ Octagon
Octagon (since QGIS 3.18)
@ HalfArc
A line-only half arc (since QGIS 3.20)
@ Line
Vertical line.
@ QuarterSquare
Quarter square (top left quarter)
@ Triangle
Triangle.
@ Cross2
Rotated cross (lines only), 'x' shape.
@ Trapezoid
Trapezoid (since QGIS 3.28)
@ ArrowHeadFilled
Right facing filled arrow head.
@ Shield
A shape consisting of a triangle attached to a rectangle (since QGIS 3.28)
@ HalfSquare
Half square (left half)
@ CrossFill
Solid filled cross.
@ Decagon
Decagon (since QGIS 3.28)
@ RoundedSquare
A square with rounded corners (since QGIS 3.28)
@ RightHalfTriangle
Right half of triangle.
@ ThirdCircle
One third circle (top left third)
@ ThirdArc
A line-only one third arc (since QGIS 3.20)
@ SquareWithCorners
A square with diagonal corners (since QGIS 3.18)
@ QuarterArc
A line-only one quarter arc (since QGIS 3.20)
@ DiamondStar
A 4-sided star (since QGIS 3.28)
@ Cross
Cross (lines only)
@ ParallelogramLeft
Parallelogram that slants left (since QGIS 3.28)
@ Heart
Heart (since QGIS 3.28)
@ DiagonalHalfSquare
Diagonal half square (bottom left half)
RenderUnit
Rendering size units.
Definition: qgis.h:3441
@ RenderingSubSymbol
Set whenever a sub-symbol of a parent symbol is currently being rendered. Can be used during symbol a...
@ RenderSymbolPreview
The render is for a symbol preview only and map based properties may not be available,...
@ RenderBlocking
Render and load remote sources in the same thread to ensure rendering remote sources (svg and images)...
@ Fill
Fill symbol.
QColor valueAsColor(int key, const QgsExpressionContext &context, const QColor &defaultColor=QColor(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a color.
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double.
QString valueAsString(int key, const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a string.
Animated marker symbol layer class.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsAnimatedMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
~QgsAnimatedMarkerSymbolLayer() override
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
QImage fetchImage(QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity) const override
Fetches the image to render.
QgsAnimatedMarkerSymbolLayer(const QString &path=QString(), double size=DEFAULT_RASTERMARKER_SIZE, double angle=DEFAULT_RASTERMARKER_ANGLE)
Constructor for animated marker symbol layer using the specified source image path.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates an animated marker symbol layer from a string map of properties.
QString layerType() const override
Returns a string that represents this layer type.
static QgsImageCache * imageCache()
Returns the application's image cache, used for caching resampled versions of raster images.
static QgsSvgCache * svgCache()
Returns the application's SVG cache, used for caching SVG images and handling parameter replacement w...
static QgsFontManager * fontManager()
Returns the application font manager, which manages available fonts and font installation for the QGI...
Exports QGIS layers to the DXF format.
Definition: qgsdxfexport.h:65
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.
Definition: qgsdxfexport.h:222
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:56
QgsGeometry geometry
Definition: qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:230
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
Definition: qgsfillsymbol.h:30
static QgsFillSymbol * createSimple(const QVariantMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties.
Filled marker symbol layer, consisting of a shape which is rendered using a QgsFillSymbol.
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QColor color() const override
Returns the "representative" color of the symbol layer.
QgsFilledMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QgsSymbol * subSymbol() override
Returns the symbol's sub symbol, if present.
~QgsFilledMarkerSymbolLayer() override
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer's subsymbol. takes ownership of the passed symbol.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
void setColor(const QColor &c) override
Sets the "representative" color for the symbol layer.
QString layerType() const override
Returns a string that represents this layer type.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsFilledMarkerSymbolLayer.
QgsFilledMarkerSymbolLayer(Qgis::MarkerShape shape=Qgis::MarkerShape::Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructor for QgsFilledMarkerSymbolLayer.
QString processFontFamilyName(const QString &name) const
Processes a font family name, applying any matching fontFamilyReplacements() to the name.
void setStrokeWidthUnit(Qgis::RenderUnit unit)
Sets the stroke width unit.
~QgsFontMarkerSymbolLayer() override
void setStrokeColor(const QColor &color) override
Sets the stroke color for the symbol layer.
QgsFontMarkerSymbolLayer(const QString &fontFamily=DEFAULT_FONTMARKER_FONT, QString chr=DEFAULT_FONTMARKER_CHR, double pointSize=DEFAULT_FONTMARKER_SIZE, const QColor &color=DEFAULT_FONTMARKER_COLOR, double angle=DEFAULT_FONTMARKER_ANGLE)
Constructs a font marker symbol layer.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
Sets the stroke width map unit scale.
double strokeWidth() const
Returns the marker's stroke width.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
void setFontStyle(const QString &style)
Sets the font style for the font which will be used to render the point.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QString fontStyle() const
Returns the font style for the associated font which will be used to render the point.
QString fontFamily() const
Returns the font family name for the associated font which will be used to render the point.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
void writeSldMarker(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Writes the symbol layer definition as a SLD XML element.
void setStrokeWidth(double width)
Set's the marker's stroke width.
static void resolveFonts(const QVariantMap &properties, const QgsReadWriteContext &context)
Resolves fonts from a properties map, raising warnings in the specified context if the required fonts...
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
void setPenJoinStyle(Qt::PenJoinStyle style)
Sets the stroke join style.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsFontMarkerSymbolLayer from an SLD XML element.
QgsFontMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
QString layerType() const override
Returns a string that represents this layer type.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsFontMarkerSymbolLayer from a property map (see properties())
static QString translateNamedStyle(const QString &namedStyle)
Returns the localized named style of a font, if such a translation is available.
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.
Qgis::GeometryType type
Definition: qgsgeometry.h:167
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.
Definition: qgsmaptopixel.h:39
double mapUnitsPerPixel() const
Returns the current map units per pixel.
double mapRotation() const
Returns the current map rotation in degrees (clockwise).
Struct for storing maximum and minimum scales for measurements in map units.
Abstract base class for marker symbol layers.
double mSize
Marker size.
Qgis::RenderUnit mOffsetUnit
Offset units.
QPointF offset() const
Returns the marker's offset, which is the horizontal and vertical displacement which the rendered mar...
double mLineAngle
Line rotation angle (see setLineAngle() for details)
HorizontalAnchorPoint
Symbol horizontal anchor points.
void setOffsetUnit(Qgis::RenderUnit unit)
Sets the units for the symbol's offset.
void setAngle(double angle)
Sets the rotation angle for the marker.
Qgis::ScaleMethod scaleMethod() const
Returns the method to use for scaling the marker's size.
Qgis::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
void setVerticalAnchorPoint(VerticalAnchorPoint v)
Sets the vertical anchor point for positioning the symbol.
QPointF mOffset
Marker offset.
void setHorizontalAnchorPoint(HorizontalAnchorPoint h)
Sets the horizontal anchor point for positioning the symbol.
QgsMapUnitScale mapUnitScale() const override
void setOffset(QPointF offset)
Sets the marker's offset, which is the horizontal and vertical displacement which the rendered marker...
void setSizeMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol's size.
double size() const
Returns the symbol size.
QgsMapUnitScale mOffsetMapUnitScale
Offset map unit scale.
HorizontalAnchorPoint mHorizontalAnchorPoint
Horizontal anchor point.
static QPointF _rotatedOffset(QPointF offset, double angle)
Adjusts a marker offset to account for rotation.
Qgis::ScaleMethod mScaleMethod
Marker size scaling method.
QgsMapUnitScale mSizeMapUnitScale
Marker size map unit scale.
Qgis::RenderUnit mSizeUnit
Marker size unit.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units for the symbol's size.
VerticalAnchorPoint
Symbol vertical anchor points.
void markerOffset(QgsSymbolRenderContext &context, double &offsetX, double &offsetY) const
Calculates the required marker offset, including both the symbol offset and any displacement required...
VerticalAnchorPoint mVerticalAnchorPoint
Vertical anchor point.
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol's offset.
double mAngle
Marker rotation angle, in degrees clockwise from north.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
double angle() const
Returns the rotation angle for the marker, in degrees clockwise from north.
void setMapUnitScale(const QgsMapUnitScale &scale) override
Resolves relative paths into absolute paths and vice versa.
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
QgsProperty property(int key) const override
Returns a matching property from the collection, if one exists.
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const override
Returns the calculated value of the property with the specified key from within the collection.
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
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.
void setFixedAspectRatio(double ratio)
Set the marker aspect ratio between width and height to be used in rendering, if the value set is low...
void setMapUnitScale(const QgsMapUnitScale &scale) override
void setCommonProperties(const QVariantMap &properties)
Sets common class properties from a properties map.
QgsRasterMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
bool preservedAspectRatio() const
Returns the preserved aspect ratio value, true if fixed aspect ratio has been lower or equal to 0.
static void resolvePaths(QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving)
Turns relative paths in properties map to absolute when reading and vice versa when writing.
~QgsRasterMarkerSymbolLayer() override
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a raster marker symbol layer from a string map of properties.
double mOpacity
The marker default opacity.
double updateDefaultAspectRatio()
Calculates the default marker aspect ratio between width and height.
double mDefaultAspectRatio
The marker default aspect ratio.
bool setPreservedAspectRatio(bool par)
Set preserved the marker aspect ratio between width and height.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QString layerType() const override
Returns a string that represents this layer type.
double opacity() const
Returns the marker opacity.
The class is used as a container of context for various read/write operations on other objects.
void pushMessage(const QString &message, Qgis::MessageLevel level=Qgis::MessageLevel::Warning) const
Append a message to the context.
Contains information about the context of a rendering operation.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
double convertToPainterUnits(double size, Qgis::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QPainter * painter()
Returns the destination QPainter for the render operation.
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
QgsExpressionContext & expressionContext()
Gets the expression context.
bool forceVectorOutput() const
Returns true if rendering operations should use vector operations instead of any faster raster shortc...
long long currentFrame() const
Returns the current frame number of the map (in frames per second), for maps which are part of an ani...
float devicePixelRatio() const
Returns the device pixel ratio.
void setFlag(Qgis::RenderContextFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
double frameRate() const
Returns the frame rate of the map, for maps which are part of an animation.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
QColor selectionColor() const
Returns the color to use when rendering selected features.
Qgis::RenderContextFlags flags() const
Returns combination of flags used for rendering.
const QgsPathResolver & pathResolver() const
Returns the path resolver for conversion between relative and absolute paths during rendering operati...
Scoped object for saving and restoring a QPainter object's state.
Abstract base class for simple marker symbol layers.
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
void calculateOffsetAndRotation(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle) const
Calculates the marker offset and rotation.
Qgis::MarkerShape mShape
Symbol shape.
QPainterPath mPath
Painter path representing shape. If mPolygon is empty then the shape is stored in mPath.
bool shapeToPolygon(Qgis::MarkerShape shape, QPolygonF &polygon) const
Creates a polygon representing the specified shape.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
static QList< Qgis::MarkerShape > availableShapes()
Returns a list of all available shape types.
~QgsSimpleMarkerSymbolLayerBase() override
static bool shapeIsFilled(Qgis::MarkerShape shape)
Returns true if a symbol shape has a fill.
QPolygonF mPolygon
Polygon of points in shape. If polygon is empty then shape is using mPath.
Qgis::MarkerShape shape() const
Returns the shape for the rendered marker symbol.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
QgsSimpleMarkerSymbolLayerBase(Qgis::MarkerShape shape=Qgis::MarkerShape::Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructor for QgsSimpleMarkerSymbolLayerBase.
static QString encodeShape(Qgis::MarkerShape shape)
Encodes a shape to its string representation.
double calculateSize(QgsSymbolRenderContext &context, bool &hasDataDefinedSize) const
Calculates the desired size of the marker, considering data defined size overrides.
static Qgis::MarkerShape decodeShape(const QString &name, bool *ok=nullptr)
Attempts to decode a string representation of a shape name to the corresponding shape.
bool prepareMarkerPath(Qgis::MarkerShape symbol)
Prepares the layer for drawing the specified shape (QPainterPath version)
bool prepareMarkerShape(Qgis::MarkerShape shape)
Prepares the layer for drawing the specified shape (QPolygonF version)
Simple marker symbol layer, consisting of a rendered shape with solid fill color and an stroke.
QPen mSelPen
QPen to use as stroke of selected symbols.
void setColor(const QColor &color) override
Sets the "representative" color for the symbol layer.
QColor mStrokeColor
Stroke color.
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.
double mStrokeWidth
Stroke width.
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.
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.
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
Qgis::RenderUnit mStrokeWidthUnit
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 QColor decodeColor(const QString &str)
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.
@ PropertyStrokeStyle
Stroke style (eg solid, dashed)
@ PropertyCapStyle
Line cap style.
@ PropertyAngle
Symbol angle.
@ PropertySize
Symbol size.
@ PropertyJoinStyle
Line join style.
@ PropertyOpacity
Opacity.
@ PropertyCharacter
Character, eg for font marker symbol layers.
@ PropertyOffset
Symbol offset.
@ PropertyStrokeWidth
Stroke width.
@ PropertyFillColor
Fill color.
@ PropertyFontStyle
Font style.
@ PropertyHeight
Symbol height.
@ PropertyFontFamily
Font family.
@ PropertyName
Name, eg shape name for simple markers.
@ PropertyStrokeColor
Stroke color.
@ PropertyWidth
Symbol width.
static const bool SELECTION_IS_OPAQUE
Whether styles for selected features ignore symbol alpha.
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns the set of attributes referenced by the layer.
void copyDataDefinedProperties(QgsSymbolLayer *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
void restoreOldDataDefinedProperties(const QVariantMap &stringMap)
Restores older data defined properties from string map.
virtual void prepareExpressions(const QgsSymbolRenderContext &context)
Prepares all data defined property expressions for evaluation.
virtual void setColor(const QColor &color)
Sets the "representative" color for the symbol layer.
virtual QColor color() const
Returns the "representative" color of the symbol layer.
void copyPaintEffect(QgsSymbolLayer *destLayer) const
Copies paint effect of this layer to another symbol layer.
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.
bool selected() const
Returns true if symbols should be rendered using the selected symbol coloring and style.
Qgis::SymbolRenderHints renderHints() const
Returns the rendering hint flags for the symbol.
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for data defined symbology.
qreal opacity() const
Returns the opacity for the symbol.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:94
Qgis::SymbolType type() const
Returns the symbol's type.
Definition: qgssymbol.h:153
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)
Returns true if the specified variant should be considered a NULL value.
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786
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:3988
QMap< QString, QString > QgsStringMap
Definition: qgis.h:4533
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
#define DEG2RAD(x)
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugError(str)
Definition: qgslogger.h:38
Q_GUI_EXPORT int qt_defaultDpiX()
Q_GUI_EXPORT int qt_defaultDpiY()
#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