QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
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 if ( mSizeUnit == Qgis::RenderUnit::MetersInMapUnits && context.renderContext().flags() & Qgis::RenderContextFlag::RenderSymbolPreview )
1161 {
1162 // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
1163 // and clamp it to a reasonable range. It's the best we can do in this situation!
1164 scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
1165 }
1166
1167 // take into account angle (which is not data-defined otherwise cache wouldn't be used)
1168 if ( !qgsDoubleNear( mAngle, 0.0 ) )
1169 {
1170 scaledSize = ( std::abs( std::sin( mAngle * M_PI / 180 ) ) + std::abs( std::cos( mAngle * M_PI / 180 ) ) ) * scaledSize;
1171 }
1172 // calculate necessary image size for the cache
1173 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
1174 const int imageSize = ( static_cast< int >( scaledSize ) + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width
1175 const double center = imageSize / 2.0;
1176 if ( imageSize > MAXIMUM_CACHE_WIDTH )
1177 {
1178 return false;
1179 }
1180
1181 mCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
1182 mCache.fill( 0 );
1183
1184 const bool needsBrush = shapeIsFilled( mShape );
1185
1186 QPainter p;
1187 p.begin( &mCache );
1188 p.setRenderHint( QPainter::Antialiasing );
1189 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1190 p.setPen( mPen );
1191 p.translate( QPointF( center, center ) );
1192 drawMarker( &p, context );
1193 p.end();
1194
1195 // Construct the selected version of the Cache
1196
1197 const QColor selColor = context.renderContext().selectionColor();
1198
1199 mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
1200 mSelCache.fill( 0 );
1201
1202 p.begin( &mSelCache );
1203 p.setRenderHint( QPainter::Antialiasing );
1204 p.setBrush( needsBrush ? mSelBrush : Qt::NoBrush );
1205 p.setPen( mSelPen );
1206 p.translate( QPointF( center, center ) );
1207 drawMarker( &p, context );
1208 p.end();
1209
1210 // Check that the selected version is different. If not, then re-render,
1211 // filling the background with the selection color and using the normal
1212 // colors for the symbol .. could be ugly!
1213
1214 if ( mSelCache == mCache )
1215 {
1216 p.begin( &mSelCache );
1217 p.setRenderHint( QPainter::Antialiasing );
1218 p.fillRect( 0, 0, imageSize, imageSize, selColor );
1219 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1220 p.setPen( mPen );
1221 p.translate( QPointF( center, center ) );
1222 drawMarker( &p, context );
1223 p.end();
1224 }
1225
1226 return true;
1227}
1228
1229void QgsSimpleMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
1230{
1231 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1232 //of the rendered point!
1233
1234 QPainter *p = context.renderContext().painter();
1235 if ( !p )
1236 {
1237 return;
1238 }
1239
1240 QColor brushColor = mColor;
1241 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
1242 mBrush.setColor( brushColor );
1243
1244 QColor penColor = mStrokeColor;
1245 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
1246 mPen.setColor( penColor );
1247
1248 bool ok = true;
1250 {
1253 if ( ok )
1254 {
1255 c.setAlphaF( c.alphaF() * context.opacity() );
1256 mBrush.setColor( c );
1257 }
1258 }
1260 {
1263 if ( ok )
1264 {
1265 c.setAlphaF( c.alphaF() * context.opacity() );
1266 mPen.setColor( c );
1267 mSelPen.setColor( c );
1268 }
1269 }
1271 {
1274 if ( ok )
1275 {
1278 }
1279 }
1281 {
1284 if ( ok )
1285 {
1288 }
1289 }
1291 {
1294 if ( ok )
1295 {
1296 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1297 mSelPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1298 }
1299 }
1301 {
1303 const QString style = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyCapStyle, context.renderContext().expressionContext(), QString(), &ok );
1304 if ( ok )
1305 {
1306 mPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1307 mSelPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1308 }
1309 }
1310
1311 if ( shapeIsFilled( shape ) )
1312 {
1313 p->setBrush( context.selected() ? mSelBrush : mBrush );
1314 }
1315 else
1316 {
1317 p->setBrush( Qt::NoBrush );
1318 }
1319 p->setPen( context.selected() ? mSelPen : mPen );
1320
1321 if ( !polygon.isEmpty() )
1322 p->drawPolygon( polygon );
1323 else
1324 p->drawPath( path );
1325}
1326
1328{
1329 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1330 //of the rendered point!
1331
1332 QPainter *p = context.renderContext().painter();
1333 if ( !p )
1334 {
1335 return;
1336 }
1337
1338 if ( mUsingCache && qgsDoubleNear( mCachedOpacity, context.opacity() ) )
1339 {
1340 const QImage &img = context.selected() ? mSelCache : mCache;
1341 const double s = img.width();
1342
1343 bool hasDataDefinedSize = false;
1344 const double scaledSize = calculateSize( context, hasDataDefinedSize );
1345
1346 bool hasDataDefinedRotation = false;
1347 QPointF offset;
1348 double angle = 0;
1349 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
1350
1351 p->drawImage( QRectF( point.x() - s / 2.0 + offset.x(),
1352 point.y() - s / 2.0 + offset.y(),
1353 s, s ), img );
1354 }
1355 else
1356 {
1358 }
1359}
1360
1362{
1363 QVariantMap map;
1364 map[QStringLiteral( "name" )] = encodeShape( mShape );
1365 map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
1366 map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
1367 map[QStringLiteral( "size" )] = QString::number( mSize );
1368 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1369 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1370 map[QStringLiteral( "angle" )] = QString::number( mAngle );
1371 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1372 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1373 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1374 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1375 map[QStringLiteral( "outline_style" )] = QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle );
1376 map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
1377 map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
1378 map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
1379 map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
1380 map[QStringLiteral( "cap_style" )] = QgsSymbolLayerUtils::encodePenCapStyle( mPenCapStyle );
1381 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1382 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1383 return map;
1384}
1385
1387{
1389 m->setOffset( mOffset );
1390 m->setSizeUnit( mSizeUnit );
1402 copyPaintEffect( m );
1403 return m;
1404}
1405
1406void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1407{
1408 // <Graphic>
1409 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
1410 element.appendChild( graphicElem );
1411
1413 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
1415
1416 // <Rotation>
1417 QString angleFunc;
1418
1419 if ( mDataDefinedProperties.isActive( QgsSymbolLayer::Property::PropertyAngle ) )
1420 {
1421 angleFunc = mDataDefinedProperties.property( QgsSymbolLayer::Property::PropertyAngle ).asExpression();
1422 }
1423 else
1424 {
1425 bool ok;
1426 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
1427 if ( !ok )
1428 {
1429 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
1430 }
1431 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
1432 {
1433 angleFunc = QString::number( angle + mAngle );
1434 }
1435 }
1436
1437 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
1438
1439 // <Displacement>
1440 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
1442}
1443
1444QString QgsSimpleMarkerSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
1445{
1446 Q_UNUSED( mmScaleFactor )
1447 Q_UNUSED( mapUnitScaleFactor )
1448#if 0
1449 QString ogrType = "3"; //default is circle
1450 if ( mName == "square" )
1451 {
1452 ogrType = "5";
1453 }
1454 else if ( mName == "triangle" )
1455 {
1456 ogrType = "7";
1457 }
1458 else if ( mName == "star" )
1459 {
1460 ogrType = "9";
1461 }
1462 else if ( mName == "circle" )
1463 {
1464 ogrType = "3";
1465 }
1466 else if ( mName == "cross" )
1467 {
1468 ogrType = "0";
1469 }
1470 else if ( mName == "x" || mName == "cross2" )
1471 {
1472 ogrType = "1";
1473 }
1474 else if ( mName == "line" )
1475 {
1476 ogrType = "10";
1477 }
1478
1479 QString ogrString;
1480 ogrString.append( "SYMBOL(" );
1481 ogrString.append( "id:" );
1482 ogrString.append( '\"' );
1483 ogrString.append( "ogr-sym-" );
1484 ogrString.append( ogrType );
1485 ogrString.append( '\"' );
1486 ogrString.append( ",c:" );
1487 ogrString.append( mColor.name() );
1488 ogrString.append( ",o:" );
1489 ogrString.append( mStrokeColor.name() );
1490 ogrString.append( QString( ",s:%1mm" ).arg( mSize ) );
1491 ogrString.append( ')' );
1492 return ogrString;
1493#endif //0
1494
1495 QString ogrString;
1496 ogrString.append( "PEN(" );
1497 ogrString.append( "c:" );
1498 ogrString.append( mColor.name() );
1499 ogrString.append( ",w:" );
1500 ogrString.append( QString::number( mSize ) );
1501 ogrString.append( "mm" );
1502 ogrString.append( ")" );
1503 return ogrString;
1504}
1505
1507{
1508 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
1509
1510 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
1511 if ( graphicElem.isNull() )
1512 return nullptr;
1513
1514 QString name = QStringLiteral( "square" );
1515 QColor color, strokeColor;
1516 double strokeWidth, size;
1517 Qt::PenStyle strokeStyle;
1518
1520 return nullptr;
1521
1522 double angle = 0.0;
1523 QString angleFunc;
1524 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
1525 {
1526 bool ok;
1527 const double d = angleFunc.toDouble( &ok );
1528 if ( ok )
1529 angle = d;
1530 }
1531
1532 QPointF offset;
1534
1535 const Qgis::MarkerShape shape = decodeShape( name );
1536
1537 double scaleFactor = 1.0;
1538 const QString uom = element.attribute( QStringLiteral( "uom" ) );
1539 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
1540 size = size * scaleFactor;
1541 offset.setX( offset.x() * scaleFactor );
1542 offset.setY( offset.y() * scaleFactor );
1543
1545 m->setOutputUnit( sldUnitSize );
1546 m->setColor( color );
1548 m->setAngle( angle );
1549 m->setOffset( offset );
1552 return m;
1553}
1554
1556{
1557 Q_UNUSED( context )
1558
1559 if ( mPolygon.count() != 0 )
1560 {
1561 p->drawPolygon( mPolygon );
1562 }
1563 else
1564 {
1565 p->drawPath( mPath );
1566 }
1567}
1568
1569bool QgsSimpleMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
1570{
1571 //data defined size?
1572 double size = mSize;
1573
1574 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
1575
1576 //data defined size
1577 bool ok = true;
1578 if ( hasDataDefinedSize )
1579 {
1581
1582 if ( ok )
1583 {
1584 switch ( mScaleMethod )
1585 {
1587 size = std::sqrt( size );
1588 break;
1590 break;
1591 }
1592 }
1593
1595 }
1596
1597 if ( mSizeUnit == Qgis::RenderUnit::Millimeters )
1598 {
1599 size *= mmMapUnitScaleFactor;
1600 }
1601
1602 if ( mSizeUnit == Qgis::RenderUnit::MapUnits )
1603 {
1605 }
1606 const double halfSize = size / 2.0;
1607
1608 //strokeWidth
1609 double strokeWidth = mStrokeWidth;
1610
1612 {
1615 }
1617 if ( mSizeUnit == Qgis::RenderUnit::MapUnits )
1618 {
1620 }
1621
1622 //color
1623 QColor pc = mPen.color();
1624 QColor bc = mBrush.color();
1626 {
1629 }
1631 {
1634 }
1635
1636 //offset
1637 double offsetX = 0;
1638 double offsetY = 0;
1639 markerOffset( context, offsetX, offsetY );
1640 offsetX *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1641 offsetY *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1642
1643
1644 QPointF off( offsetX, offsetY );
1645
1646 //angle
1647 double angle = mAngle + mLineAngle;
1649 {
1652 }
1653
1656 {
1658 const QString shapeName = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyName, context.renderContext().expressionContext(), QString(), &ok );
1659 if ( ok )
1660 {
1661 shape = decodeShape( shapeName, &ok );
1662 if ( !ok )
1663 shape = mShape;
1664 }
1665 }
1666
1667 if ( angle )
1668 off = _rotatedOffset( off, angle );
1669
1671
1672 QTransform t;
1673 t.translate( shift.x() + off.x(), shift.y() - off.y() );
1674
1675 if ( !qgsDoubleNear( angle, 0.0 ) )
1676 t.rotate( -angle );
1677
1678 QPolygonF polygon;
1679 if ( shapeToPolygon( shape, polygon ) )
1680 {
1681 t.scale( halfSize, -halfSize );
1682
1683 polygon = t.map( polygon );
1684
1686 p.reserve( polygon.size() );
1687 for ( int i = 0; i < polygon.size(); i++ )
1688 {
1689 p << QgsPoint( polygon[i] );
1690 }
1691
1692 if ( mBrush.style() != Qt::NoBrush )
1693 e.writePolygon( QgsRingSequence() << p, layerName, QStringLiteral( "SOLID" ), bc );
1694 if ( mPen.style() != Qt::NoPen )
1695 e.writePolyline( p, layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1696 }
1697 else if ( shape == Qgis::MarkerShape::Circle )
1698 {
1699 shift += QPointF( off.x(), -off.y() );
1700 if ( mBrush.style() != Qt::NoBrush )
1701 e.writeFilledCircle( layerName, bc, QgsPoint( shift ), halfSize );
1702 if ( mPen.style() != Qt::NoPen )
1703 e.writeCircle( layerName, pc, QgsPoint( shift ), halfSize, QStringLiteral( "CONTINUOUS" ), strokeWidth );
1704 }
1705 else if ( shape == Qgis::MarkerShape::Line )
1706 {
1707 const QPointF pt1 = t.map( QPointF( 0, -halfSize ) );
1708 const QPointF pt2 = t.map( QPointF( 0, halfSize ) );
1709
1710 if ( mPen.style() != Qt::NoPen )
1711 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1712 }
1713 else if ( shape == Qgis::MarkerShape::Cross )
1714 {
1715 if ( mPen.style() != Qt::NoPen )
1716 {
1717 const QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
1718 const QPointF pt2 = t.map( QPointF( halfSize, 0 ) );
1719 const QPointF pt3 = t.map( QPointF( 0, -halfSize ) );
1720 const QPointF pt4 = t.map( QPointF( 0, halfSize ) );
1721
1722 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1723 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1724 }
1725 }
1726 else if ( shape == Qgis::MarkerShape::Cross2 )
1727 {
1728 if ( mPen.style() != Qt::NoPen )
1729 {
1730 const QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
1731 const QPointF pt2 = t.map( QPointF( halfSize, halfSize ) );
1732 const QPointF pt3 = t.map( QPointF( halfSize, -halfSize ) );
1733 const QPointF pt4 = t.map( QPointF( -halfSize, halfSize ) );
1734
1735 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1736 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1737 }
1738 }
1739 else if ( shape == Qgis::MarkerShape::ArrowHead )
1740 {
1741 if ( mPen.style() != Qt::NoPen )
1742 {
1743 const QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
1744 const QPointF pt2 = t.map( QPointF( 0, 0 ) );
1745 const QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
1746
1747 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1748 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1749 }
1750 }
1751 else
1752 {
1753 QgsDebugMsg( QStringLiteral( "Unsupported dxf marker name %1" ).arg( encodeShape( shape ) ) );
1754 return false;
1755 }
1756
1757 return true;
1758}
1759
1760
1762{
1764 mStrokeWidthUnit = unit;
1765}
1766
1768{
1770 {
1771 return mStrokeWidthUnit;
1772 }
1773 return Qgis::RenderUnit::Unknown;
1774}
1775
1777{
1780}
1781
1783{
1785 {
1787 }
1788 return QgsMapUnitScale();
1789}
1790
1792{
1793 return mSizeUnit == Qgis::RenderUnit::MapUnits || mSizeUnit == Qgis::RenderUnit::MetersInMapUnits
1794 || mOffsetUnit == Qgis::RenderUnit::MapUnits || mOffsetUnit == Qgis::RenderUnit::MetersInMapUnits
1795 || mStrokeWidthUnit == Qgis::RenderUnit::MapUnits || mStrokeWidthUnit == Qgis::RenderUnit::MetersInMapUnits;
1796}
1797
1799{
1800 QRectF symbolBounds = QgsSimpleMarkerSymbolLayerBase::bounds( point, context );
1801
1802 // need to account for stroke width
1803 double penWidth = mStrokeWidth;
1804 bool ok = true;
1806 {
1809 if ( ok )
1810 {
1811 penWidth = strokeWidth;
1812 }
1813 }
1816 {
1819 if ( ok && strokeStyle == QLatin1String( "no" ) )
1820 {
1821 penWidth = 0.0;
1822 }
1823 }
1824 else if ( mStrokeStyle == Qt::NoPen )
1825 penWidth = 0;
1826
1827 //antialiasing, add 1 pixel
1828 penWidth += 1;
1829
1830 //extend bounds by pen width / 2.0
1831 symbolBounds.adjust( -penWidth / 2.0, -penWidth / 2.0,
1832 penWidth / 2.0, penWidth / 2.0 );
1833
1834 return symbolBounds;
1835}
1836
1837void QgsSimpleMarkerSymbolLayer::setColor( const QColor &color )
1838{
1839 if ( shapeIsFilled( mShape ) )
1840 {
1842 }
1843 else
1844 {
1846 }
1847}
1848
1850{
1851 if ( shapeIsFilled( mShape ) )
1852 {
1853 return fillColor();
1854 }
1855 else
1856 {
1857 return strokeColor();
1858 }
1859}
1860
1861
1862
1863
1864//
1865// QgsFilledMarkerSymbolLayer
1866//
1867
1869 : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
1870{
1871 mFill.reset( static_cast<QgsFillSymbol *>( QgsFillSymbol::createSimple( QVariantMap() ) ) );
1872}
1873
1875
1877{
1878 QString name = DEFAULT_SIMPLEMARKER_NAME;
1882
1883 if ( props.contains( QStringLiteral( "name" ) ) )
1884 name = props[QStringLiteral( "name" )].toString();
1885 if ( props.contains( QStringLiteral( "size" ) ) )
1886 size = props[QStringLiteral( "size" )].toDouble();
1887 if ( props.contains( QStringLiteral( "angle" ) ) )
1888 angle = props[QStringLiteral( "angle" )].toDouble();
1889 if ( props.contains( QStringLiteral( "scale_method" ) ) )
1890 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1891
1893 if ( props.contains( QStringLiteral( "offset" ) ) )
1894 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1895 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1896 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1897 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1898 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1899 if ( props.contains( QStringLiteral( "size_unit" ) ) )
1900 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1901 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1902 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1903 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1904 {
1905 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1906 }
1907 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1908 {
1909 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1910 }
1911
1913
1915
1916 return m;
1917}
1918
1920{
1921 return QStringLiteral( "FilledMarker" );
1922}
1923
1925{
1926 if ( mFill )
1927 {
1928 mFill->startRender( context.renderContext(), context.fields() );
1929 }
1930
1932}
1933
1935{
1936 if ( mFill )
1937 {
1938 mFill->stopRender( context.renderContext() );
1939 }
1940}
1941
1943{
1944 QVariantMap map;
1945 map[QStringLiteral( "name" )] = encodeShape( mShape );
1946 map[QStringLiteral( "size" )] = QString::number( mSize );
1947 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1948 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1949 map[QStringLiteral( "angle" )] = QString::number( mAngle );
1950 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1951 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1952 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1953 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1954 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1955 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1956
1957 if ( mFill )
1958 {
1959 map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mFill->color() );
1960 }
1961 return map;
1962}
1963
1965{
1967 copyPaintEffect( m );
1969 m->setSubSymbol( mFill->clone() );
1970 return m;
1971}
1972
1974{
1975 return mFill.get();
1976}
1977
1979{
1980 if ( symbol && symbol->type() == Qgis::SymbolType::Fill )
1981 {
1982 mFill.reset( static_cast<QgsFillSymbol *>( symbol ) );
1983 return true;
1984 }
1985 else
1986 {
1987 delete symbol;
1988 return false;
1989 }
1990}
1991
1993{
1994 if ( mFill )
1995 {
1996 return QgsSymbolLayerUtils::estimateMaxSymbolBleed( mFill.get(), context );
1997 }
1998 return 0;
1999}
2000
2002{
2003 QSet<QString> attr = QgsSimpleMarkerSymbolLayerBase::usedAttributes( context );
2004 if ( mFill )
2005 attr.unite( mFill->usedAttributes( context ) );
2006 return attr;
2007}
2008
2010{
2012 return true;
2013 if ( mFill && mFill->hasDataDefinedProperties() )
2014 return true;
2015 return false;
2016}
2017
2019{
2020 mColor = c;
2021 if ( mFill )
2022 mFill->setColor( c );
2023}
2024
2026{
2027 return mFill ? mFill->color() : mColor;
2028}
2029
2031{
2032 return mSizeUnit == Qgis::RenderUnit::MapUnits || mSizeUnit == Qgis::RenderUnit::MetersInMapUnits
2033 || mOffsetUnit == Qgis::RenderUnit::MapUnits || mOffsetUnit == Qgis::RenderUnit::MetersInMapUnits
2034 || ( mFill && mFill->usesMapUnits() );
2035}
2036
2038{
2040 if ( mFill )
2041 mFill->setOutputUnit( unit );
2042}
2043
2044void QgsFilledMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
2045{
2046 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
2047 //of the rendered point!
2048
2049 QPainter *p = context.renderContext().painter();
2050 if ( !p )
2051 {
2052 return;
2053 }
2054
2055 const double prevOpacity = mFill->opacity();
2056 mFill->setOpacity( mFill->opacity() * context.opacity() );
2057
2058 if ( shapeIsFilled( shape ) )
2059 {
2060 p->setBrush( Qt::red );
2061 }
2062 else
2063 {
2064 p->setBrush( Qt::NoBrush );
2065 }
2066 p->setPen( Qt::black );
2067
2068 const bool prevIsSubsymbol = context.renderContext().flags() & Qgis::RenderContextFlag::RenderingSubSymbol;
2070
2071 if ( !polygon.isEmpty() )
2072 {
2073 mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
2074 }
2075 else
2076 {
2077 const QPolygonF poly = path.toFillPolygon();
2078 mFill->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
2079 }
2080
2082
2083 mFill->setOpacity( prevOpacity );
2084}
2085
2086
2088
2089
2090QgsSvgMarkerSymbolLayer::QgsSvgMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
2091{
2092 mSize = size;
2093 mAngle = angle;
2094 mOffset = QPointF( 0, 0 );
2096 mStrokeWidth = 0.2;
2097 mStrokeWidthUnit = Qgis::RenderUnit::Millimeters;
2098 mColor = QColor( 35, 35, 35 );
2099 mStrokeColor = QColor( 35, 35, 35 );
2100 setPath( path );
2101}
2102
2104
2106{
2107 QString name;
2111
2112 if ( props.contains( QStringLiteral( "name" ) ) )
2113 name = props[QStringLiteral( "name" )].toString();
2114 if ( props.contains( QStringLiteral( "size" ) ) )
2115 size = props[QStringLiteral( "size" )].toDouble();
2116 if ( props.contains( QStringLiteral( "angle" ) ) )
2117 angle = props[QStringLiteral( "angle" )].toDouble();
2118 if ( props.contains( QStringLiteral( "scale_method" ) ) )
2119 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
2120
2122
2123 if ( props.contains( QStringLiteral( "size_unit" ) ) )
2124 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
2125 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
2126 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
2127 if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
2128 m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
2129 if ( props.contains( QStringLiteral( "offset" ) ) )
2130 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
2131 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
2132 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
2133 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2134 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
2135 if ( props.contains( QStringLiteral( "fill" ) ) )
2136 {
2137 //pre 2.5 projects used "fill"
2138 m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "fill" )].toString() ) );
2139 }
2140 else if ( props.contains( QStringLiteral( "color" ) ) )
2141 {
2142 m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )].toString() ) );
2143 }
2144 if ( props.contains( QStringLiteral( "outline" ) ) )
2145 {
2146 //pre 2.5 projects used "outline"
2147 m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline" )].toString() ) );
2148 }
2149 else if ( props.contains( QStringLiteral( "outline_color" ) ) )
2150 {
2151 m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )].toString() ) );
2152 }
2153 else if ( props.contains( QStringLiteral( "line_color" ) ) )
2154 {
2155 m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )].toString() ) );
2156 }
2157
2158 if ( props.contains( QStringLiteral( "outline-width" ) ) )
2159 {
2160 //pre 2.5 projects used "outline-width"
2161 m->setStrokeWidth( props[QStringLiteral( "outline-width" )].toDouble() );
2162 }
2163 else if ( props.contains( QStringLiteral( "outline_width" ) ) )
2164 {
2165 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
2166 }
2167 else if ( props.contains( QStringLiteral( "line_width" ) ) )
2168 {
2169 m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
2170 }
2171
2172 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
2173 {
2174 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
2175 }
2176 else if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
2177 {
2178 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
2179 }
2180 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
2181 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
2182
2183 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2184 {
2185 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2186 }
2187 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2188 {
2189 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2190 }
2191
2193
2195
2196 if ( props.contains( QStringLiteral( "parameters" ) ) )
2197 {
2198 const QVariantMap parameters = props[QStringLiteral( "parameters" )].toMap();
2200 }
2201
2202 return m;
2203}
2204
2205void QgsSvgMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
2206{
2207 const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
2208 if ( it != properties.end() )
2209 {
2210 if ( saving )
2211 {
2212 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
2213 }
2214 else
2215 {
2216 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
2217 }
2218 }
2219}
2220
2221void QgsSvgMarkerSymbolLayer::setPath( const QString &path )
2222{
2224 mHasFillParam = false;
2225 mPath = path;
2226 QColor defaultFillColor, defaultStrokeColor;
2227 double strokeWidth, fillOpacity, strokeOpacity;
2228 bool hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
2229 bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
2230 QgsApplication::svgCache()->containsParams( path, mHasFillParam, hasDefaultFillColor, defaultFillColor,
2231 hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
2232 hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2233 hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
2234 hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
2235
2236 const double newFillOpacity = hasFillOpacityParam ? fillColor().alphaF() : 1.0;
2237 const double newStrokeOpacity = hasStrokeOpacityParam ? strokeColor().alphaF() : 1.0;
2238
2239 if ( hasDefaultFillColor )
2240 {
2241 defaultFillColor.setAlphaF( newFillOpacity );
2242 setFillColor( defaultFillColor );
2243 }
2244 if ( hasDefaultFillOpacity )
2245 {
2246 QColor c = fillColor();
2247 c.setAlphaF( fillOpacity );
2248 setFillColor( c );
2249 }
2250 if ( hasDefaultStrokeColor )
2251 {
2252 defaultStrokeColor.setAlphaF( newStrokeOpacity );
2253 setStrokeColor( defaultStrokeColor );
2254 }
2255 if ( hasDefaultStrokeWidth )
2256 {
2258 }
2259 if ( hasDefaultStrokeOpacity )
2260 {
2261 QColor c = strokeColor();
2262 c.setAlphaF( strokeOpacity );
2263 setStrokeColor( c );
2264 }
2265
2267}
2268
2270{
2271 if ( mDefaultAspectRatio == 0.0 )
2272 {
2273 //size
2274 const double size = mSize;
2275 //assume 88 dpi as standard value
2276 const double widthScaleFactor = 3.465;
2277 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( mPath, size, mColor, mStrokeColor, mStrokeWidth, widthScaleFactor );
2278 // set default aspect ratio
2279 mDefaultAspectRatio = svgViewbox.isValid() ? svgViewbox.height() / svgViewbox.width() : 0.0;
2280 }
2281 return mDefaultAspectRatio;
2282}
2283
2285{
2286 const bool aPreservedAspectRatio = preservedAspectRatio();
2287 if ( aPreservedAspectRatio && !par )
2288 {
2290 }
2291 else if ( !aPreservedAspectRatio && par )
2292 {
2293 mFixedAspectRatio = 0.0;
2294 }
2295 return preservedAspectRatio();
2296}
2297
2298void QgsSvgMarkerSymbolLayer::setParameters( const QMap<QString, QgsProperty> &parameters )
2299{
2301}
2302
2303
2305{
2306 return QStringLiteral( "SvgMarker" );
2307}
2308
2310{
2311 QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
2312 Q_UNUSED( context )
2313}
2314
2316{
2317 Q_UNUSED( context )
2318}
2319
2321{
2322 QPainter *p = context.renderContext().painter();
2323 if ( !p )
2324 return;
2325
2326 bool hasDataDefinedSize = false;
2327 const double scaledWidth = calculateSize( context, hasDataDefinedSize );
2328 const double width = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2329
2330 //don't render symbols with a width below one or above 10,000 pixels
2331 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2332 {
2333 return;
2334 }
2335
2336 const QgsScopedQPainterState painterState( p );
2337
2338 bool hasDataDefinedAspectRatio = false;
2339 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2340 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2341
2343
2344 double strokeWidth = mStrokeWidth;
2346 {
2349 }
2351
2352 QColor fillColor = mColor;
2353 if ( context.selected() && mHasFillParam )
2354 {
2356 }
2358 {
2361 }
2362
2363 QColor strokeColor = mStrokeColor;
2365 {
2368 }
2369
2370 QString path = mPath;
2372 {
2375 context.renderContext().pathResolver() );
2377 {
2378 // adjust height of data defined path
2379 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2380 context.renderContext().scaleFactor(), aspectRatio,
2381 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2382 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2383 }
2384 }
2385
2386 QPointF outputOffset;
2387 double angle = 0.0;
2388 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2389
2390 p->translate( point + outputOffset );
2391
2392 const bool rotated = !qgsDoubleNear( angle, 0 );
2393 if ( rotated )
2394 p->rotate( angle );
2395
2396 bool fitsInCache = true;
2397 bool usePict = true;
2398 const bool rasterizeSelected = !mHasFillParam || mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyName );
2399 if ( ( !context.renderContext().forceVectorOutput() && !rotated ) || ( context.selected() && rasterizeSelected ) )
2400 {
2402 context.renderContext().scaleFactor(), fitsInCache, aspectRatio,
2403 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2404 if ( fitsInCache && img.width() > 1 )
2405 {
2406 usePict = false;
2407
2408 if ( context.selected() )
2409 {
2411 }
2412
2413 //consider transparency
2414 if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2415 {
2416 QImage transparentImage = img.copy();
2417 QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2418 p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
2419 }
2420 else
2421 {
2422 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2423 }
2424 }
2425 }
2426
2427 if ( usePict || !fitsInCache )
2428 {
2429 p->setOpacity( context.opacity() );
2431 context.renderContext().scaleFactor(), context.renderContext().forceVectorOutput(), aspectRatio,
2432 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2433 if ( pct.width() > 1 )
2434 {
2435 const QgsScopedQPainterState painterPictureState( p );
2436 _fixQPictureDPI( p );
2437 p->drawPicture( 0, 0, pct );
2438 }
2439 }
2440
2441 // workaround issue with nested QPictures forgetting antialiasing flag - see https://github.com/qgis/QGIS/issues/22909
2443}
2444
2445double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2446{
2447 double scaledSize = mSize;
2449
2450 bool ok = true;
2451 if ( hasDataDefinedSize )
2452 {
2455 }
2456 else
2457 {
2459 if ( hasDataDefinedSize )
2460 {
2463 }
2464 }
2465
2466 if ( hasDataDefinedSize && ok )
2467 {
2468 switch ( mScaleMethod )
2469 {
2471 scaledSize = std::sqrt( scaledSize );
2472 break;
2474 break;
2475 }
2476 }
2477
2478 return scaledSize;
2479}
2480
2481double QgsSvgMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2482{
2484 if ( !hasDataDefinedAspectRatio )
2485 return mFixedAspectRatio;
2486
2488 return 0.0;
2489
2490 double scaledAspectRatio = mDefaultAspectRatio;
2491 if ( mFixedAspectRatio > 0.0 )
2492 scaledAspectRatio = mFixedAspectRatio;
2493
2494 const double defaultHeight = mSize * scaledAspectRatio;
2495 scaledAspectRatio = defaultHeight / scaledSize;
2496
2497 bool ok = true;
2498 double scaledHeight = scaledSize * scaledAspectRatio;
2500 {
2501 context.setOriginalValueVariable( defaultHeight );
2503 }
2504
2505 if ( hasDataDefinedAspectRatio && ok )
2506 {
2507 switch ( mScaleMethod )
2508 {
2510 scaledHeight = sqrt( scaledHeight );
2511 break;
2513 break;
2514 }
2515 }
2516
2517 scaledAspectRatio = scaledHeight / scaledSize;
2518
2519 return scaledAspectRatio;
2520}
2521
2522void QgsSvgMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
2523{
2524 //offset
2525 double offsetX = 0;
2526 double offsetY = 0;
2527 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
2528 offset = QPointF( offsetX, offsetY );
2529
2532 {
2535 }
2536
2538 if ( hasDataDefinedRotation )
2539 {
2540 // For non-point markers, "dataDefinedRotation" means following the
2541 // shape (shape-data defined). For them, "field-data defined" does
2542 // not work at all. TODO: if "field-data defined" ever gets implemented
2543 // we'll need a way to distinguish here between the two, possibly
2544 // using another flag in renderHints()
2545 const QgsFeature *f = context.feature();
2546 if ( f )
2547 {
2548 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
2549 {
2550 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2551 angle += m2p.mapRotation();
2552 }
2553 }
2554 }
2555
2556 if ( angle )
2558}
2559
2560
2562{
2563 QVariantMap map;
2564 map[QStringLiteral( "name" )] = mPath;
2565 map[QStringLiteral( "size" )] = QString::number( mSize );
2566 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2567 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2568 map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2569 map[QStringLiteral( "angle" )] = QString::number( mAngle );
2570 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2571 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2572 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2573 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2574 map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
2575 map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
2576 map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
2577 map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2578 map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2579 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
2580 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
2581
2582 map[QStringLiteral( "parameters" )] = QgsProperty::propertyMapToVariantMap( mParameters );
2583
2584 return map;
2585}
2586
2588{
2589 return mSizeUnit == Qgis::RenderUnit::MapUnits || mSizeUnit == Qgis::RenderUnit::MetersInMapUnits
2590 || mOffsetUnit == Qgis::RenderUnit::MapUnits || mOffsetUnit == Qgis::RenderUnit::MetersInMapUnits
2591 || mStrokeWidthUnit == Qgis::RenderUnit::MapUnits || mStrokeWidthUnit == Qgis::RenderUnit::MetersInMapUnits;
2592}
2593
2595{
2598 m->setColor( mColor );
2603 m->setOffset( mOffset );
2606 m->setSizeUnit( mSizeUnit );
2611
2613 copyPaintEffect( m );
2614 return m;
2615}
2616
2618{
2620 mStrokeWidthUnit = unit;
2621}
2622
2624{
2626 if ( unit != mStrokeWidthUnit )
2627 {
2628 return Qgis::RenderUnit::Unknown;
2629 }
2630 return unit;
2631}
2632
2634{
2637}
2638
2640{
2642 {
2644 }
2645 return QgsMapUnitScale();
2646}
2647
2648void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
2649{
2650 // <Graphic>
2651 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2652 element.appendChild( graphicElem );
2653
2654 // encode a parametric SVG reference
2655 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
2658
2659 // <Rotation>
2660 QString angleFunc;
2661 bool ok;
2662 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2663 if ( !ok )
2664 {
2665 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
2666 }
2667 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2668 {
2669 angleFunc = QString::number( angle + mAngle );
2670 }
2671
2672 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2673
2674 // <Displacement>
2675 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2677}
2678
2680{
2681 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
2682
2683 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
2684 if ( graphicElem.isNull() )
2685 return nullptr;
2686
2687 QString path, mimeType;
2688 // Unused and to be DEPRECATED in externalGraphicFromSld
2689 QColor fillColor_;
2690 double size;
2691
2692 if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor_, size ) )
2693 return nullptr;
2694
2695 double scaleFactor = 1.0;
2696 const QString uom = element.attribute( QStringLiteral( "uom" ) );
2697 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
2698 size = size * scaleFactor;
2699
2700 if ( mimeType != QLatin1String( "image/svg+xml" ) )
2701 return nullptr;
2702
2703 double angle = 0.0;
2704 QString angleFunc;
2705 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2706 {
2707 bool ok;
2708 const double d = angleFunc.toDouble( &ok );
2709 if ( ok )
2710 angle = d;
2711 }
2712
2713 QPointF offset;
2715
2716 // Extract parameters from URL
2717 QString realPath { path };
2718 QUrl svgUrl { path };
2719
2720 // Because color definition can start with '#', the url parsing won't recognize the query string entirely
2721 QUrlQuery queryString;
2722
2723 if ( svgUrl.hasQuery() && svgUrl.hasFragment() )
2724 {
2725 const QString queryPart { path.mid( path.indexOf( '?' ) + 1 ) };
2726 queryString.setQuery( queryPart );
2727 }
2728
2729 // Remove query for simple file paths
2730 if ( svgUrl.scheme().isEmpty() || svgUrl.isLocalFile() )
2731 {
2732 svgUrl.setQuery( QString() );
2733 realPath = svgUrl.path();
2734 }
2735
2737
2738 QMap<QString, QgsProperty> params;
2739
2740 bool ok;
2741
2742 if ( queryString.hasQueryItem( QStringLiteral( "fill" ) ) )
2743 {
2744 const QColor fillColor { queryString.queryItemValue( QStringLiteral( "fill" ) ) };
2745 m->setFillColor( fillColor );
2746 }
2747
2748 if ( queryString.hasQueryItem( QStringLiteral( "fill-opacity" ) ) )
2749 {
2750 const double alpha { queryString.queryItemValue( QStringLiteral( "fill-opacity" ) ).toDouble( &ok ) };
2751 if ( ok )
2752 {
2753 params.insert( QStringLiteral( "fill-opacity" ), QgsProperty::fromValue( alpha ) );
2754 }
2755 }
2756
2757 if ( queryString.hasQueryItem( QStringLiteral( "outline" ) ) )
2758 {
2759 const QColor strokeColor { queryString.queryItemValue( QStringLiteral( "outline" ) ) };
2761 }
2762
2763 if ( queryString.hasQueryItem( QStringLiteral( "outline-opacity" ) ) )
2764 {
2765 const double alpha { queryString.queryItemValue( QStringLiteral( "outline-opacity" ) ).toDouble( &ok ) };
2766 if ( ok )
2767 {
2768 params.insert( QStringLiteral( "outline-opacity" ), QgsProperty::fromValue( alpha ) );
2769 }
2770 }
2771
2772 if ( queryString.hasQueryItem( QStringLiteral( "outline-width" ) ) )
2773 {
2774 const int width { queryString.queryItemValue( QStringLiteral( "outline-width" ) ).toInt( &ok )};
2775 if ( ok )
2776 {
2777 m->setStrokeWidth( width );
2778 }
2779 }
2780
2781 if ( ! params.isEmpty() )
2782 {
2783 m->setParameters( params );
2784 }
2785
2786 m->setOutputUnit( sldUnitSize );
2787 m->setAngle( angle );
2788 m->setOffset( offset );
2789 return m;
2790}
2791
2792bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
2793{
2794 //size
2795 double size = mSize;
2796
2797 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
2798
2799 bool ok = true;
2800 if ( hasDataDefinedSize )
2801 {
2804 }
2805
2806 if ( hasDataDefinedSize && ok )
2807 {
2808 switch ( mScaleMethod )
2809 {
2811 size = std::sqrt( size );
2812 break;
2814 break;
2815 }
2816 }
2817
2818 if ( mSizeUnit == Qgis::RenderUnit::Millimeters )
2819 {
2820 size *= mmMapUnitScaleFactor;
2821 }
2822
2823 //offset, angle
2824 QPointF offset = mOffset;
2825
2827 {
2829 const QVariant val = mDataDefinedProperties.value( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext(), QString() );
2830 const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
2831 if ( ok )
2832 offset = res;
2833 }
2834 const double offsetX = offset.x();
2835 const double offsetY = offset.y();
2836
2837 QPointF outputOffset( offsetX, offsetY );
2838
2839 double angle = mAngle + mLineAngle;
2841 {
2844 }
2845
2846 if ( angle )
2847 outputOffset = _rotatedOffset( outputOffset, angle );
2848
2850
2851 QString path = mPath;
2853 {
2856 context.renderContext().pathResolver() );
2857 }
2858
2859 double strokeWidth = mStrokeWidth;
2861 {
2864 }
2866
2867 QColor fillColor = mColor;
2869 {
2872 }
2873
2874 QColor strokeColor = mStrokeColor;
2876 {
2879 }
2880
2882
2883 const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, size, fillColor, strokeColor, strokeWidth,
2885 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2886
2887 QSvgRenderer r( svgContent );
2888 if ( !r.isValid() )
2889 return false;
2890
2891 QgsDxfPaintDevice pd( &e );
2892 pd.setDrawingSize( QSizeF( r.defaultSize() ) );
2893
2894 QSizeF outSize( r.defaultSize() );
2895 outSize.scale( size, size, Qt::KeepAspectRatio );
2896
2897 QPainter p;
2898 p.begin( &pd );
2899 if ( !qgsDoubleNear( angle, 0.0 ) )
2900 {
2901 p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
2902 p.rotate( angle );
2903 p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
2904 }
2905 pd.setShift( shift + QPointF( outputOffset.x(), -outputOffset.y() ) );
2906 pd.setOutputSize( QRectF( -outSize.width() / 2.0, -outSize.height() / 2.0, outSize.width(), outSize.height() ) );
2907 pd.setLayer( layerName );
2908 r.render( &p );
2909 p.end();
2910 return true;
2911}
2912
2914{
2915 bool hasDataDefinedSize = false;
2916 double scaledWidth = calculateSize( context, hasDataDefinedSize );
2917
2918 bool hasDataDefinedAspectRatio = false;
2919 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2920 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2921
2922 scaledWidth = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2923 scaledHeight = context.renderContext().convertToPainterUnits( scaledHeight, mSizeUnit, mSizeMapUnitScale );
2924
2925 //don't render symbols with size below one or above 10,000 pixels
2926 if ( static_cast< int >( scaledWidth ) < 1 || 10000.0 < scaledWidth )
2927 {
2928 return QRectF();
2929 }
2930
2931 QPointF outputOffset;
2932 double angle = 0.0;
2933 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2934
2935 double strokeWidth = mStrokeWidth;
2937 {
2940 }
2942
2943 QString path = mPath;
2945 {
2948 context.renderContext().pathResolver() );
2950 {
2951 // need to get colors to take advantage of cached SVGs
2952 QColor fillColor = mColor;
2954 {
2957 }
2958
2959 const QColor strokeColor = mStrokeColor;
2961 {
2964 }
2965
2967
2968 // adjust height of data defined path
2969 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2970 context.renderContext().scaleFactor(), aspectRatio,
2971 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2972 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2973 }
2974 }
2975
2976 QTransform transform;
2977 // move to the desired position
2978 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
2979
2980 if ( !qgsDoubleNear( angle, 0.0 ) )
2981 transform.rotate( angle );
2982
2983 //antialiasing
2984 strokeWidth += 1.0 / 2.0;
2985
2986 QRectF symbolBounds = transform.mapRect( QRectF( -scaledWidth / 2.0,
2987 -scaledHeight / 2.0,
2988 scaledWidth,
2989 scaledHeight ) );
2990
2991 //extend bounds by pen width / 2.0
2992 symbolBounds.adjust( -strokeWidth / 2.0, -strokeWidth / 2.0,
2993 strokeWidth / 2.0, strokeWidth / 2.0 );
2994
2995 return symbolBounds;
2996}
2997
2999
3000QgsRasterMarkerSymbolLayer::QgsRasterMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
3001 : mPath( path )
3002{
3003 mSize = size;
3004 mAngle = angle;
3005 mOffset = QPointF( 0, 0 );
3008}
3009
3011
3013{
3014 QString path;
3018
3019 if ( props.contains( QStringLiteral( "imageFile" ) ) )
3020 path = props[QStringLiteral( "imageFile" )].toString();
3021 if ( props.contains( QStringLiteral( "size" ) ) )
3022 size = props[QStringLiteral( "size" )].toDouble();
3023 if ( props.contains( QStringLiteral( "angle" ) ) )
3024 angle = props[QStringLiteral( "angle" )].toDouble();
3025 if ( props.contains( QStringLiteral( "scale_method" ) ) )
3026 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
3027
3028 std::unique_ptr< QgsRasterMarkerSymbolLayer > m = std::make_unique< QgsRasterMarkerSymbolLayer >( path, size, angle, scaleMethod );
3029 m->setCommonProperties( props );
3030 return m.release();
3031}
3032
3033void QgsRasterMarkerSymbolLayer::setCommonProperties( const QVariantMap &properties )
3034{
3035 if ( properties.contains( QStringLiteral( "alpha" ) ) )
3036 {
3037 setOpacity( properties[QStringLiteral( "alpha" )].toDouble() );
3038 }
3039
3040 if ( properties.contains( QStringLiteral( "size_unit" ) ) )
3041 setSizeUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "size_unit" )].toString() ) );
3042 if ( properties.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3043 setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3044 if ( properties.contains( QStringLiteral( "fixedAspectRatio" ) ) )
3045 setFixedAspectRatio( properties[QStringLiteral( "fixedAspectRatio" )].toDouble() );
3046
3047 if ( properties.contains( QStringLiteral( "offset" ) ) )
3048 setOffset( QgsSymbolLayerUtils::decodePoint( properties[QStringLiteral( "offset" )].toString() ) );
3049 if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
3050 setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )].toString() ) );
3051 if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3052 setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3053
3054 if ( properties.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3055 {
3056 setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( properties[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3057 }
3058 if ( properties.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3059 {
3060 setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( properties[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3061 }
3062
3065}
3066
3067void QgsRasterMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
3068{
3069 const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
3070 if ( it != properties.end() && it.value().type() == QVariant::String )
3071 {
3072 if ( saving )
3073 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
3074 else
3075 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
3076 }
3077}
3078
3079void QgsRasterMarkerSymbolLayer::setPath( const QString &path )
3080{
3081 mPath = path;
3083}
3084
3086{
3087 const bool aPreservedAspectRatio = preservedAspectRatio();
3088 if ( aPreservedAspectRatio && !par )
3089 {
3091 }
3092 else if ( !aPreservedAspectRatio && par )
3093 {
3094 mFixedAspectRatio = 0.0;
3095 }
3096 return preservedAspectRatio();
3097}
3098
3100{
3101 if ( mDefaultAspectRatio == 0.0 )
3102 {
3104 mDefaultAspectRatio = ( !size.isNull() && size.isValid() && size.width() > 0 ) ? static_cast< double >( size.height() ) / static_cast< double >( size.width() ) : 0.0;
3105 }
3106 return mDefaultAspectRatio;
3107}
3108
3110{
3111 return QStringLiteral( "RasterMarker" );
3112}
3113
3115{
3116 QPainter *p = context.renderContext().painter();
3117 if ( !p )
3118 return;
3119
3120 QString path = mPath;
3122 {
3125 }
3126
3127 if ( path.isEmpty() )
3128 return;
3129
3130 double width = 0.0;
3131 double height = 0.0;
3132
3133 bool hasDataDefinedSize = false;
3134 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3135
3136 bool hasDataDefinedAspectRatio = false;
3137 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3138
3139 QPointF outputOffset;
3140 double angle = 0.0;
3141
3142 // RenderPercentage Unit Type takes original image size
3143 if ( mSizeUnit == Qgis::RenderUnit::Percentage )
3144 {
3146 if ( size.isEmpty() )
3147 return;
3148
3149 width = ( scaledSize * static_cast< double >( size.width() ) ) / 100.0;
3150 height = ( scaledSize * static_cast< double >( size.height() ) ) / 100.0;
3151
3152 // don't render symbols with size below one or above 10,000 pixels
3153 if ( static_cast< int >( width ) < 1 || 10000.0 < width || static_cast< int >( height ) < 1 || 10000.0 < height )
3154 return;
3155
3156 calculateOffsetAndRotation( context, width, height, outputOffset, angle );
3157 }
3158 else
3159 {
3160 width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3161 height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3162
3163 if ( preservedAspectRatio() && path != mPath )
3164 {
3166 if ( !size.isNull() && size.isValid() && size.width() > 0 )
3167 {
3168 height = width * ( static_cast< double >( size.height() ) / static_cast< double >( size.width() ) );
3169 }
3170 }
3171
3172 // don't render symbols with size below one or above 10,000 pixels
3173 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
3174 return;
3175
3176 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3177 }
3178
3179 const QgsScopedQPainterState painterState( p );
3180 p->translate( point + outputOffset );
3181
3182 const bool rotated = !qgsDoubleNear( angle, 0 );
3183 if ( rotated )
3184 p->rotate( angle );
3185
3186 double opacity = mOpacity;
3188 {
3191 }
3192 opacity *= context.opacity();
3193
3194 QImage img = fetchImage( context.renderContext(), path, QSize( width, preservedAspectRatio() ? 0 : width * aspectRatio ), preservedAspectRatio(), opacity );
3195 if ( !img.isNull() )
3196 {
3197 if ( context.selected() )
3198 {
3200 }
3201
3202 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
3203 }
3204}
3205
3206QImage QgsRasterMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
3207{
3208 bool cached = false;
3209 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking );
3210}
3211
3212double QgsRasterMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
3213{
3214 double scaledSize = mSize;
3216
3217 bool ok = true;
3218 if ( hasDataDefinedSize )
3219 {
3222 }
3223 else
3224 {
3226 if ( hasDataDefinedSize )
3227 {
3230 }
3231 }
3232
3233 if ( hasDataDefinedSize && ok )
3234 {
3235 switch ( mScaleMethod )
3236 {
3238 scaledSize = std::sqrt( scaledSize );
3239 break;
3241 break;
3242 }
3243 }
3244
3245 return scaledSize;
3246}
3247
3248double QgsRasterMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
3249{
3251 if ( !hasDataDefinedAspectRatio )
3252 return mFixedAspectRatio;
3253
3255 return 0.0;
3256
3257 double scaledAspectRatio = mDefaultAspectRatio;
3258 if ( mFixedAspectRatio > 0.0 )
3259 scaledAspectRatio = mFixedAspectRatio;
3260
3261 const double defaultHeight = mSize * scaledAspectRatio;
3262 scaledAspectRatio = defaultHeight / scaledSize;
3263
3264 bool ok = true;
3265 double scaledHeight = scaledSize * scaledAspectRatio;
3267 {
3268 context.setOriginalValueVariable( defaultHeight );
3270 }
3271
3272 if ( hasDataDefinedAspectRatio && ok )
3273 {
3274 switch ( mScaleMethod )
3275 {
3277 scaledHeight = sqrt( scaledHeight );
3278 break;
3280 break;
3281 }
3282 }
3283
3284 scaledAspectRatio = scaledHeight / scaledSize;
3285
3286 return scaledAspectRatio;
3287}
3288
3289void QgsRasterMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
3290{
3291 //offset
3292 double offsetX = 0;
3293 double offsetY = 0;
3294 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
3295 offset = QPointF( offsetX, offsetY );
3296
3299 {
3302 }
3303
3305 if ( hasDataDefinedRotation )
3306 {
3307 const QgsFeature *f = context.feature();
3308 if ( f )
3309 {
3310 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3311 {
3312 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3313 angle += m2p.mapRotation();
3314 }
3315 }
3316 }
3317
3318 if ( angle )
3320}
3321
3322
3324{
3325 QVariantMap map;
3326 map[QStringLiteral( "imageFile" )] = mPath;
3327 map[QStringLiteral( "size" )] = QString::number( mSize );
3328 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3329 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3330 map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
3331 map[QStringLiteral( "angle" )] = QString::number( mAngle );
3332 map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
3333 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3334 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3335 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3336 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
3337 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3338 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3339 return map;
3340}
3341
3343{
3344 std::unique_ptr< QgsRasterMarkerSymbolLayer > m = std::make_unique< QgsRasterMarkerSymbolLayer >( mPath, mSize, mAngle );
3345 copyCommonProperties( m.get() );
3346 return m.release();
3347}
3348
3349
3351{
3353 other->setOpacity( mOpacity );
3354 other->setOffset( mOffset );
3355 other->setOffsetUnit( mOffsetUnit );
3357 other->setSizeUnit( mSizeUnit );
3362 copyPaintEffect( other );
3363}
3364
3366{
3367 return mSizeUnit == Qgis::RenderUnit::MapUnits || mSizeUnit == Qgis::RenderUnit::MetersInMapUnits
3368 || mOffsetUnit == Qgis::RenderUnit::MapUnits || mOffsetUnit == Qgis::RenderUnit::MetersInMapUnits;
3369}
3370
3372{
3373 return QColor();
3374}
3375
3377{
3379}
3380
3382{
3384}
3385
3387{
3388 bool hasDataDefinedSize = false;
3389 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3390 const double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3391 bool hasDataDefinedAspectRatio = false;
3392 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3393 const double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3394
3395 //don't render symbols with size below one or above 10,000 pixels
3396 if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
3397 {
3398 return QRectF();
3399 }
3400
3401 QPointF outputOffset;
3402 double angle = 0.0;
3403 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3404
3405 QTransform transform;
3406
3407 // move to the desired position
3408 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3409
3410 if ( !qgsDoubleNear( angle, 0.0 ) )
3411 transform.rotate( angle );
3412
3413 QRectF symbolBounds = transform.mapRect( QRectF( -width / 2.0,
3414 -height / 2.0,
3415 width,
3416 height ) );
3417
3418 return symbolBounds;
3419}
3420
3422
3423QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QString chr, double pointSize, const QColor &color, double angle )
3424{
3425 mFontFamily = fontFamily;
3426 mString = chr;
3427 mColor = color;
3428 mAngle = angle;
3429 mSize = pointSize;
3430 mOrigSize = pointSize;
3431 mSizeUnit = Qgis::RenderUnit::Millimeters;
3432 mOffset = QPointF( 0, 0 );
3433 mOffsetUnit = Qgis::RenderUnit::Millimeters;
3434 mStrokeColor = DEFAULT_FONTMARKER_BORDERCOLOR;
3435 mStrokeWidth = 0.0;
3436 mStrokeWidthUnit = Qgis::RenderUnit::Millimeters;
3437 mPenJoinStyle = DEFAULT_FONTMARKER_JOINSTYLE;
3438}
3439
3441
3443{
3445 QString string = DEFAULT_FONTMARKER_CHR;
3446 double pointSize = DEFAULT_FONTMARKER_SIZE;
3449
3450 if ( props.contains( QStringLiteral( "font" ) ) )
3451 fontFamily = props[QStringLiteral( "font" )].toString();
3452 if ( props.contains( QStringLiteral( "chr" ) ) && props[QStringLiteral( "chr" )].toString().length() > 0 )
3453 string = props[QStringLiteral( "chr" )].toString();
3454 if ( props.contains( QStringLiteral( "size" ) ) )
3455 pointSize = props[QStringLiteral( "size" )].toDouble();
3456 if ( props.contains( QStringLiteral( "color" ) ) )
3457 color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )].toString() );
3458 if ( props.contains( QStringLiteral( "angle" ) ) )
3459 angle = props[QStringLiteral( "angle" )].toDouble();
3460
3462
3463 if ( props.contains( QStringLiteral( "font_style" ) ) )
3464 m->setFontStyle( props[QStringLiteral( "font_style" )].toString() );
3465 if ( props.contains( QStringLiteral( "outline_color" ) ) )
3466 m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )].toString() ) );
3467 if ( props.contains( QStringLiteral( "outline_width" ) ) )
3468 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
3469 if ( props.contains( QStringLiteral( "offset" ) ) )
3470 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
3471 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
3472 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
3473 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3474 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3475 if ( props.contains( QStringLiteral( "size_unit" ) ) )
3476 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
3477 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3478 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3479 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
3480 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
3481 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
3482 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
3483 if ( props.contains( QStringLiteral( "joinstyle" ) ) )
3484 m->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() ) );
3485 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3486 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3487 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3488 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3489
3491
3492 return m;
3493}
3494
3496{
3497 return QStringLiteral( "FontMarker" );
3498}
3499
3501{
3502 QColor brushColor = mColor;
3503 QColor penColor = mStrokeColor;
3504
3505 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
3506 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
3507
3508 mBrush = QBrush( brushColor );
3509 mPen = QPen( penColor );
3510 mPen.setJoinStyle( mPenJoinStyle );
3511 mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
3512
3513 mFont = QFont( QgsApplication::fontManager()->processFontFamilyName( mFontFamily ) );
3514 if ( !mFontStyle.isEmpty() )
3515 {
3516 mFont.setStyleName( QgsFontUtils::translateNamedStyle( mFontStyle ) );
3517 }
3518
3519 double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3520 mNonZeroFontSize = !qgsDoubleNear( sizePixels, 0.0 );
3521
3522 if ( mNonZeroFontSize && sizePixels > MAX_FONT_CHARACTER_SIZE_IN_PIXELS )
3523 {
3524 // if font is too large (e.g using map units and map is very zoomed in), then we limit
3525 // the font size and instead scale up the painter.
3526 // this avoids issues with massive font sizes (eg https://github.com/qgis/QGIS/issues/42270)
3527 mFontSizeScale = sizePixels / MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3528 sizePixels = MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3529 }
3530 else
3531 mFontSizeScale = 1.0;
3532
3533 // if a non zero, but small pixel size results, round up to 2 pixels so that a "dot" is at least visible
3534 // (if we set a <=1 pixel size here Qt will reset the font to a default size, leading to much too large symbols)
3535 mFont.setPixelSize( std::max( 2, static_cast< int >( std::round( sizePixels ) ) ) );
3536 mFontMetrics.reset( new QFontMetrics( mFont ) );
3537 mChrWidth = mFontMetrics->horizontalAdvance( mString );
3538 mChrOffset = QPointF( mChrWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3539 mOrigSize = mSize; // save in case the size would be data defined
3540
3541 // use caching only when not using a data defined character
3545 if ( mUseCachedPath )
3546 {
3547 QPointF chrOffset = mChrOffset;
3548 double chrWidth;
3549 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3550 mCachedPath = QPainterPath();
3551 mCachedPath.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3552 }
3553}
3554
3556{
3557 Q_UNUSED( context )
3558}
3559
3560QString QgsFontMarkerSymbolLayer::characterToRender( QgsSymbolRenderContext &context, QPointF &charOffset, double &charWidth )
3561{
3562 charOffset = mChrOffset;
3563 QString stringToRender = mString;
3565 {
3566 context.setOriginalValueVariable( mString );
3568 if ( stringToRender != mString )
3569 {
3570 charWidth = mFontMetrics->horizontalAdvance( stringToRender );
3571 charOffset = QPointF( charWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3572 }
3573 }
3574 return stringToRender;
3575}
3576
3577void QgsFontMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
3578 double scaledSize,
3579 bool &hasDataDefinedRotation,
3580 QPointF &offset,
3581 double &angle ) const
3582{
3583 //offset
3584 double offsetX = 0;
3585 double offsetY = 0;
3586 markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
3587 offset = QPointF( offsetX, offsetY );
3588
3589 //angle
3590 bool ok = true;
3593 {
3596
3597 // If the expression evaluation was not successful, fallback to static value
3598 if ( !ok )
3600 }
3601
3602 hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation;
3603 if ( hasDataDefinedRotation )
3604 {
3605 // For non-point markers, "dataDefinedRotation" means following the
3606 // shape (shape-data defined). For them, "field-data defined" does
3607 // not work at all. TODO: if "field-data defined" ever gets implemented
3608 // we'll need a way to distinguish here between the two, possibly
3609 // using another flag in renderHints()
3610 const QgsFeature *f = context.feature();
3611 if ( f )
3612 {
3613 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3614 {
3615 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3616 angle += m2p.mapRotation();
3617 }
3618 }
3619 }
3620
3621 if ( angle )
3623}
3624
3625double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context )
3626{
3627 double scaledSize = mSize;
3628 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
3629
3630 bool ok = true;
3631 if ( hasDataDefinedSize )
3632 {
3635 }
3636
3637 if ( hasDataDefinedSize && ok )
3638 {
3639 switch ( mScaleMethod )
3640 {
3642 scaledSize = std::sqrt( scaledSize );
3643 break;
3645 break;
3646 }
3647 }
3648 return scaledSize;
3649}
3650
3652{
3653 QPainter *p = context.renderContext().painter();
3654 if ( !p || !mNonZeroFontSize )
3655 return;
3656
3657 QTransform transform;
3658
3659 bool ok;
3660 QColor brushColor = mColor;
3662 {
3665 }
3666 brushColor = context.selected() ? context.renderContext().selectionColor() : brushColor;
3667 if ( !context.selected() || !SELECTION_IS_OPAQUE )
3668 {
3669 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
3670 }
3671 mBrush.setColor( brushColor );
3672
3673 QColor penColor = mStrokeColor;
3675 {
3678 }
3679 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
3680
3681 double penWidth = context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3683 {
3684 context.setOriginalValueVariable( mStrokeWidth );
3686 if ( ok )
3687 {
3688 penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3689 }
3690 }
3691
3693 {
3696 if ( ok )
3697 {
3698 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
3699 }
3700 }
3701
3702 const QgsScopedQPainterState painterState( p );
3703 p->setBrush( mBrush );
3704 if ( !qgsDoubleNear( penWidth, 0.0 ) )
3705 {
3706 mPen.setColor( penColor );
3707 mPen.setWidthF( penWidth );
3708 p->setPen( mPen );
3709 }
3710 else
3711 {
3712 p->setPen( Qt::NoPen );
3713 }
3714
3716 {
3717 context.setOriginalValueVariable( mFontFamily );
3719 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( ok ? fontFamily : mFontFamily );
3720 mFont.setFamily( processedFamily );
3721 }
3723 {
3724 context.setOriginalValueVariable( mFontStyle );
3727 }
3729 {
3730 mFontMetrics.reset( new QFontMetrics( mFont ) );
3731 }
3732
3733 QPointF chrOffset = mChrOffset;
3734 double chrWidth;
3735 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3736
3737 const double sizeToRender = calculateSize( context );
3738
3739 bool hasDataDefinedRotation = false;
3740 QPointF offset;
3741 double angle = 0;
3742 calculateOffsetAndRotation( context, sizeToRender, hasDataDefinedRotation, offset, angle );
3743
3744 p->translate( point.x() + offset.x(), point.y() + offset.y() );
3745
3746 if ( !qgsDoubleNear( angle, 0.0 ) )
3747 transform.rotate( angle );
3748
3749 if ( !qgsDoubleNear( sizeToRender, mOrigSize ) )
3750 {
3751 const double s = sizeToRender / mOrigSize;
3752 transform.scale( s, s );
3753 }
3754
3755 if ( !qgsDoubleNear( mFontSizeScale, 1.0 ) )
3756 transform.scale( mFontSizeScale, mFontSizeScale );
3757
3758 if ( mUseCachedPath )
3759 {
3760 p->drawPath( transform.map( mCachedPath ) );
3761 }
3762 else
3763 {
3764 QPainterPath path;
3765 path.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3766 p->drawPath( transform.map( path ) );
3767 }
3768}
3769
3771{
3772 QVariantMap props;
3773 props[QStringLiteral( "font" )] = mFontFamily;
3774 props[QStringLiteral( "font_style" )] = mFontStyle;
3775 props[QStringLiteral( "chr" )] = mString;
3776 props[QStringLiteral( "size" )] = QString::number( mSize );
3777 props[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3778 props[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3779 props[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
3780 props[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
3781 props[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
3782 props[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
3783 props[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
3784 props[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
3785 props[QStringLiteral( "angle" )] = QString::number( mAngle );
3786 props[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3787 props[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3788 props[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3789 props[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3790 props[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3791 return props;
3792}
3793
3795{
3796 QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( mFontFamily, mString, mSize, mColor, mAngle );
3797 m->setFontStyle( mFontStyle );
3798 m->setStrokeColor( mStrokeColor );
3799 m->setStrokeWidth( mStrokeWidth );
3800 m->setStrokeWidthUnit( mStrokeWidthUnit );
3801 m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
3802 m->setPenJoinStyle( mPenJoinStyle );
3803 m->setOffset( mOffset );
3806 m->setSizeUnit( mSizeUnit );
3811 copyPaintEffect( m );
3812 return m;
3813}
3814
3815void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
3816{
3817 // <Graphic>
3818 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
3819 element.appendChild( graphicElem );
3820
3821 const QString fontPath = QStringLiteral( "ttf://%1" ).arg( mFontFamily );
3822 int markIndex = !mString.isEmpty() ? mString.at( 0 ).unicode() : 0;
3823 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
3824 QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, QStringLiteral( "ttf" ), &markIndex, mColor, size );
3825
3826 // <Rotation>
3827 QString angleFunc;
3828 bool ok;
3829 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
3830 if ( !ok )
3831 {
3832 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
3833 }
3834 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
3835 {
3836 angleFunc = QString::number( angle + mAngle );
3837 }
3838 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
3839
3840 // <Displacement>
3841 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
3843}
3844
3846{
3847 return mSizeUnit == Qgis::RenderUnit::MapUnits || mSizeUnit == Qgis::RenderUnit::MetersInMapUnits
3848 || mStrokeWidthUnit == Qgis::RenderUnit::MapUnits || mStrokeWidthUnit == Qgis::RenderUnit::MetersInMapUnits
3849 || mOffsetUnit == Qgis::RenderUnit::MapUnits || mOffsetUnit == Qgis::RenderUnit::MetersInMapUnits;
3850}
3851
3853{
3855 mStrokeWidthUnit = unit;
3856}
3857
3859{
3860 QPointF chrOffset = mChrOffset;
3861 double chrWidth = mChrWidth;
3862 //calculate width of rendered character
3863 ( void )characterToRender( context, chrOffset, chrWidth );
3864
3865 if ( !mFontMetrics )
3866 mFontMetrics.reset( new QFontMetrics( mFont ) );
3867
3868 double scaledSize = calculateSize( context );
3869 if ( !qgsDoubleNear( scaledSize, mOrigSize ) )
3870 {
3871 chrWidth *= scaledSize / mOrigSize;
3872 }
3873 chrWidth *= mFontSizeScale;
3874
3875 bool hasDataDefinedRotation = false;
3876 QPointF offset;
3877 double angle = 0;
3878 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
3879 scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3880
3881 QTransform transform;
3882
3883 // move to the desired position
3884 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
3885
3886 if ( !qgsDoubleNear( angle, 0.0 ) )
3887 transform.rotate( angle );
3888
3889 QRectF symbolBounds = transform.mapRect( QRectF( -chrWidth / 2.0,
3890 -scaledSize / 2.0,
3891 chrWidth,
3892 scaledSize ) );
3893 return symbolBounds;
3894}
3895
3897{
3898 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
3899
3900 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
3901 if ( graphicElem.isNull() )
3902 return nullptr;
3903
3904 QString name, format;
3905 QColor color;
3906 double size;
3907 int chr;
3908
3909 if ( !QgsSymbolLayerUtils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
3910 return nullptr;
3911
3912 if ( !name.startsWith( QLatin1String( "ttf://" ) ) || format != QLatin1String( "ttf" ) )
3913 return nullptr;
3914
3915 const QString fontFamily = name.mid( 6 );
3916
3917 double angle = 0.0;
3918 QString angleFunc;
3919 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
3920 {
3921 bool ok;
3922 const double d = angleFunc.toDouble( &ok );
3923 if ( ok )
3924 angle = d;
3925 }
3926
3927 QPointF offset;
3929
3930 double scaleFactor = 1.0;
3931 const QString uom = element.attribute( QStringLiteral( "uom" ) );
3932 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
3933 offset.setX( offset.x() * scaleFactor );
3934 offset.setY( offset.y() * scaleFactor );
3935 size = size * scaleFactor;
3936
3938 m->setOutputUnit( sldUnitSize );
3939 m->setAngle( angle );
3940 m->setOffset( offset );
3941 return m;
3942}
3943
3944void QgsFontMarkerSymbolLayer::resolveFonts( const QVariantMap &properties, const QgsReadWriteContext &context )
3945{
3946 const QString fontFamily = properties.value( QStringLiteral( "font" ), DEFAULT_FONTMARKER_FONT ).toString();
3947 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( fontFamily );
3948 QString matched;
3949 if ( !QgsFontUtils::fontFamilyMatchOnSystem( processedFamily )
3950 && !QgsApplication::fontManager()->tryToDownloadFontFamily( processedFamily, matched ) )
3951 {
3952 context.pushMessage( QObject::tr( "Font “%1” not available on system" ).arg( processedFamily ) );
3953 }
3954}
3955
3957{
3958 QMap<QString, QgsProperty>::iterator it = mParameters.begin();
3959 for ( ; it != mParameters.end(); ++it )
3960 it.value().prepare( context.renderContext().expressionContext() );
3961
3963}
3964
3965
3967{
3968 QSet<QString> attrs = QgsMarkerSymbolLayer::usedAttributes( context );
3969
3970 QMap<QString, QgsProperty>::const_iterator it = mParameters.constBegin();
3971 for ( ; it != mParameters.constEnd(); ++it )
3972 {
3973 attrs.unite( it.value().referencedFields( context.expressionContext(), true ) );
3974 }
3975
3976 return attrs;
3977}
3978
3979//
3980// QgsAnimatedMarkerSymbolLayer
3981//
3982
3984 : QgsRasterMarkerSymbolLayer( path, size, angle )
3985{
3986
3987}
3988
3990
3992{
3993 QString path;
3996
3997 if ( properties.contains( QStringLiteral( "imageFile" ) ) )
3998 path = properties[QStringLiteral( "imageFile" )].toString();
3999 if ( properties.contains( QStringLiteral( "size" ) ) )
4000 size = properties[QStringLiteral( "size" )].toDouble();
4001 if ( properties.contains( QStringLiteral( "angle" ) ) )
4002 angle = properties[QStringLiteral( "angle" )].toDouble();
4003
4004 std::unique_ptr< QgsAnimatedMarkerSymbolLayer > m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( path, size, angle );
4005 m->setFrameRate( properties.value( QStringLiteral( "frameRate" ), QStringLiteral( "10" ) ).toDouble() );
4006
4007 m->setCommonProperties( properties );
4008 return m.release();
4009}
4010
4012{
4013 return QStringLiteral( "AnimatedMarker" );
4014}
4015
4017{
4018 QVariantMap res = QgsRasterMarkerSymbolLayer::properties();
4019 res.insert( QStringLiteral( "frameRate" ), mFrameRateFps );
4020 return res;
4021}
4022
4024{
4025 std::unique_ptr< QgsAnimatedMarkerSymbolLayer > m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( mPath, mSize, mAngle );
4026 m->setFrameRate( mFrameRateFps );
4027 copyCommonProperties( m.get() );
4028 return m.release();
4029}
4030
4032{
4034
4035 mPreparedPaths.clear();
4037 {
4039 mStaticPath = true;
4040 }
4041 else
4042 {
4043 mStaticPath = false;
4044 }
4045}
4046
4047QImage QgsAnimatedMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
4048{
4049 if ( !mStaticPath && !mPreparedPaths.contains( path ) )
4050 {
4052 mPreparedPaths.insert( path );
4053 }
4054
4055 const long long mapFrameNumber = context.currentFrame();
4057 const double markerAnimationDuration = totalFrameCount / mFrameRateFps;
4058
4059 double animationTimeSeconds = 0;
4060 if ( mapFrameNumber >= 0 && context.frameRate() > 0 )
4061 {
4062 // render is part of an animation, so we base the calculated frame on that
4063 animationTimeSeconds = mapFrameNumber / context.frameRate();
4064 }
4065 else
4066 {
4067 // render is outside of animation, so base the calculated frame on the current epoch
4068 animationTimeSeconds = QDateTime::currentMSecsSinceEpoch() / 1000.0;
4069 }
4070
4071 const double markerAnimationProgressSeconds = std::fmod( animationTimeSeconds, markerAnimationDuration );
4072 const int movieFrame = static_cast< int >( std::floor( markerAnimationProgressSeconds * mFrameRateFps ) );
4073
4074 bool cached = false;
4075 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking, 96, movieFrame );
4076}
4077
@ DynamicRotation
Rotation of symbol may be changed during rendering and symbol should not be cached.
ScaleMethod
Scale methods.
Definition: qgis.h:334
@ ScaleDiameter
Calculate scale by the diameter.
@ ScaleArea
Calculate scale by the area.
MarkerShape
Marker shapes.
Definition: qgis.h:1909
@ 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:3176
@ 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:229
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:233
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...
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:93
Qgis::SymbolType type() const
Returns the symbol's type.
Definition: qgssymbol.h:152
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:3509
QMap< QString, QString > QgsStringMap
Definition: qgis.h:4054
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
#define DEG2RAD(x)
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(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