QGIS API Documentation 3.34.0-Prizren (ffbdd678812)
Loading...
Searching...
No Matches
qgsmarkersymbollayer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmarkersymbollayer.cpp
3 ---------------------
4 begin : November 2009
5 copyright : (C) 2009 by Martin Dobias
6 email : wonder dot sk at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17#include "qgssymbollayerutils.h"
18
19#include "qgsdxfexport.h"
20#include "qgsdxfpaintdevice.h"
21#include "qgsfontutils.h"
22#include "qgsimagecache.h"
23#include "qgsimageoperation.h"
24#include "qgsrendercontext.h"
25#include "qgslogger.h"
26#include "qgssvgcache.h"
27#include "qgsunittypes.h"
28#include "qgssymbol.h"
29#include "qgsfillsymbol.h"
30#include "qgsfontmanager.h"
31
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
108
110 : mShape( shape )
111{
112 mSize = size;
113 mAngle = angle;
114 mOffset = QPointF( 0, 0 );
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 );
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 );
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
502
504{
505 polygon.clear();
506
507 switch ( shape )
508 {
510 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 1, 1 ) ) );
511 return true;
512
514 {
515 static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 0.6072;
516
517 polygon << QPointF( - VERTEX_OFFSET_FROM_ORIGIN, 1 )
518 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, 1 )
519 << QPointF( 1, VERTEX_OFFSET_FROM_ORIGIN )
520 << QPointF( 1, -VERTEX_OFFSET_FROM_ORIGIN )
521 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, -1 )
522 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, -1 )
523 << QPointF( -1, -VERTEX_OFFSET_FROM_ORIGIN )
524 << QPointF( -1, VERTEX_OFFSET_FROM_ORIGIN )
525 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 );
526 return true;
527 }
528
530 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 0 ) ) );
531 return true;
532
534 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 1 ) ) );
535 return true;
536
538 polygon << QPointF( -1, -1 ) << QPointF( 1, 1 ) << QPointF( -1, 1 ) << QPointF( -1, -1 );
539 return true;
540
542 polygon << QPointF( 0.5, -0.5 )
543 << QPointF( 1, 0.5 )
544 << QPointF( -1, 0.5 )
545 << QPointF( -0.5, -0.5 )
546 << QPointF( 0.5, -0.5 );
547 return true;
548
550 polygon << QPointF( 0.5, 0.5 )
551 << QPointF( 1, -0.5 )
552 << QPointF( -0.5, -0.5 )
553 << QPointF( -1, 0.5 )
554 << QPointF( 0.5, 0.5 );
555 return true;
556
558 polygon << QPointF( 1, 0.5 )
559 << QPointF( 0.5, -0.5 )
560 << QPointF( -1, -0.5 )
561 << QPointF( -0.5, 0.5 )
562 << QPointF( 1, 0.5 );
563 return true;
564
566 polygon << QPointF( -1, 0 ) << QPointF( 0, 1 )
567 << QPointF( 1, 0 ) << QPointF( 0, -1 ) << QPointF( -1, 0 );
568 return true;
569
571 polygon << QPointF( 1, 0.5 )
572 << QPointF( 1, -1 )
573 << QPointF( -1, -1 )
574 << QPointF( -1, 0.5 )
575 << QPointF( 0, 1 )
576 << QPointF( 1, 0.5 );
577 return true;
578
580 /* angular-representation of hardcoded values used
581 polygon << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288.0 ) ) )
582 << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) )
583 << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) )
584 << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) )
585 << QPointF( 0, -1 ); */
586 polygon << QPointF( -0.9511, -0.3090 )
587 << QPointF( -0.5878, 0.8090 )
588 << QPointF( 0.5878, 0.8090 )
589 << QPointF( 0.9511, -0.3090 )
590 << QPointF( 0, -1 )
591 << QPointF( -0.9511, -0.3090 );
592 return true;
593
595 /* angular-representation of hardcoded values used
596 polygon << QPointF( std::sin( DEG2RAD( 300.0 ) ), - std::cos( DEG2RAD( 300.0 ) ) )
597 << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
598 << QPointF( std::sin( DEG2RAD( 180.0 ) ), - std::cos( DEG2RAD( 180.0 ) ) )
599 << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
600 << QPointF( std::sin( DEG2RAD( 60.0 ) ), - std::cos( DEG2RAD( 60.0 ) ) )
601 << QPointF( 0, -1 ); */
602 polygon << QPointF( -0.8660, -0.5 )
603 << QPointF( -0.8660, 0.5 )
604 << QPointF( 0, 1 )
605 << QPointF( 0.8660, 0.5 )
606 << QPointF( 0.8660, -0.5 )
607 << QPointF( 0, -1 )
608 << QPointF( -0.8660, -0.5 );
609 return true;
610
612 {
613 static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 1.0 / ( 1 + M_SQRT2 );
614
615 polygon << QPointF( - VERTEX_OFFSET_FROM_ORIGIN, 1 )
616 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, 1 )
617 << QPointF( 1, VERTEX_OFFSET_FROM_ORIGIN )
618 << QPointF( 1, -VERTEX_OFFSET_FROM_ORIGIN )
619 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, -1 )
620 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, -1 )
621 << QPointF( -1, -VERTEX_OFFSET_FROM_ORIGIN )
622 << QPointF( -1, VERTEX_OFFSET_FROM_ORIGIN )
623 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 );
624 return true;
625 }
626
628 {
629
630 polygon << QPointF( 0.587785252, 0.809016994 )
631 << QPointF( 0.951056516, 0.309016994 )
632 << QPointF( 0.951056516, -0.309016994 )
633 << QPointF( 0.587785252, -0.809016994 )
634 << QPointF( 0, -1 )
635 << QPointF( -0.587785252, -0.809016994 )
636 << QPointF( -0.951056516, -0.309016994 )
637 << QPointF( -0.951056516, 0.309016994 )
638 << QPointF( -0.587785252, 0.809016994 )
639 << QPointF( 0, 1 )
640 << QPointF( 0.587785252, 0.809016994 );
641 return true;
642 }
643
645 polygon << QPointF( -1, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
646 return true;
647
649 /* angular-representation of hardcoded values used
650 polygon << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
651 << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
652 << QPointF( 0, -1 ); */
653 polygon << QPointF( -0.8660, 0.5 )
654 << QPointF( 0.8660, 0.5 )
655 << QPointF( 0, -1 )
656 << QPointF( -0.8660, 0.5 );
657 return true;
658
660 polygon << QPointF( 0, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( 0, 1 );
661 return true;
662
664 polygon << QPointF( -1, 1 ) << QPointF( 0, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
665 return true;
666
668 {
669 const double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
670
671 polygon << QPointF( inner_r * std::sin( DEG2RAD( 315.0 ) ), - inner_r * std::cos( DEG2RAD( 315.0 ) ) )
672 << QPointF( std::sin( DEG2RAD( 270 ) ), - std::cos( DEG2RAD( 270 ) ) )
673 << QPointF( inner_r * std::sin( DEG2RAD( 225.0 ) ), - inner_r * std::cos( DEG2RAD( 225.0 ) ) )
674 << QPointF( std::sin( DEG2RAD( 180 ) ), - std::cos( DEG2RAD( 180 ) ) )
675 << QPointF( inner_r * std::sin( DEG2RAD( 135.0 ) ), - inner_r * std::cos( DEG2RAD( 135.0 ) ) )
676 << QPointF( std::sin( DEG2RAD( 90 ) ), - std::cos( DEG2RAD( 90 ) ) )
677 << QPointF( inner_r * std::sin( DEG2RAD( 45.0 ) ), - inner_r * std::cos( DEG2RAD( 45.0 ) ) )
678 << QPointF( std::sin( DEG2RAD( 0 ) ), - std::cos( DEG2RAD( 0 ) ) );
679 return true;
680 }
681
683 {
684 const double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
685
686 polygon << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ) // 324
687 << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288 ) ) ) // 288
688 << QPointF( inner_r * std::sin( DEG2RAD( 252.0 ) ), - inner_r * std::cos( DEG2RAD( 252.0 ) ) ) // 252
689 << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) ) // 216
690 << QPointF( 0, inner_r ) // 180
691 << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) ) // 144
692 << QPointF( inner_r * std::sin( DEG2RAD( 108.0 ) ), - inner_r * std::cos( DEG2RAD( 108.0 ) ) ) // 108
693 << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) ) // 72
694 << QPointF( inner_r * std::sin( DEG2RAD( 36.0 ) ), - inner_r * std::cos( DEG2RAD( 36.0 ) ) ) // 36
695 << QPointF( 0, -1 )
696 << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ); // 324; // 0
697 return true;
698 }
699
701 polygon << QPointF( 0, -1 )
702 << QPointF( 0.5, -0.5 )
703 << QPointF( 0.25, -0.5 )
704 << QPointF( 0.25, 1 )
705 << QPointF( -0.25, 1 )
706 << QPointF( -0.25, -0.5 )
707 << QPointF( -0.5, -0.5 )
708 << QPointF( 0, -1 );
709 return true;
710
712 polygon << QPointF( 0, 0 ) << QPointF( -1, 1 ) << QPointF( -1, -1 ) << QPointF( 0, 0 );
713 return true;
714
716 polygon << QPointF( -1, -0.2 )
717 << QPointF( -1, -0.2 )
718 << QPointF( -1, 0.2 )
719 << QPointF( -0.2, 0.2 )
720 << QPointF( -0.2, 1 )
721 << QPointF( 0.2, 1 )
722 << QPointF( 0.2, 0.2 )
723 << QPointF( 1, 0.2 )
724 << QPointF( 1, -0.2 )
725 << QPointF( 0.2, -0.2 )
726 << QPointF( 0.2, -1 )
727 << QPointF( -0.2, -1 )
728 << QPointF( -0.2, -0.2 )
729 << QPointF( -1, -0.2 );
730 return true;
731
733 {
734 static constexpr double THICKNESS = 0.3;
735 static constexpr double HALF_THICKNESS = THICKNESS / 2.0;
736 static constexpr double INTERSECTION_POINT = THICKNESS / M_SQRT2;
737 static constexpr double DIAGONAL1 = M_SQRT1_2 - INTERSECTION_POINT * 0.5;
738 static constexpr double DIAGONAL2 = M_SQRT1_2 + INTERSECTION_POINT * 0.5;
739
740 polygon << QPointF( -HALF_THICKNESS, -1 )
741 << QPointF( HALF_THICKNESS, -1 )
742 << QPointF( HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
743 << QPointF( DIAGONAL1, -DIAGONAL2 )
744 << QPointF( DIAGONAL2, -DIAGONAL1 )
745 << QPointF( HALF_THICKNESS + INTERSECTION_POINT, -HALF_THICKNESS )
746 << QPointF( 1, -HALF_THICKNESS )
747 << QPointF( 1, HALF_THICKNESS )
748 << QPointF( HALF_THICKNESS + INTERSECTION_POINT, HALF_THICKNESS )
749 << QPointF( DIAGONAL2, DIAGONAL1 )
750 << QPointF( DIAGONAL1, DIAGONAL2 )
751 << QPointF( HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
752 << QPointF( HALF_THICKNESS, 1 )
753 << QPointF( -HALF_THICKNESS, 1 )
754 << QPointF( -HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
755 << QPointF( -DIAGONAL1, DIAGONAL2 )
756 << QPointF( -DIAGONAL2, DIAGONAL1 )
757 << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, HALF_THICKNESS )
758 << QPointF( -1, HALF_THICKNESS )
759 << QPointF( -1, -HALF_THICKNESS )
760 << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, -HALF_THICKNESS )
761 << QPointF( -DIAGONAL2, -DIAGONAL1 )
762 << QPointF( -DIAGONAL1, -DIAGONAL2 )
763 << QPointF( -HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
764 << QPointF( -HALF_THICKNESS, -1 );
765 return true;
766 }
767
781 return false;
782 }
783
784 return false;
785}
786
788{
789 mPath = QPainterPath();
790
791 switch ( symbol )
792 {
794
795 mPath.addEllipse( QRectF( -1, -1, 2, 2 ) ); // x,y,w,h
796 return true;
797
799 mPath.moveTo( -1, -1 );
800 mPath.addRoundedRect( -1, -1, 2, 2, 0.25, 0.25 );
801 return true;
802
804 mPath.arcTo( -1, -1, 2, 2, 0, 180 );
805 mPath.lineTo( 0, 0 );
806 return true;
807
809 mPath.arcTo( -1, -1, 2, 2, 90, 120 );
810 mPath.lineTo( 0, 0 );
811 return true;
812
814 mPath.arcTo( -1, -1, 2, 2, 90, 90 );
815 mPath.lineTo( 0, 0 );
816 return true;
817
819 mPath.moveTo( 1, 0 );
820 mPath.arcTo( -1, -1, 2, 2, 0, 180 );
821 return true;
822
824 mPath.moveTo( 0, -1 );
825 mPath.arcTo( -1, -1, 2, 2, 90, 120 );
826 return true;
827
829 mPath.moveTo( 0, -1 );
830 mPath.arcTo( -1, -1, 2, 2, 90, 90 );
831 return true;
832
834 mPath.moveTo( -1, 0 );
835 mPath.lineTo( 1, 0 ); // horizontal
836 mPath.moveTo( 0, -1 );
837 mPath.lineTo( 0, 1 ); // vertical
838 return true;
839
841 mPath.moveTo( -1, -1 );
842 mPath.lineTo( 1, 1 );
843 mPath.moveTo( 1, -1 );
844 mPath.lineTo( -1, 1 );
845 return true;
846
848 mPath.moveTo( 0, -1 );
849 mPath.lineTo( 0, 1 ); // vertical line
850 return true;
851
853 mPath.moveTo( -1, -1 );
854 mPath.lineTo( 0, 0 );
855 mPath.lineTo( -1, 1 );
856 return true;
857
859 mPath.moveTo( 0, 0.75 );
860 mPath.arcTo( 0, -1, 1, 1, -45, 210 );
861 mPath.arcTo( -1, -1, 1, 1, 15, 210 );
862 mPath.lineTo( 0, 0.75 );
863 return true;
864
889 return false;
890 }
891 return false;
892}
893
894double QgsSimpleMarkerSymbolLayerBase::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
895{
896 double scaledSize = mSize;
897
899 bool ok = true;
900 if ( hasDataDefinedSize )
901 {
904 mSize, &ok );
905 }
906
907 if ( hasDataDefinedSize && ok )
908 {
909 switch ( mScaleMethod )
910 {
912 scaledSize = std::sqrt( scaledSize );
913 break;
915 break;
916 }
917 }
918
919 return scaledSize;
920}
921
922void QgsSimpleMarkerSymbolLayerBase::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle ) const
923{
924 //offset
925 double offsetX = 0;
926 double offsetY = 0;
927 markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
928 offset = QPointF( offsetX, offsetY );
929
930 hasDataDefinedRotation = false;
931 //angle
932 bool ok = true;
935 {
938
939 // If the expression evaluation was not successful, fallback to static value
940 if ( !ok )
942
943 hasDataDefinedRotation = true;
944 }
945
946 hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || hasDataDefinedRotation;
947
948 if ( hasDataDefinedRotation )
949 {
950 // For non-point markers, "dataDefinedRotation" means following the
951 // shape (shape-data defined). For them, "field-data defined" does
952 // not work at all. TODO: if "field-data defined" ever gets implemented
953 // we'll need a way to distinguish here between the two, possibly
954 // using another flag in renderHints()
955 const QgsFeature *f = context.feature();
956 if ( f )
957 {
958 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
959 {
960 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
961 angle += m2p.mapRotation();
962 }
963 }
964 }
965
966 if ( angle )
968}
969
970
971//
972// QgsSimpleMarkerSymbolLayer
973//
974
975QgsSimpleMarkerSymbolLayer::QgsSimpleMarkerSymbolLayer( Qgis::MarkerShape shape, double size, double angle, Qgis::ScaleMethod scaleMethod, const QColor &color, const QColor &strokeColor, Qt::PenJoinStyle penJoinStyle )
976 : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
977 , mStrokeColor( strokeColor )
978 , mPenJoinStyle( penJoinStyle )
979{
980 mColor = color;
981}
982
984
986{
994
995 if ( props.contains( QStringLiteral( "name" ) ) )
996 {
997 shape = decodeShape( props[QStringLiteral( "name" )].toString() );
998 }
999 if ( props.contains( QStringLiteral( "color" ) ) )
1000 color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )].toString() );
1001 if ( props.contains( QStringLiteral( "color_border" ) ) )
1002 {
1003 //pre 2.5 projects use "color_border"
1004 strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color_border" )].toString() );
1005 }
1006 else if ( props.contains( QStringLiteral( "outline_color" ) ) )
1007 {
1008 strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )].toString() );
1009 }
1010 else if ( props.contains( QStringLiteral( "line_color" ) ) )
1011 {
1012 strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )].toString() );
1013 }
1014 if ( props.contains( QStringLiteral( "joinstyle" ) ) )
1015 {
1016 penJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() );
1017 }
1018 if ( props.contains( QStringLiteral( "size" ) ) )
1019 size = props[QStringLiteral( "size" )].toDouble();
1020 if ( props.contains( QStringLiteral( "angle" ) ) )
1021 angle = props[QStringLiteral( "angle" )].toDouble();
1022 if ( props.contains( QStringLiteral( "scale_method" ) ) )
1023 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1024
1026 if ( props.contains( QStringLiteral( "offset" ) ) )
1027 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1028 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1029 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1030 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1031 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1032 if ( props.contains( QStringLiteral( "size_unit" ) ) )
1033 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1034 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1035 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1036
1037 if ( props.contains( QStringLiteral( "outline_style" ) ) )
1038 {
1039 m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "outline_style" )].toString() ) );
1040 }
1041 else if ( props.contains( QStringLiteral( "line_style" ) ) )
1042 {
1043 m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "line_style" )].toString() ) );
1044 }
1045 if ( props.contains( QStringLiteral( "outline_width" ) ) )
1046 {
1047 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
1048 }
1049 else if ( props.contains( QStringLiteral( "line_width" ) ) )
1050 {
1051 m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
1052 }
1053 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
1054 {
1055 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
1056 }
1057 if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
1058 {
1059 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
1060 }
1061 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
1062 {
1063 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
1064 }
1065
1066 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1067 {
1068 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1069 }
1070 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1071 {
1072 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1073 }
1074
1075 if ( props.contains( QStringLiteral( "cap_style" ) ) )
1076 {
1077 m->setPenCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( props[QStringLiteral( "cap_style" )].toString() ) );
1078 }
1079
1081
1082 return m;
1083}
1084
1085
1087{
1088 return QStringLiteral( "SimpleMarker" );
1089}
1090
1092{
1094
1095 QColor brushColor = mColor;
1096 QColor penColor = mStrokeColor;
1097
1098 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
1099 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
1100
1101 mBrush = QBrush( brushColor );
1102 mPen = QPen( penColor );
1103 mPen.setStyle( mStrokeStyle );
1104 mPen.setCapStyle( mPenCapStyle );
1105 mPen.setJoinStyle( mPenJoinStyle );
1107
1108 QColor selBrushColor = context.renderContext().selectionColor();
1109 QColor selPenColor = selBrushColor == mColor ? selBrushColor : mStrokeColor;
1110 if ( context.opacity() < 1 && !SELECTION_IS_OPAQUE )
1111 {
1112 selBrushColor.setAlphaF( context.opacity() );
1113 selPenColor.setAlphaF( context.opacity() );
1114 }
1115 mSelBrush = QBrush( selBrushColor );
1116 mSelPen = QPen( selPenColor );
1117 mSelPen.setStyle( mStrokeStyle );
1119
1121 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
1122
1123 // use caching only when:
1124 // - size, rotation, shape, color, stroke color is not data-defined
1125 // - drawing to screen (not printer)
1126 mUsingCache = !hasDataDefinedRotation && !hasDataDefinedSize && !context.renderContext().forceVectorOutput()
1130
1131 if ( mUsingCache )
1132 mCachedOpacity = context.opacity();
1133
1134 if ( !shapeIsFilled( mShape ) )
1135 {
1136 // some markers can't be drawn as a polygon (circle, cross)
1137 // For these set the selected stroke color to the selected color
1138 mSelPen.setColor( selBrushColor );
1139 }
1140
1141
1142 if ( mUsingCache )
1143 {
1144 if ( !prepareCache( context ) )
1145 {
1146 mUsingCache = false;
1147 }
1148 }
1149 else
1150 {
1151 mCache = QImage();
1152 mSelCache = QImage();
1153 }
1154}
1155
1156
1158{
1159 double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
1160 const double deviceRatio = context.renderContext().devicePixelRatio();
1162 {
1163 // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
1164 // and clamp it to a reasonable range. It's the best we can do in this situation!
1165 scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
1166 }
1167
1168 // take into account angle (which is not data-defined otherwise cache wouldn't be used)
1169 if ( !qgsDoubleNear( mAngle, 0.0 ) )
1170 {
1171 scaledSize = ( std::abs( std::sin( mAngle * M_PI / 180 ) ) + std::abs( std::cos( mAngle * M_PI / 180 ) ) ) * scaledSize;
1172 }
1173 // calculate necessary image size for the cache
1174 const double pw = static_cast< int >( std::round( ( ( qgsDoubleNear( mPen.widthF(), 0.0 ) ? 1 : mPen.widthF() * 4 ) + 1 ) ) ) / 2 * 2; // make even (round up); handle cosmetic pen
1175 const int imageSize = ( static_cast< int >( scaledSize ) + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width
1176 const double center = imageSize / 2.0;
1177 if ( imageSize * deviceRatio > MAXIMUM_CACHE_WIDTH )
1178 {
1179 return false;
1180 }
1181
1182 mCache = QImage( QSize( imageSize * deviceRatio,
1183 imageSize * deviceRatio ), QImage::Format_ARGB32_Premultiplied );
1184 mCache.setDevicePixelRatio( context.renderContext().devicePixelRatio() );
1185 mCache.setDotsPerMeterX( std::round( context.renderContext().scaleFactor() * 1000 ) );
1186 mCache.setDotsPerMeterY( std::round( context.renderContext().scaleFactor() * 1000 ) );
1187 mCache.fill( 0 );
1188
1189 const bool needsBrush = shapeIsFilled( mShape );
1190
1191 QPainter p;
1192 p.begin( &mCache );
1193 p.setRenderHint( QPainter::Antialiasing );
1194 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1195 p.setPen( mPen );
1196 p.translate( QPointF( center, center ) );
1197 drawMarker( &p, context );
1198 p.end();
1199
1200 // Construct the selected version of the Cache
1201
1202 const QColor selColor = context.renderContext().selectionColor();
1203
1204 mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
1205 mSelCache.fill( 0 );
1206
1207 p.begin( &mSelCache );
1208 p.setRenderHint( QPainter::Antialiasing );
1209 p.setBrush( needsBrush ? mSelBrush : Qt::NoBrush );
1210 p.setPen( mSelPen );
1211 p.translate( QPointF( center, center ) );
1212 drawMarker( &p, context );
1213 p.end();
1214
1215 // Check that the selected version is different. If not, then re-render,
1216 // filling the background with the selection color and using the normal
1217 // colors for the symbol .. could be ugly!
1218
1219 if ( mSelCache == mCache )
1220 {
1221 p.begin( &mSelCache );
1222 p.setRenderHint( QPainter::Antialiasing );
1223 p.fillRect( 0, 0, imageSize, imageSize, selColor );
1224 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1225 p.setPen( mPen );
1226 p.translate( QPointF( center, center ) );
1227 drawMarker( &p, context );
1228 p.end();
1229 }
1230
1231 return true;
1232}
1233
1234void QgsSimpleMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
1235{
1236 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1237 //of the rendered point!
1238
1239 QPainter *p = context.renderContext().painter();
1240 if ( !p )
1241 {
1242 return;
1243 }
1244
1245 QColor brushColor = mColor;
1246 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
1247 mBrush.setColor( brushColor );
1248
1249 QColor penColor = mStrokeColor;
1250 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
1251 mPen.setColor( penColor );
1252
1253 bool ok = true;
1255 {
1258 if ( ok )
1259 {
1260 c.setAlphaF( c.alphaF() * context.opacity() );
1261 mBrush.setColor( c );
1262 }
1263 }
1265 {
1268 if ( ok )
1269 {
1270 c.setAlphaF( c.alphaF() * context.opacity() );
1271 mPen.setColor( c );
1272 mSelPen.setColor( c );
1273 }
1274 }
1276 {
1279 if ( ok )
1280 {
1283 }
1284 }
1286 {
1289 if ( ok )
1290 {
1293 }
1294 }
1296 {
1299 if ( ok )
1300 {
1301 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1302 mSelPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1303 }
1304 }
1306 {
1308 const QString style = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyCapStyle, context.renderContext().expressionContext(), QString(), &ok );
1309 if ( ok )
1310 {
1311 mPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1312 mSelPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1313 }
1314 }
1315
1316 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
1317 if ( shapeIsFilled( shape ) )
1318 {
1319 p->setBrush( useSelectedColor ? mSelBrush : mBrush );
1320 }
1321 else
1322 {
1323 p->setBrush( Qt::NoBrush );
1324 }
1325 p->setPen( useSelectedColor ? mSelPen : mPen );
1326
1327 if ( !polygon.isEmpty() )
1328 p->drawPolygon( polygon );
1329 else
1330 p->drawPath( path );
1331}
1332
1334{
1335 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1336 //of the rendered point!
1337
1338 QPainter *p = context.renderContext().painter();
1339 if ( !p )
1340 {
1341 return;
1342 }
1343
1344 if ( mUsingCache && qgsDoubleNear( mCachedOpacity, context.opacity() ) )
1345 {
1346 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
1347 const QImage &img = useSelectedColor ? mSelCache : mCache;
1348 const double s = img.width() / img.devicePixelRatioF();
1349
1350 bool hasDataDefinedSize = false;
1351 const double scaledSize = calculateSize( context, hasDataDefinedSize );
1352
1353 bool hasDataDefinedRotation = false;
1354 QPointF offset;
1355 double angle = 0;
1356 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
1357
1358 p->drawImage( QRectF( point.x() - s / 2.0 + offset.x(),
1359 point.y() - s / 2.0 + offset.y(),
1360 s, s ), img );
1361 }
1362 else
1363 {
1365 }
1366}
1367
1369{
1370 QVariantMap map;
1371 map[QStringLiteral( "name" )] = encodeShape( mShape );
1372 map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
1373 map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
1374 map[QStringLiteral( "size" )] = QString::number( mSize );
1375 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1376 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1377 map[QStringLiteral( "angle" )] = QString::number( mAngle );
1378 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1379 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1380 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1381 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1382 map[QStringLiteral( "outline_style" )] = QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle );
1383 map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
1384 map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
1385 map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
1386 map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
1387 map[QStringLiteral( "cap_style" )] = QgsSymbolLayerUtils::encodePenCapStyle( mPenCapStyle );
1388 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1389 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1390 return map;
1391}
1392
1412
1413void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1414{
1415 // <Graphic>
1416 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
1417 element.appendChild( graphicElem );
1418
1420 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
1422
1423 // <Rotation>
1424 QString angleFunc;
1425
1427 {
1429 }
1430 else
1431 {
1432 bool ok;
1433 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
1434 if ( !ok )
1435 {
1436 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
1437 }
1438 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
1439 {
1440 angleFunc = QString::number( angle + mAngle );
1441 }
1442 }
1443
1444 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
1445
1446 // <Displacement>
1447 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
1449}
1450
1451QString QgsSimpleMarkerSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
1452{
1453 Q_UNUSED( mmScaleFactor )
1454 Q_UNUSED( mapUnitScaleFactor )
1455#if 0
1456 QString ogrType = "3"; //default is circle
1457 if ( mName == "square" )
1458 {
1459 ogrType = "5";
1460 }
1461 else if ( mName == "triangle" )
1462 {
1463 ogrType = "7";
1464 }
1465 else if ( mName == "star" )
1466 {
1467 ogrType = "9";
1468 }
1469 else if ( mName == "circle" )
1470 {
1471 ogrType = "3";
1472 }
1473 else if ( mName == "cross" )
1474 {
1475 ogrType = "0";
1476 }
1477 else if ( mName == "x" || mName == "cross2" )
1478 {
1479 ogrType = "1";
1480 }
1481 else if ( mName == "line" )
1482 {
1483 ogrType = "10";
1484 }
1485
1486 QString ogrString;
1487 ogrString.append( "SYMBOL(" );
1488 ogrString.append( "id:" );
1489 ogrString.append( '\"' );
1490 ogrString.append( "ogr-sym-" );
1491 ogrString.append( ogrType );
1492 ogrString.append( '\"' );
1493 ogrString.append( ",c:" );
1494 ogrString.append( mColor.name() );
1495 ogrString.append( ",o:" );
1496 ogrString.append( mStrokeColor.name() );
1497 ogrString.append( QString( ",s:%1mm" ).arg( mSize ) );
1498 ogrString.append( ')' );
1499 return ogrString;
1500#endif //0
1501
1502 QString ogrString;
1503 ogrString.append( "PEN(" );
1504 ogrString.append( "c:" );
1505 ogrString.append( mColor.name() );
1506 ogrString.append( ",w:" );
1507 ogrString.append( QString::number( mSize ) );
1508 ogrString.append( "mm" );
1509 ogrString.append( ")" );
1510 return ogrString;
1511}
1512
1514{
1515 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
1516
1517 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
1518 if ( graphicElem.isNull() )
1519 return nullptr;
1520
1521 QString name = QStringLiteral( "square" );
1522 QColor color, strokeColor;
1523 double strokeWidth, size;
1524 Qt::PenStyle strokeStyle;
1525
1527 return nullptr;
1528
1529 double angle = 0.0;
1530 QString angleFunc;
1531 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
1532 {
1533 bool ok;
1534 const double d = angleFunc.toDouble( &ok );
1535 if ( ok )
1536 angle = d;
1537 }
1538
1539 QPointF offset;
1541
1542 const Qgis::MarkerShape shape = decodeShape( name );
1543
1544 double scaleFactor = 1.0;
1545 const QString uom = element.attribute( QStringLiteral( "uom" ) );
1546 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
1547 size = size * scaleFactor;
1548 offset.setX( offset.x() * scaleFactor );
1549 offset.setY( offset.y() * scaleFactor );
1550
1552 m->setOutputUnit( sldUnitSize );
1553 m->setColor( color );
1555 m->setAngle( angle );
1556 m->setOffset( offset );
1559 return m;
1560}
1561
1563{
1564 Q_UNUSED( context )
1565
1566 if ( mPolygon.count() != 0 )
1567 {
1568 p->drawPolygon( mPolygon );
1569 }
1570 else
1571 {
1572 p->drawPath( mPath );
1573 }
1574}
1575
1576bool QgsSimpleMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
1577{
1578 //data defined size?
1579 double size = mSize;
1580
1581 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
1582
1583 //data defined size
1584 bool ok = true;
1585 if ( hasDataDefinedSize )
1586 {
1588
1589 if ( ok )
1590 {
1591 switch ( mScaleMethod )
1592 {
1594 size = std::sqrt( size );
1595 break;
1597 break;
1598 }
1599 }
1600 }
1601
1603 {
1604 size *= mmMapUnitScaleFactor;
1605 }
1606
1608 {
1610 }
1611 const double halfSize = size / 2.0;
1612
1613 //strokeWidth
1614 double strokeWidth = mStrokeWidth;
1615
1617 {
1620 }
1623 {
1625 }
1626
1627 //color
1628 QColor pc = mPen.color();
1629 QColor bc = mBrush.color();
1631 {
1634 }
1636 {
1639 }
1640
1641 //offset
1642 double offsetX = 0;
1643 double offsetY = 0;
1644 markerOffset( context, offsetX, offsetY );
1645 offsetX *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1646 offsetY *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1647
1648
1649 QPointF off( offsetX, offsetY );
1650
1651 //angle
1652 double angle = mAngle + mLineAngle;
1654 {
1657 }
1658
1661 {
1663 const QString shapeName = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyName, context.renderContext().expressionContext(), QString(), &ok );
1664 if ( ok )
1665 {
1666 shape = decodeShape( shapeName, &ok );
1667 if ( !ok )
1668 shape = mShape;
1669 }
1670 }
1671
1672 if ( angle )
1673 off = _rotatedOffset( off, angle );
1674
1676
1677 QTransform t;
1678 t.translate( shift.x() + off.x(), shift.y() - off.y() );
1679
1680 if ( !qgsDoubleNear( angle, 0.0 ) )
1681 t.rotate( -angle );
1682
1683 QPolygonF polygon;
1684 if ( shapeToPolygon( shape, polygon ) )
1685 {
1686 t.scale( halfSize, -halfSize );
1687
1688 polygon = t.map( polygon );
1689
1691 p.reserve( polygon.size() );
1692 for ( int i = 0; i < polygon.size(); i++ )
1693 {
1694 p << QgsPoint( polygon[i] );
1695 }
1696
1697 if ( mBrush.style() != Qt::NoBrush )
1698 e.writePolygon( QgsRingSequence() << p, layerName, QStringLiteral( "SOLID" ), bc );
1699 if ( mPen.style() != Qt::NoPen )
1700 e.writePolyline( p, layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1701 }
1702 else if ( shape == Qgis::MarkerShape::Circle )
1703 {
1704 shift += QPointF( off.x(), -off.y() );
1705 if ( mBrush.style() != Qt::NoBrush )
1706 e.writeFilledCircle( layerName, bc, QgsPoint( shift ), halfSize );
1707 if ( mPen.style() != Qt::NoPen )
1708 e.writeCircle( layerName, pc, QgsPoint( shift ), halfSize, QStringLiteral( "CONTINUOUS" ), strokeWidth );
1709 }
1710 else if ( shape == Qgis::MarkerShape::Line )
1711 {
1712 const QPointF pt1 = t.map( QPointF( 0, -halfSize ) );
1713 const QPointF pt2 = t.map( QPointF( 0, halfSize ) );
1714
1715 if ( mPen.style() != Qt::NoPen )
1716 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1717 }
1718 else if ( shape == Qgis::MarkerShape::Cross )
1719 {
1720 if ( mPen.style() != Qt::NoPen )
1721 {
1722 const QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
1723 const QPointF pt2 = t.map( QPointF( halfSize, 0 ) );
1724 const QPointF pt3 = t.map( QPointF( 0, -halfSize ) );
1725 const QPointF pt4 = t.map( QPointF( 0, halfSize ) );
1726
1727 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1728 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1729 }
1730 }
1731 else if ( shape == Qgis::MarkerShape::Cross2 )
1732 {
1733 if ( mPen.style() != Qt::NoPen )
1734 {
1735 const QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
1736 const QPointF pt2 = t.map( QPointF( halfSize, halfSize ) );
1737 const QPointF pt3 = t.map( QPointF( halfSize, -halfSize ) );
1738 const QPointF pt4 = t.map( QPointF( -halfSize, halfSize ) );
1739
1740 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1741 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1742 }
1743 }
1744 else if ( shape == Qgis::MarkerShape::ArrowHead )
1745 {
1746 if ( mPen.style() != Qt::NoPen )
1747 {
1748 const QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
1749 const QPointF pt2 = t.map( QPointF( 0, 0 ) );
1750 const QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
1751
1752 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1753 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1754 }
1755 }
1756 else
1757 {
1758 QgsDebugError( QStringLiteral( "Unsupported dxf marker name %1" ).arg( encodeShape( shape ) ) );
1759 return false;
1760 }
1761
1762 return true;
1763}
1764
1765
1771
1780
1786
1795
1802
1804{
1805 QRectF symbolBounds = QgsSimpleMarkerSymbolLayerBase::bounds( point, context );
1806
1807 // need to account for stroke width
1808 double penWidth = mStrokeWidth;
1809 bool ok = true;
1811 {
1814 if ( ok )
1815 {
1816 penWidth = strokeWidth;
1817 }
1818 }
1821 {
1824 if ( ok && strokeStyle == QLatin1String( "no" ) )
1825 {
1826 penWidth = 0.0;
1827 }
1828 }
1829 else if ( mStrokeStyle == Qt::NoPen )
1830 penWidth = 0;
1831
1832 //antialiasing, add 1 pixel
1833 penWidth += 1;
1834
1835 //extend bounds by pen width / 2.0
1836 symbolBounds.adjust( -penWidth / 2.0, -penWidth / 2.0,
1837 penWidth / 2.0, penWidth / 2.0 );
1838
1839 return symbolBounds;
1840}
1841
1842void QgsSimpleMarkerSymbolLayer::setColor( const QColor &color )
1843{
1844 if ( shapeIsFilled( mShape ) )
1845 {
1847 }
1848 else
1849 {
1851 }
1852}
1853
1855{
1856 if ( shapeIsFilled( mShape ) )
1857 {
1858 return fillColor();
1859 }
1860 else
1861 {
1862 return strokeColor();
1863 }
1864}
1865
1866
1867
1868
1869//
1870// QgsFilledMarkerSymbolLayer
1871//
1872
1874 : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
1875{
1876 mFill.reset( static_cast<QgsFillSymbol *>( QgsFillSymbol::createSimple( QVariantMap() ) ) );
1877}
1878
1880
1882{
1883 QString name = DEFAULT_SIMPLEMARKER_NAME;
1887
1888 if ( props.contains( QStringLiteral( "name" ) ) )
1889 name = props[QStringLiteral( "name" )].toString();
1890 if ( props.contains( QStringLiteral( "size" ) ) )
1891 size = props[QStringLiteral( "size" )].toDouble();
1892 if ( props.contains( QStringLiteral( "angle" ) ) )
1893 angle = props[QStringLiteral( "angle" )].toDouble();
1894 if ( props.contains( QStringLiteral( "scale_method" ) ) )
1895 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1896
1898 if ( props.contains( QStringLiteral( "offset" ) ) )
1899 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1900 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1901 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1902 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1903 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1904 if ( props.contains( QStringLiteral( "size_unit" ) ) )
1905 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1906 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1907 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1908 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1909 {
1910 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1911 }
1912 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1913 {
1914 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1915 }
1916
1918
1920
1921 return m;
1922}
1923
1925{
1926 return QStringLiteral( "FilledMarker" );
1927}
1928
1930{
1931 if ( mFill )
1932 {
1933 mFill->startRender( context.renderContext(), context.fields() );
1934 }
1935
1937}
1938
1940{
1941 if ( mFill )
1942 {
1943 mFill->stopRender( context.renderContext() );
1944 }
1945}
1946
1948{
1949 QVariantMap map;
1950 map[QStringLiteral( "name" )] = encodeShape( mShape );
1951 map[QStringLiteral( "size" )] = QString::number( mSize );
1952 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1953 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1954 map[QStringLiteral( "angle" )] = QString::number( mAngle );
1955 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1956 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1957 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1958 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1959 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1960 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1961
1962 if ( mFill )
1963 {
1964 map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mFill->color() );
1965 }
1966 return map;
1967}
1968
1977
1979{
1980 return mFill.get();
1981}
1982
1984{
1985 if ( symbol && symbol->type() == Qgis::SymbolType::Fill )
1986 {
1987 mFill.reset( static_cast<QgsFillSymbol *>( symbol ) );
1988 return true;
1989 }
1990 else
1991 {
1992 delete symbol;
1993 return false;
1994 }
1995}
1996
1998{
1999 if ( mFill )
2000 {
2001 return QgsSymbolLayerUtils::estimateMaxSymbolBleed( mFill.get(), context );
2002 }
2003 return 0;
2004}
2005
2007{
2008 QSet<QString> attr = QgsSimpleMarkerSymbolLayerBase::usedAttributes( context );
2009 if ( mFill )
2010 attr.unite( mFill->usedAttributes( context ) );
2011 return attr;
2012}
2013
2015{
2017 return true;
2018 if ( mFill && mFill->hasDataDefinedProperties() )
2019 return true;
2020 return false;
2021}
2022
2024{
2025 mColor = c;
2026 if ( mFill )
2027 mFill->setColor( c );
2028}
2029
2031{
2032 return mFill ? mFill->color() : mColor;
2033}
2034
2041
2043{
2045 if ( mFill )
2046 mFill->setOutputUnit( unit );
2047}
2048
2049void QgsFilledMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
2050{
2051 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
2052 //of the rendered point!
2053
2054 QPainter *p = context.renderContext().painter();
2055 if ( !p )
2056 {
2057 return;
2058 }
2059
2060 const double prevOpacity = mFill->opacity();
2061 mFill->setOpacity( mFill->opacity() * context.opacity() );
2062
2063 if ( shapeIsFilled( shape ) )
2064 {
2065 p->setBrush( Qt::red );
2066 }
2067 else
2068 {
2069 p->setBrush( Qt::NoBrush );
2070 }
2071 p->setPen( Qt::black );
2072
2073 const bool prevIsSubsymbol = context.renderContext().flags() & Qgis::RenderContextFlag::RenderingSubSymbol;
2075
2076 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
2077 if ( !polygon.isEmpty() )
2078 {
2079 mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
2080 }
2081 else
2082 {
2083 const QPolygonF poly = path.toFillPolygon();
2084 mFill->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
2085 }
2086
2088
2089 mFill->setOpacity( prevOpacity );
2090}
2091
2092
2094
2095
2096QgsSvgMarkerSymbolLayer::QgsSvgMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
2097{
2098 mSize = size;
2099 mAngle = angle;
2100 mOffset = QPointF( 0, 0 );
2102 mStrokeWidth = 0.2;
2104 mColor = QColor( 35, 35, 35 );
2105 mStrokeColor = QColor( 35, 35, 35 );
2106 setPath( path );
2107}
2108
2110
2112{
2113 QString name;
2117
2118 if ( props.contains( QStringLiteral( "name" ) ) )
2119 name = props[QStringLiteral( "name" )].toString();
2120 if ( props.contains( QStringLiteral( "size" ) ) )
2121 size = props[QStringLiteral( "size" )].toDouble();
2122 if ( props.contains( QStringLiteral( "angle" ) ) )
2123 angle = props[QStringLiteral( "angle" )].toDouble();
2124 if ( props.contains( QStringLiteral( "scale_method" ) ) )
2125 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
2126
2128
2129 if ( props.contains( QStringLiteral( "size_unit" ) ) )
2130 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
2131 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
2132 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
2133 if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
2134 m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
2135 if ( props.contains( QStringLiteral( "offset" ) ) )
2136 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
2137 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
2138 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
2139 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2140 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
2141 if ( props.contains( QStringLiteral( "fill" ) ) )
2142 {
2143 //pre 2.5 projects used "fill"
2144 m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "fill" )].toString() ) );
2145 }
2146 else if ( props.contains( QStringLiteral( "color" ) ) )
2147 {
2148 m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )].toString() ) );
2149 }
2150 if ( props.contains( QStringLiteral( "outline" ) ) )
2151 {
2152 //pre 2.5 projects used "outline"
2153 m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline" )].toString() ) );
2154 }
2155 else if ( props.contains( QStringLiteral( "outline_color" ) ) )
2156 {
2157 m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )].toString() ) );
2158 }
2159 else if ( props.contains( QStringLiteral( "line_color" ) ) )
2160 {
2161 m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )].toString() ) );
2162 }
2163
2164 if ( props.contains( QStringLiteral( "outline-width" ) ) )
2165 {
2166 //pre 2.5 projects used "outline-width"
2167 m->setStrokeWidth( props[QStringLiteral( "outline-width" )].toDouble() );
2168 }
2169 else if ( props.contains( QStringLiteral( "outline_width" ) ) )
2170 {
2171 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
2172 }
2173 else if ( props.contains( QStringLiteral( "line_width" ) ) )
2174 {
2175 m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
2176 }
2177
2178 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
2179 {
2180 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
2181 }
2182 else if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
2183 {
2184 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
2185 }
2186 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
2187 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
2188
2189 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2190 {
2191 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2192 }
2193 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2194 {
2195 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2196 }
2197
2199
2201
2202 if ( props.contains( QStringLiteral( "parameters" ) ) )
2203 {
2204 const QVariantMap parameters = props[QStringLiteral( "parameters" )].toMap();
2206 }
2207
2208 return m;
2209}
2210
2211void QgsSvgMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
2212{
2213 const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
2214 if ( it != properties.end() )
2215 {
2216 if ( saving )
2217 {
2218 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
2219 }
2220 else
2221 {
2222 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
2223 }
2224 }
2225}
2226
2227void QgsSvgMarkerSymbolLayer::setPath( const QString &path )
2228{
2230 mHasFillParam = false;
2231 mPath = path;
2232 QColor defaultFillColor, defaultStrokeColor;
2233 double strokeWidth, fillOpacity, strokeOpacity;
2234 bool hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
2235 bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
2236 QgsApplication::svgCache()->containsParams( path, mHasFillParam, hasDefaultFillColor, defaultFillColor,
2237 hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
2238 hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2239 hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
2240 hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
2241
2242 const double newFillOpacity = hasFillOpacityParam ? fillColor().alphaF() : 1.0;
2243 const double newStrokeOpacity = hasStrokeOpacityParam ? strokeColor().alphaF() : 1.0;
2244
2245 if ( hasDefaultFillColor )
2246 {
2247 defaultFillColor.setAlphaF( newFillOpacity );
2248 setFillColor( defaultFillColor );
2249 }
2250 if ( hasDefaultFillOpacity )
2251 {
2252 QColor c = fillColor();
2253 c.setAlphaF( fillOpacity );
2254 setFillColor( c );
2255 }
2256 if ( hasDefaultStrokeColor )
2257 {
2258 defaultStrokeColor.setAlphaF( newStrokeOpacity );
2259 setStrokeColor( defaultStrokeColor );
2260 }
2261 if ( hasDefaultStrokeWidth )
2262 {
2264 }
2265 if ( hasDefaultStrokeOpacity )
2266 {
2267 QColor c = strokeColor();
2268 c.setAlphaF( strokeOpacity );
2269 setStrokeColor( c );
2270 }
2271
2273}
2274
2276{
2277 if ( mDefaultAspectRatio == 0.0 )
2278 {
2279 //size
2280 const double size = mSize;
2281 //assume 88 dpi as standard value
2282 const double widthScaleFactor = 3.465;
2283 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( mPath, size, mColor, mStrokeColor, mStrokeWidth, widthScaleFactor );
2284 // set default aspect ratio
2285 mDefaultAspectRatio = svgViewbox.isValid() ? svgViewbox.height() / svgViewbox.width() : 0.0;
2286 }
2287 return mDefaultAspectRatio;
2288}
2289
2291{
2292 const bool aPreservedAspectRatio = preservedAspectRatio();
2293 if ( aPreservedAspectRatio && !par )
2294 {
2296 }
2297 else if ( !aPreservedAspectRatio && par )
2298 {
2299 mFixedAspectRatio = 0.0;
2300 }
2301 return preservedAspectRatio();
2302}
2303
2304void QgsSvgMarkerSymbolLayer::setParameters( const QMap<QString, QgsProperty> &parameters )
2305{
2307}
2308
2309
2311{
2312 return QStringLiteral( "SvgMarker" );
2313}
2314
2316{
2317 QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
2318 Q_UNUSED( context )
2319}
2320
2322{
2323 Q_UNUSED( context )
2324}
2325
2327{
2328 QPainter *p = context.renderContext().painter();
2329 if ( !p )
2330 return;
2331
2332 bool hasDataDefinedSize = false;
2333 const double scaledWidth = calculateSize( context, hasDataDefinedSize );
2334 const double devicePixelRatio = context.renderContext().devicePixelRatio();
2335 const double width = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2336
2337 //don't render symbols with a width below one or above 10,000 pixels
2338 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2339 {
2340 return;
2341 }
2342
2343 const QgsScopedQPainterState painterState( p );
2344
2345 bool hasDataDefinedAspectRatio = false;
2346 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2347 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2348
2350
2351 double strokeWidth = mStrokeWidth;
2353 {
2356 }
2358
2359 QColor fillColor = mColor;
2360 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
2361 if ( useSelectedColor && mHasFillParam )
2362 {
2364 }
2366 {
2369 }
2370
2371 QColor strokeColor = mStrokeColor;
2373 {
2376 }
2377
2378 QString path = mPath;
2380 {
2383 context.renderContext().pathResolver() );
2385 {
2386 // adjust height of data defined path
2387 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2388 context.renderContext().scaleFactor(), aspectRatio,
2389 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2390 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2391 }
2392 }
2393
2394 QPointF outputOffset;
2395 double angle = 0.0;
2396 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2397
2398 p->translate( point + outputOffset );
2399
2400 const bool rotated = !qgsDoubleNear( angle, 0 );
2401 if ( rotated )
2402 p->rotate( angle );
2403
2404 bool fitsInCache = true;
2405 bool usePict = true;
2406 const bool rasterizeSelected = !mHasFillParam || mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyName );
2407 if ( ( !context.renderContext().forceVectorOutput() && !rotated ) || ( useSelectedColor && rasterizeSelected ) )
2408 {
2409 QImage img = QgsApplication::svgCache()->svgAsImage( path, width * devicePixelRatio, fillColor, strokeColor, strokeWidth,
2410 context.renderContext().scaleFactor(), fitsInCache, aspectRatio,
2411 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2412 if ( fitsInCache && img.width() > 1 )
2413 {
2414 usePict = false;
2415
2416 if ( useSelectedColor )
2417 {
2419 }
2420
2421 //consider transparency
2422 if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2423 {
2424 QImage transparentImage = img.copy();
2425 QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2426 if ( devicePixelRatio == 1 )
2427 {
2428 p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
2429 }
2430 else
2431 {
2432 p->drawImage( QRectF( -transparentImage.width() / 2.0 / devicePixelRatio, -transparentImage.height() / 2.0 / devicePixelRatio,
2433 transparentImage.width() / devicePixelRatio, transparentImage.height() / devicePixelRatio
2434 ), transparentImage );
2435 }
2436 }
2437 else
2438 {
2439 if ( devicePixelRatio == 1 )
2440 {
2441 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2442 }
2443 else
2444 {
2445 p->drawImage( QRectF( -img.width() / 2.0 / devicePixelRatio, -img.height() / 2.0 / devicePixelRatio,
2446 img.width() / devicePixelRatio, img.height() / devicePixelRatio ), img );
2447 }
2448 }
2449 }
2450 }
2451
2452 if ( usePict || !fitsInCache )
2453 {
2454 p->setOpacity( context.opacity() );
2456 context.renderContext().scaleFactor(), context.renderContext().forceVectorOutput(), aspectRatio,
2457 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2458 if ( pct.width() > 1 )
2459 {
2460 const QgsScopedQPainterState painterPictureState( p );
2461 _fixQPictureDPI( p );
2462 p->drawPicture( 0, 0, pct );
2463 }
2464 }
2465
2466 // workaround issue with nested QPictures forgetting antialiasing flag - see https://github.com/qgis/QGIS/issues/22909
2468}
2469
2470double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2471{
2472 double scaledSize = mSize;
2474
2475 bool ok = true;
2476 if ( hasDataDefinedSize )
2477 {
2480 }
2481 else
2482 {
2484 if ( hasDataDefinedSize )
2485 {
2488 }
2489 }
2490
2491 if ( hasDataDefinedSize && ok )
2492 {
2493 switch ( mScaleMethod )
2494 {
2496 scaledSize = std::sqrt( scaledSize );
2497 break;
2499 break;
2500 }
2501 }
2502
2503 return scaledSize;
2504}
2505
2506double QgsSvgMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2507{
2509 if ( !hasDataDefinedAspectRatio )
2510 return mFixedAspectRatio;
2511
2513 return 0.0;
2514
2515 double scaledAspectRatio = mDefaultAspectRatio;
2516 if ( mFixedAspectRatio > 0.0 )
2517 scaledAspectRatio = mFixedAspectRatio;
2518
2519 const double defaultHeight = mSize * scaledAspectRatio;
2520 scaledAspectRatio = defaultHeight / scaledSize;
2521
2522 bool ok = true;
2523 double scaledHeight = scaledSize * scaledAspectRatio;
2525 {
2526 context.setOriginalValueVariable( defaultHeight );
2528 }
2529
2530 if ( hasDataDefinedAspectRatio && ok )
2531 {
2532 switch ( mScaleMethod )
2533 {
2535 scaledHeight = sqrt( scaledHeight );
2536 break;
2538 break;
2539 }
2540 }
2541
2542 scaledAspectRatio = scaledHeight / scaledSize;
2543
2544 return scaledAspectRatio;
2545}
2546
2547void QgsSvgMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
2548{
2549 //offset
2550 double offsetX = 0;
2551 double offsetY = 0;
2552 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
2553 offset = QPointF( offsetX, offsetY );
2554
2557 {
2560 }
2561
2563 if ( hasDataDefinedRotation )
2564 {
2565 // For non-point markers, "dataDefinedRotation" means following the
2566 // shape (shape-data defined). For them, "field-data defined" does
2567 // not work at all. TODO: if "field-data defined" ever gets implemented
2568 // we'll need a way to distinguish here between the two, possibly
2569 // using another flag in renderHints()
2570 const QgsFeature *f = context.feature();
2571 if ( f )
2572 {
2573 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
2574 {
2575 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2576 angle += m2p.mapRotation();
2577 }
2578 }
2579 }
2580
2581 if ( angle )
2583}
2584
2585
2587{
2588 QVariantMap map;
2589 map[QStringLiteral( "name" )] = mPath;
2590 map[QStringLiteral( "size" )] = QString::number( mSize );
2591 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2592 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2593 map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2594 map[QStringLiteral( "angle" )] = QString::number( mAngle );
2595 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2596 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2597 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2598 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2599 map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
2600 map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
2601 map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
2602 map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2603 map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2604 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
2605 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
2606
2607 map[QStringLiteral( "parameters" )] = QgsProperty::propertyMapToVariantMap( mParameters );
2608
2609 return map;
2610}
2611
2618
2641
2647
2649{
2651 if ( unit != mStrokeWidthUnit )
2652 {
2654 }
2655 return unit;
2656}
2657
2663
2672
2673void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
2674{
2675 // <Graphic>
2676 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2677 element.appendChild( graphicElem );
2678
2679 // encode a parametric SVG reference
2680 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
2683
2684 // <Rotation>
2685 QString angleFunc;
2686 bool ok;
2687 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2688 if ( !ok )
2689 {
2690 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
2691 }
2692 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2693 {
2694 angleFunc = QString::number( angle + mAngle );
2695 }
2696
2697 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2698
2699 // <Displacement>
2700 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2702}
2703
2705{
2706 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
2707
2708 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
2709 if ( graphicElem.isNull() )
2710 return nullptr;
2711
2712 QString path, mimeType;
2713 // Unused and to be DEPRECATED in externalGraphicFromSld
2714 QColor fillColor_;
2715 double size;
2716
2717 if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor_, size ) )
2718 return nullptr;
2719
2720 double scaleFactor = 1.0;
2721 const QString uom = element.attribute( QStringLiteral( "uom" ) );
2722 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
2723 size = size * scaleFactor;
2724
2725 if ( mimeType != QLatin1String( "image/svg+xml" ) )
2726 return nullptr;
2727
2728 double angle = 0.0;
2729 QString angleFunc;
2730 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2731 {
2732 bool ok;
2733 const double d = angleFunc.toDouble( &ok );
2734 if ( ok )
2735 angle = d;
2736 }
2737
2738 QPointF offset;
2740
2741 // Extract parameters from URL
2742 QString realPath { path };
2743 QUrl svgUrl { path };
2744
2745 // Because color definition can start with '#', the url parsing won't recognize the query string entirely
2746 QUrlQuery queryString;
2747
2748 if ( svgUrl.hasQuery() && svgUrl.hasFragment() )
2749 {
2750 const QString queryPart { path.mid( path.indexOf( '?' ) + 1 ) };
2751 queryString.setQuery( queryPart );
2752 }
2753
2754 // Remove query for simple file paths
2755 if ( svgUrl.scheme().isEmpty() || svgUrl.isLocalFile() )
2756 {
2757 svgUrl.setQuery( QString() );
2758 realPath = svgUrl.path();
2759 }
2760
2762
2763 QMap<QString, QgsProperty> params;
2764
2765 bool ok;
2766
2767 if ( queryString.hasQueryItem( QStringLiteral( "fill" ) ) )
2768 {
2769 const QColor fillColor { queryString.queryItemValue( QStringLiteral( "fill" ) ) };
2770 m->setFillColor( fillColor );
2771 }
2772
2773 if ( queryString.hasQueryItem( QStringLiteral( "fill-opacity" ) ) )
2774 {
2775 const double alpha { queryString.queryItemValue( QStringLiteral( "fill-opacity" ) ).toDouble( &ok ) };
2776 if ( ok )
2777 {
2778 params.insert( QStringLiteral( "fill-opacity" ), QgsProperty::fromValue( alpha ) );
2779 }
2780 }
2781
2782 if ( queryString.hasQueryItem( QStringLiteral( "outline" ) ) )
2783 {
2784 const QColor strokeColor { queryString.queryItemValue( QStringLiteral( "outline" ) ) };
2786 }
2787
2788 if ( queryString.hasQueryItem( QStringLiteral( "outline-opacity" ) ) )
2789 {
2790 const double alpha { queryString.queryItemValue( QStringLiteral( "outline-opacity" ) ).toDouble( &ok ) };
2791 if ( ok )
2792 {
2793 params.insert( QStringLiteral( "outline-opacity" ), QgsProperty::fromValue( alpha ) );
2794 }
2795 }
2796
2797 if ( queryString.hasQueryItem( QStringLiteral( "outline-width" ) ) )
2798 {
2799 const int width { queryString.queryItemValue( QStringLiteral( "outline-width" ) ).toInt( &ok )};
2800 if ( ok )
2801 {
2802 m->setStrokeWidth( width );
2803 }
2804 }
2805
2806 if ( ! params.isEmpty() )
2807 {
2808 m->setParameters( params );
2809 }
2810
2811 m->setOutputUnit( sldUnitSize );
2812 m->setAngle( angle );
2813 m->setOffset( offset );
2814 return m;
2815}
2816
2817bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
2818{
2819 //size
2820 double size = mSize;
2821
2822 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
2823
2824 bool ok = true;
2825 if ( hasDataDefinedSize )
2826 {
2829 }
2830
2831 if ( hasDataDefinedSize && ok )
2832 {
2833 switch ( mScaleMethod )
2834 {
2836 size = std::sqrt( size );
2837 break;
2839 break;
2840 }
2841 }
2842
2844 {
2845 size *= mmMapUnitScaleFactor;
2846 }
2847
2848 //offset, angle
2849 QPointF offset = mOffset;
2850
2852 {
2854 const QVariant val = mDataDefinedProperties.value( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext(), QString() );
2855 const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
2856 if ( ok )
2857 offset = res;
2858 }
2859 const double offsetX = offset.x();
2860 const double offsetY = offset.y();
2861
2862 QPointF outputOffset( offsetX, offsetY );
2863
2864 double angle = mAngle + mLineAngle;
2866 {
2869 }
2870
2871 if ( angle )
2872 outputOffset = _rotatedOffset( outputOffset, angle );
2873
2875
2876 QString path = mPath;
2878 {
2881 context.renderContext().pathResolver() );
2882 }
2883
2884 double strokeWidth = mStrokeWidth;
2886 {
2889 }
2891
2892 QColor fillColor = mColor;
2894 {
2897 }
2898
2899 QColor strokeColor = mStrokeColor;
2901 {
2904 }
2905
2907
2908 const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, size, fillColor, strokeColor, strokeWidth,
2910 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2911
2912 QSvgRenderer r( svgContent );
2913 if ( !r.isValid() )
2914 return false;
2915
2916 QgsDxfPaintDevice pd( &e );
2917 pd.setDrawingSize( QSizeF( r.defaultSize() ) );
2918
2919 QSizeF outSize( r.defaultSize() );
2920 outSize.scale( size, size, Qt::KeepAspectRatio );
2921
2922 QPainter p;
2923 p.begin( &pd );
2924 if ( !qgsDoubleNear( angle, 0.0 ) )
2925 {
2926 p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
2927 p.rotate( angle );
2928 p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
2929 }
2930 pd.setShift( shift + QPointF( outputOffset.x(), -outputOffset.y() ) );
2931 pd.setOutputSize( QRectF( -outSize.width() / 2.0, -outSize.height() / 2.0, outSize.width(), outSize.height() ) );
2932 pd.setLayer( layerName );
2933 r.render( &p );
2934 p.end();
2935 return true;
2936}
2937
2939{
2940 bool hasDataDefinedSize = false;
2941 double scaledWidth = calculateSize( context, hasDataDefinedSize );
2942
2943 bool hasDataDefinedAspectRatio = false;
2944 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2945 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2946
2947 scaledWidth = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2948 scaledHeight = context.renderContext().convertToPainterUnits( scaledHeight, mSizeUnit, mSizeMapUnitScale );
2949
2950 //don't render symbols with size below one or above 10,000 pixels
2951 if ( static_cast< int >( scaledWidth ) < 1 || 10000.0 < scaledWidth )
2952 {
2953 return QRectF();
2954 }
2955
2956 QPointF outputOffset;
2957 double angle = 0.0;
2958 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2959
2960 double strokeWidth = mStrokeWidth;
2962 {
2965 }
2967
2968 QString path = mPath;
2970 {
2973 context.renderContext().pathResolver() );
2975 {
2976 // need to get colors to take advantage of cached SVGs
2977 QColor fillColor = mColor;
2979 {
2982 }
2983
2984 const QColor strokeColor = mStrokeColor;
2986 {
2989 }
2990
2992
2993 // adjust height of data defined path
2994 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2995 context.renderContext().scaleFactor(), aspectRatio,
2996 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2997 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2998 }
2999 }
3000
3001 QTransform transform;
3002 // move to the desired position
3003 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3004
3005 if ( !qgsDoubleNear( angle, 0.0 ) )
3006 transform.rotate( angle );
3007
3008 //antialiasing
3009 strokeWidth += 1.0 / 2.0;
3010
3011 QRectF symbolBounds = transform.mapRect( QRectF( -scaledWidth / 2.0,
3012 -scaledHeight / 2.0,
3013 scaledWidth,
3014 scaledHeight ) );
3015
3016 //extend bounds by pen width / 2.0
3017 symbolBounds.adjust( -strokeWidth / 2.0, -strokeWidth / 2.0,
3018 strokeWidth / 2.0, strokeWidth / 2.0 );
3019
3020 return symbolBounds;
3021}
3022
3024
3025QgsRasterMarkerSymbolLayer::QgsRasterMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
3026 : mPath( path )
3027{
3028 mSize = size;
3029 mAngle = angle;
3030 mOffset = QPointF( 0, 0 );
3033}
3034
3036
3038{
3039 QString path;
3043
3044 if ( props.contains( QStringLiteral( "imageFile" ) ) )
3045 path = props[QStringLiteral( "imageFile" )].toString();
3046 if ( props.contains( QStringLiteral( "size" ) ) )
3047 size = props[QStringLiteral( "size" )].toDouble();
3048 if ( props.contains( QStringLiteral( "angle" ) ) )
3049 angle = props[QStringLiteral( "angle" )].toDouble();
3050 if ( props.contains( QStringLiteral( "scale_method" ) ) )
3051 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
3052
3053 std::unique_ptr< QgsRasterMarkerSymbolLayer > m = std::make_unique< QgsRasterMarkerSymbolLayer >( path, size, angle, scaleMethod );
3054 m->setCommonProperties( props );
3055 return m.release();
3056}
3057
3058void QgsRasterMarkerSymbolLayer::setCommonProperties( const QVariantMap &properties )
3059{
3060 if ( properties.contains( QStringLiteral( "alpha" ) ) )
3061 {
3062 setOpacity( properties[QStringLiteral( "alpha" )].toDouble() );
3063 }
3064
3065 if ( properties.contains( QStringLiteral( "size_unit" ) ) )
3066 setSizeUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "size_unit" )].toString() ) );
3067 if ( properties.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3068 setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3069 if ( properties.contains( QStringLiteral( "fixedAspectRatio" ) ) )
3070 setFixedAspectRatio( properties[QStringLiteral( "fixedAspectRatio" )].toDouble() );
3071
3072 if ( properties.contains( QStringLiteral( "offset" ) ) )
3073 setOffset( QgsSymbolLayerUtils::decodePoint( properties[QStringLiteral( "offset" )].toString() ) );
3074 if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
3075 setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )].toString() ) );
3076 if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3077 setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3078
3079 if ( properties.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3080 {
3081 setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( properties[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3082 }
3083 if ( properties.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3084 {
3085 setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( properties[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3086 }
3087
3090}
3091
3092void QgsRasterMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
3093{
3094 const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
3095 if ( it != properties.end() && it.value().type() == QVariant::String )
3096 {
3097 if ( saving )
3098 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
3099 else
3100 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
3101 }
3102}
3103
3104void QgsRasterMarkerSymbolLayer::setPath( const QString &path )
3105{
3106 mPath = path;
3108}
3109
3111{
3112 const bool aPreservedAspectRatio = preservedAspectRatio();
3113 if ( aPreservedAspectRatio && !par )
3114 {
3116 }
3117 else if ( !aPreservedAspectRatio && par )
3118 {
3119 mFixedAspectRatio = 0.0;
3120 }
3121 return preservedAspectRatio();
3122}
3123
3125{
3126 if ( mDefaultAspectRatio == 0.0 )
3127 {
3129 mDefaultAspectRatio = ( !size.isNull() && size.isValid() && size.width() > 0 ) ? static_cast< double >( size.height() ) / static_cast< double >( size.width() ) : 0.0;
3130 }
3131 return mDefaultAspectRatio;
3132}
3133
3135{
3136 return QStringLiteral( "RasterMarker" );
3137}
3138
3140{
3141 QPainter *p = context.renderContext().painter();
3142 if ( !p )
3143 return;
3144
3145 QString path = mPath;
3147 {
3150 }
3151
3152 if ( path.isEmpty() )
3153 return;
3154
3155 double width = 0.0;
3156 double height = 0.0;
3157
3158 bool hasDataDefinedSize = false;
3159 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3160
3161 bool hasDataDefinedAspectRatio = false;
3162 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3163
3164 QPointF outputOffset;
3165 double angle = 0.0;
3166
3167 // RenderPercentage Unit Type takes original image size
3169 {
3171 if ( size.isEmpty() )
3172 return;
3173
3174 width = ( scaledSize * static_cast< double >( size.width() ) ) / 100.0;
3175 height = ( scaledSize * static_cast< double >( size.height() ) ) / 100.0;
3176
3177 // don't render symbols with size below one or above 10,000 pixels
3178 if ( static_cast< int >( width ) < 1 || 10000.0 < width || static_cast< int >( height ) < 1 || 10000.0 < height )
3179 return;
3180
3181 calculateOffsetAndRotation( context, width, height, outputOffset, angle );
3182 }
3183 else
3184 {
3185 width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3186 height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3187
3188 if ( preservedAspectRatio() && path != mPath )
3189 {
3191 if ( !size.isNull() && size.isValid() && size.width() > 0 )
3192 {
3193 height = width * ( static_cast< double >( size.height() ) / static_cast< double >( size.width() ) );
3194 }
3195 }
3196
3197 // don't render symbols with size below one or above 10,000 pixels
3198 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
3199 return;
3200
3201 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3202 }
3203
3204 const QgsScopedQPainterState painterState( p );
3205 p->translate( point + outputOffset );
3206
3207 const bool rotated = !qgsDoubleNear( angle, 0 );
3208 if ( rotated )
3209 p->rotate( angle );
3210
3211 double opacity = mOpacity;
3213 {
3216 }
3217 opacity *= context.opacity();
3218
3219 QImage img = fetchImage( context.renderContext(), path, QSize( width, preservedAspectRatio() ? 0 : width * aspectRatio ), preservedAspectRatio(), opacity );
3220 if ( !img.isNull() )
3221 {
3222 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
3223 if ( useSelectedColor )
3224 {
3226 }
3227
3228 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
3229 }
3230}
3231
3232QImage QgsRasterMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
3233{
3234 bool cached = false;
3235 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking );
3236}
3237
3238double QgsRasterMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
3239{
3240 double scaledSize = mSize;
3242
3243 bool ok = true;
3244 if ( hasDataDefinedSize )
3245 {
3248 }
3249 else
3250 {
3252 if ( hasDataDefinedSize )
3253 {
3256 }
3257 }
3258
3259 if ( hasDataDefinedSize && ok )
3260 {
3261 switch ( mScaleMethod )
3262 {
3264 scaledSize = std::sqrt( scaledSize );
3265 break;
3267 break;
3268 }
3269 }
3270
3271 return scaledSize;
3272}
3273
3274double QgsRasterMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
3275{
3277 if ( !hasDataDefinedAspectRatio )
3278 return mFixedAspectRatio;
3279
3281 return 0.0;
3282
3283 double scaledAspectRatio = mDefaultAspectRatio;
3284 if ( mFixedAspectRatio > 0.0 )
3285 scaledAspectRatio = mFixedAspectRatio;
3286
3287 const double defaultHeight = mSize * scaledAspectRatio;
3288 scaledAspectRatio = defaultHeight / scaledSize;
3289
3290 bool ok = true;
3291 double scaledHeight = scaledSize * scaledAspectRatio;
3293 {
3294 context.setOriginalValueVariable( defaultHeight );
3296 }
3297
3298 if ( hasDataDefinedAspectRatio && ok )
3299 {
3300 switch ( mScaleMethod )
3301 {
3303 scaledHeight = sqrt( scaledHeight );
3304 break;
3306 break;
3307 }
3308 }
3309
3310 scaledAspectRatio = scaledHeight / scaledSize;
3311
3312 return scaledAspectRatio;
3313}
3314
3315void QgsRasterMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
3316{
3317 //offset
3318 double offsetX = 0;
3319 double offsetY = 0;
3320 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
3321 offset = QPointF( offsetX, offsetY );
3322
3325 {
3328 }
3329
3331 if ( hasDataDefinedRotation )
3332 {
3333 const QgsFeature *f = context.feature();
3334 if ( f )
3335 {
3336 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3337 {
3338 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3339 angle += m2p.mapRotation();
3340 }
3341 }
3342 }
3343
3344 if ( angle )
3346}
3347
3348
3350{
3351 QVariantMap map;
3352 map[QStringLiteral( "imageFile" )] = mPath;
3353 map[QStringLiteral( "size" )] = QString::number( mSize );
3354 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3355 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3356 map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
3357 map[QStringLiteral( "angle" )] = QString::number( mAngle );
3358 map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
3359 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3360 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3361 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3362 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
3363 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3364 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3365 return map;
3366}
3367
3369{
3370 std::unique_ptr< QgsRasterMarkerSymbolLayer > m = std::make_unique< QgsRasterMarkerSymbolLayer >( mPath, mSize, mAngle );
3371 copyCommonProperties( m.get() );
3372 return m.release();
3373}
3374
3375
3390
3396
3398{
3399 return QColor();
3400}
3401
3406
3411
3413{
3414 bool hasDataDefinedSize = false;
3415 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3416 const double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3417 bool hasDataDefinedAspectRatio = false;
3418 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3419 const double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3420
3421 //don't render symbols with size below one or above 10,000 pixels
3422 if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
3423 {
3424 return QRectF();
3425 }
3426
3427 QPointF outputOffset;
3428 double angle = 0.0;
3429 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3430
3431 QTransform transform;
3432
3433 // move to the desired position
3434 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3435
3436 if ( !qgsDoubleNear( angle, 0.0 ) )
3437 transform.rotate( angle );
3438
3439 QRectF symbolBounds = transform.mapRect( QRectF( -width / 2.0,
3440 -height / 2.0,
3441 width,
3442 height ) );
3443
3444 return symbolBounds;
3445}
3446
3448
3449QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QString chr, double pointSize, const QColor &color, double angle )
3450{
3451 mFontFamily = fontFamily;
3452 mString = chr;
3453 mColor = color;
3454 mAngle = angle;
3455 mSize = pointSize;
3456 mOrigSize = pointSize;
3458 mOffset = QPointF( 0, 0 );
3460 mStrokeColor = DEFAULT_FONTMARKER_BORDERCOLOR;
3461 mStrokeWidth = 0.0;
3462 mStrokeWidthUnit = Qgis::RenderUnit::Millimeters;
3463 mPenJoinStyle = DEFAULT_FONTMARKER_JOINSTYLE;
3464}
3465
3467
3469{
3471 QString string = DEFAULT_FONTMARKER_CHR;
3472 double pointSize = DEFAULT_FONTMARKER_SIZE;
3475
3476 if ( props.contains( QStringLiteral( "font" ) ) )
3477 fontFamily = props[QStringLiteral( "font" )].toString();
3478 if ( props.contains( QStringLiteral( "chr" ) ) && props[QStringLiteral( "chr" )].toString().length() > 0 )
3479 string = props[QStringLiteral( "chr" )].toString();
3480 if ( props.contains( QStringLiteral( "size" ) ) )
3481 pointSize = props[QStringLiteral( "size" )].toDouble();
3482 if ( props.contains( QStringLiteral( "color" ) ) )
3483 color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )].toString() );
3484 if ( props.contains( QStringLiteral( "angle" ) ) )
3485 angle = props[QStringLiteral( "angle" )].toDouble();
3486
3488
3489 if ( props.contains( QStringLiteral( "font_style" ) ) )
3490 m->setFontStyle( props[QStringLiteral( "font_style" )].toString() );
3491 if ( props.contains( QStringLiteral( "outline_color" ) ) )
3492 m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )].toString() ) );
3493 if ( props.contains( QStringLiteral( "outline_width" ) ) )
3494 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
3495 if ( props.contains( QStringLiteral( "offset" ) ) )
3496 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
3497 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
3498 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
3499 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3500 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3501 if ( props.contains( QStringLiteral( "size_unit" ) ) )
3502 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
3503 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3504 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3505 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
3506 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
3507 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
3508 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
3509 if ( props.contains( QStringLiteral( "joinstyle" ) ) )
3510 m->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() ) );
3511 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3512 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3513 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3514 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3515
3517
3518 return m;
3519}
3520
3522{
3523 return QStringLiteral( "FontMarker" );
3524}
3525
3527{
3528 QColor brushColor = mColor;
3529 QColor penColor = mStrokeColor;
3530
3531 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
3532 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
3533
3534 mBrush = QBrush( brushColor );
3535 mPen = QPen( penColor );
3536 mPen.setJoinStyle( mPenJoinStyle );
3537 mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
3538
3539 mFont = QgsFontUtils::createFont( QgsApplication::fontManager()->processFontFamilyName( mFontFamily ) );
3540 if ( !mFontStyle.isEmpty() )
3541 {
3542 mFont.setStyleName( QgsFontUtils::translateNamedStyle( mFontStyle ) );
3543 }
3544
3545 double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3546 mNonZeroFontSize = !qgsDoubleNear( sizePixels, 0.0 );
3547
3548 if ( mNonZeroFontSize && sizePixels > MAX_FONT_CHARACTER_SIZE_IN_PIXELS )
3549 {
3550 // if font is too large (e.g using map units and map is very zoomed in), then we limit
3551 // the font size and instead scale up the painter.
3552 // this avoids issues with massive font sizes (eg https://github.com/qgis/QGIS/issues/42270)
3553 mFontSizeScale = sizePixels / MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3554 sizePixels = MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3555 }
3556 else
3557 mFontSizeScale = 1.0;
3558
3559 // if a non zero, but small pixel size results, round up to 2 pixels so that a "dot" is at least visible
3560 // (if we set a <=1 pixel size here Qt will reset the font to a default size, leading to much too large symbols)
3561 mFont.setPixelSize( std::max( 2, static_cast< int >( std::round( sizePixels ) ) ) );
3562 mFontMetrics.reset( new QFontMetrics( mFont ) );
3563 mChrWidth = mFontMetrics->horizontalAdvance( mString );
3564 mChrOffset = QPointF( mChrWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3565 mOrigSize = mSize; // save in case the size would be data defined
3566
3567 // use caching only when not using a data defined character
3571 if ( mUseCachedPath )
3572 {
3573 QPointF chrOffset = mChrOffset;
3574 double chrWidth;
3575 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3576 mCachedPath = QPainterPath();
3577 mCachedPath.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3578 }
3579}
3580
3582{
3583 Q_UNUSED( context )
3584}
3585
3586QString QgsFontMarkerSymbolLayer::characterToRender( QgsSymbolRenderContext &context, QPointF &charOffset, double &charWidth )
3587{
3588 charOffset = mChrOffset;
3589 QString stringToRender = mString;
3591 {
3592 context.setOriginalValueVariable( mString );
3594 if ( stringToRender != mString )
3595 {
3596 charWidth = mFontMetrics->horizontalAdvance( stringToRender );
3597 charOffset = QPointF( charWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3598 }
3599 }
3600 return stringToRender;
3601}
3602
3603void QgsFontMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
3604 double scaledSize,
3605 bool &hasDataDefinedRotation,
3606 QPointF &offset,
3607 double &angle ) const
3608{
3609 //offset
3610 double offsetX = 0;
3611 double offsetY = 0;
3612 markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
3613 offset = QPointF( offsetX, offsetY );
3614
3615 //angle
3616 bool ok = true;
3619 {
3622
3623 // If the expression evaluation was not successful, fallback to static value
3624 if ( !ok )
3626 }
3627
3628 hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation;
3629 if ( hasDataDefinedRotation )
3630 {
3631 // For non-point markers, "dataDefinedRotation" means following the
3632 // shape (shape-data defined). For them, "field-data defined" does
3633 // not work at all. TODO: if "field-data defined" ever gets implemented
3634 // we'll need a way to distinguish here between the two, possibly
3635 // using another flag in renderHints()
3636 const QgsFeature *f = context.feature();
3637 if ( f )
3638 {
3639 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3640 {
3641 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3642 angle += m2p.mapRotation();
3643 }
3644 }
3645 }
3646
3647 if ( angle )
3649}
3650
3651double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context )
3652{
3653 double scaledSize = mSize;
3654 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
3655
3656 bool ok = true;
3657 if ( hasDataDefinedSize )
3658 {
3661 }
3662
3663 if ( hasDataDefinedSize && ok )
3664 {
3665 switch ( mScaleMethod )
3666 {
3668 scaledSize = std::sqrt( scaledSize );
3669 break;
3671 break;
3672 }
3673 }
3674 return scaledSize;
3675}
3676
3678{
3679 QPainter *p = context.renderContext().painter();
3680 if ( !p || !mNonZeroFontSize )
3681 return;
3682
3683 QTransform transform;
3684
3685 bool ok;
3686 QColor brushColor = mColor;
3688 {
3691 }
3692 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
3693 brushColor = useSelectedColor ? context.renderContext().selectionColor() : brushColor;
3694 if ( !useSelectedColor || !SELECTION_IS_OPAQUE )
3695 {
3696 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
3697 }
3698 mBrush.setColor( brushColor );
3699
3700 QColor penColor = mStrokeColor;
3702 {
3705 }
3706 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
3707
3708 double penWidth = context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3710 {
3711 context.setOriginalValueVariable( mStrokeWidth );
3713 if ( ok )
3714 {
3715 penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3716 }
3717 }
3718
3720 {
3723 if ( ok )
3724 {
3725 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
3726 }
3727 }
3728
3729 const QgsScopedQPainterState painterState( p );
3730 p->setBrush( mBrush );
3731 if ( !qgsDoubleNear( penWidth, 0.0 ) )
3732 {
3733 mPen.setColor( penColor );
3734 mPen.setWidthF( penWidth );
3735 p->setPen( mPen );
3736 }
3737 else
3738 {
3739 p->setPen( Qt::NoPen );
3740 }
3741
3743 {
3744 context.setOriginalValueVariable( mFontFamily );
3746 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( ok ? fontFamily : mFontFamily );
3747 QgsFontUtils::setFontFamily( mFont, processedFamily );
3748 }
3750 {
3751 context.setOriginalValueVariable( mFontStyle );
3754 }
3756 {
3757 mFontMetrics.reset( new QFontMetrics( mFont ) );
3758 }
3759
3760 QPointF chrOffset = mChrOffset;
3761 double chrWidth;
3762 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3763
3764 const double sizeToRender = calculateSize( context );
3765
3766 bool hasDataDefinedRotation = false;
3767 QPointF offset;
3768 double angle = 0;
3769 calculateOffsetAndRotation( context, sizeToRender, hasDataDefinedRotation, offset, angle );
3770
3771 p->translate( point.x() + offset.x(), point.y() + offset.y() );
3772
3773 if ( !qgsDoubleNear( angle, 0.0 ) )
3774 transform.rotate( angle );
3775
3776 if ( !qgsDoubleNear( sizeToRender, mOrigSize ) )
3777 {
3778 const double s = sizeToRender / mOrigSize;
3779 transform.scale( s, s );
3780 }
3781
3782 if ( !qgsDoubleNear( mFontSizeScale, 1.0 ) )
3783 transform.scale( mFontSizeScale, mFontSizeScale );
3784
3785 if ( mUseCachedPath )
3786 {
3787 p->drawPath( transform.map( mCachedPath ) );
3788 }
3789 else
3790 {
3791 QPainterPath path;
3792 path.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3793 p->drawPath( transform.map( path ) );
3794 }
3795}
3796
3798{
3799 QVariantMap props;
3800 props[QStringLiteral( "font" )] = mFontFamily;
3801 props[QStringLiteral( "font_style" )] = mFontStyle;
3802 props[QStringLiteral( "chr" )] = mString;
3803 props[QStringLiteral( "size" )] = QString::number( mSize );
3804 props[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3805 props[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3806 props[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
3807 props[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
3808 props[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
3809 props[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
3810 props[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
3811 props[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
3812 props[QStringLiteral( "angle" )] = QString::number( mAngle );
3813 props[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3814 props[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3815 props[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3816 props[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3817 props[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3818 return props;
3819}
3820
3822{
3823 QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( mFontFamily, mString, mSize, mColor, mAngle );
3824 m->setFontStyle( mFontStyle );
3825 m->setStrokeColor( mStrokeColor );
3826 m->setStrokeWidth( mStrokeWidth );
3827 m->setStrokeWidthUnit( mStrokeWidthUnit );
3828 m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
3829 m->setPenJoinStyle( mPenJoinStyle );
3830 m->setOffset( mOffset );
3833 m->setSizeUnit( mSizeUnit );
3838 copyPaintEffect( m );
3839 return m;
3840}
3841
3842void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
3843{
3844 // <Graphic>
3845 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
3846 element.appendChild( graphicElem );
3847
3848 const QString fontPath = QStringLiteral( "ttf://%1" ).arg( mFontFamily );
3849 int markIndex = !mString.isEmpty() ? mString.at( 0 ).unicode() : 0;
3850 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
3851 QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, QStringLiteral( "ttf" ), &markIndex, mColor, size );
3852
3853 // <Rotation>
3854 QString angleFunc;
3855 bool ok;
3856 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
3857 if ( !ok )
3858 {
3859 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
3860 }
3861 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
3862 {
3863 angleFunc = QString::number( angle + mAngle );
3864 }
3865 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
3866
3867 // <Displacement>
3868 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
3870}
3871
3878
3880{
3882 mStrokeWidthUnit = unit;
3883}
3884
3886{
3887 QPointF chrOffset = mChrOffset;
3888 double chrWidth = mChrWidth;
3889 //calculate width of rendered character
3890 ( void )characterToRender( context, chrOffset, chrWidth );
3891
3892 if ( !mFontMetrics )
3893 mFontMetrics.reset( new QFontMetrics( mFont ) );
3894
3895 double scaledSize = calculateSize( context );
3896 if ( !qgsDoubleNear( scaledSize, mOrigSize ) )
3897 {
3898 chrWidth *= scaledSize / mOrigSize;
3899 }
3900 chrWidth *= mFontSizeScale;
3901
3902 bool hasDataDefinedRotation = false;
3903 QPointF offset;
3904 double angle = 0;
3905 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
3906 scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3907
3908 QTransform transform;
3909
3910 // move to the desired position
3911 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
3912
3913 if ( !qgsDoubleNear( angle, 0.0 ) )
3914 transform.rotate( angle );
3915
3916 QRectF symbolBounds = transform.mapRect( QRectF( -chrWidth / 2.0,
3917 -scaledSize / 2.0,
3918 chrWidth,
3919 scaledSize ) );
3920 return symbolBounds;
3921}
3922
3924{
3925 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
3926
3927 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
3928 if ( graphicElem.isNull() )
3929 return nullptr;
3930
3931 QString name, format;
3932 QColor color;
3933 double size;
3934 int chr;
3935
3936 if ( !QgsSymbolLayerUtils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
3937 return nullptr;
3938
3939 if ( !name.startsWith( QLatin1String( "ttf://" ) ) || format != QLatin1String( "ttf" ) )
3940 return nullptr;
3941
3942 const QString fontFamily = name.mid( 6 );
3943
3944 double angle = 0.0;
3945 QString angleFunc;
3946 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
3947 {
3948 bool ok;
3949 const double d = angleFunc.toDouble( &ok );
3950 if ( ok )
3951 angle = d;
3952 }
3953
3954 QPointF offset;
3956
3957 double scaleFactor = 1.0;
3958 const QString uom = element.attribute( QStringLiteral( "uom" ) );
3959 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
3960 offset.setX( offset.x() * scaleFactor );
3961 offset.setY( offset.y() * scaleFactor );
3962 size = size * scaleFactor;
3963
3965 m->setOutputUnit( sldUnitSize );
3966 m->setAngle( angle );
3967 m->setOffset( offset );
3968 return m;
3969}
3970
3971void QgsFontMarkerSymbolLayer::resolveFonts( const QVariantMap &properties, const QgsReadWriteContext &context )
3972{
3973 const QString fontFamily = properties.value( QStringLiteral( "font" ), DEFAULT_FONTMARKER_FONT ).toString();
3974 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( fontFamily );
3975 QString matched;
3976 if ( !QgsFontUtils::fontFamilyMatchOnSystem( processedFamily )
3977 && !QgsApplication::fontManager()->tryToDownloadFontFamily( processedFamily, matched ) )
3978 {
3979 context.pushMessage( QObject::tr( "Font “%1” not available on system" ).arg( processedFamily ) );
3980 }
3981}
3982
3984{
3985 QMap<QString, QgsProperty>::iterator it = mParameters.begin();
3986 for ( ; it != mParameters.end(); ++it )
3987 it.value().prepare( context.renderContext().expressionContext() );
3988
3990}
3991
3992
3994{
3995 QSet<QString> attrs = QgsMarkerSymbolLayer::usedAttributes( context );
3996
3997 QMap<QString, QgsProperty>::const_iterator it = mParameters.constBegin();
3998 for ( ; it != mParameters.constEnd(); ++it )
3999 {
4000 attrs.unite( it.value().referencedFields( context.expressionContext(), true ) );
4001 }
4002
4003 return attrs;
4004}
4005
4006//
4007// QgsAnimatedMarkerSymbolLayer
4008//
4009
4010QgsAnimatedMarkerSymbolLayer::QgsAnimatedMarkerSymbolLayer( const QString &path, double size, double angle )
4011 : QgsRasterMarkerSymbolLayer( path, size, angle )
4012{
4013
4014}
4015
4017
4019{
4020 QString path;
4023
4024 if ( properties.contains( QStringLiteral( "imageFile" ) ) )
4025 path = properties[QStringLiteral( "imageFile" )].toString();
4026 if ( properties.contains( QStringLiteral( "size" ) ) )
4027 size = properties[QStringLiteral( "size" )].toDouble();
4028 if ( properties.contains( QStringLiteral( "angle" ) ) )
4029 angle = properties[QStringLiteral( "angle" )].toDouble();
4030
4031 std::unique_ptr< QgsAnimatedMarkerSymbolLayer > m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( path, size, angle );
4032 m->setFrameRate( properties.value( QStringLiteral( "frameRate" ), QStringLiteral( "10" ) ).toDouble() );
4033
4034 m->setCommonProperties( properties );
4035 return m.release();
4036}
4037
4039{
4040 return QStringLiteral( "AnimatedMarker" );
4041}
4042
4044{
4045 QVariantMap res = QgsRasterMarkerSymbolLayer::properties();
4046 res.insert( QStringLiteral( "frameRate" ), mFrameRateFps );
4047 return res;
4048}
4049
4051{
4052 std::unique_ptr< QgsAnimatedMarkerSymbolLayer > m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( mPath, mSize, mAngle );
4053 m->setFrameRate( mFrameRateFps );
4054 copyCommonProperties( m.get() );
4055 return m.release();
4056}
4057
4059{
4061
4062 mPreparedPaths.clear();
4064 {
4066 mStaticPath = true;
4067 }
4068 else
4069 {
4070 mStaticPath = false;
4071 }
4072}
4073
4074QImage QgsAnimatedMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
4075{
4076 if ( !mStaticPath && !mPreparedPaths.contains( path ) )
4077 {
4079 mPreparedPaths.insert( path );
4080 }
4081
4082 const long long mapFrameNumber = context.currentFrame();
4084 const double markerAnimationDuration = totalFrameCount / mFrameRateFps;
4085
4086 double animationTimeSeconds = 0;
4087 if ( mapFrameNumber >= 0 && context.frameRate() > 0 )
4088 {
4089 // render is part of an animation, so we base the calculated frame on that
4090 animationTimeSeconds = mapFrameNumber / context.frameRate();
4091 }
4092 else
4093 {
4094 // render is outside of animation, so base the calculated frame on the current epoch
4095 animationTimeSeconds = QDateTime::currentMSecsSinceEpoch() / 1000.0;
4096 }
4097
4098 const double markerAnimationProgressSeconds = std::fmod( animationTimeSeconds, markerAnimationDuration );
4099 const int movieFrame = static_cast< int >( std::floor( markerAnimationProgressSeconds * mFrameRateFps ) );
4100
4101 bool cached = false;
4102 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking, 96, movieFrame );
4103}
4104
@ DynamicRotation
Rotation of symbol may be changed during rendering and symbol should not be cached.
ScaleMethod
Scale methods.
Definition qgis.h:382
@ ScaleDiameter
Calculate scale by the diameter.
@ ScaleArea
Calculate scale by the area.
MarkerShape
Marker shapes.
Definition qgis.h:2237
@ 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:3627
@ Percentage
Percentage of another measurement (e.g., canvas size, feature size)
@ Millimeters
Millimeters.
@ Unknown
Mixed or unknown units.
@ MapUnits
Map units.
@ MetersInMapUnits
Meters value as Map units.
@ RenderingSubSymbol
Set whenever a sub-symbol of a parent symbol is currently being rendered. Can be used during symbol a...
@ RenderSymbolPreview
The render is for a symbol preview only and map based properties may not be available,...
@ RenderBlocking
Render and load remote sources in the same thread to ensure rendering remote sources (svg and images)...
@ Fill
Fill symbol.
QColor valueAsColor(int key, const QgsExpressionContext &context, const QColor &defaultColor=QColor(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a color.
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double.
QString valueAsString(int key, const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a string.
Animated marker symbol layer class.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsAnimatedMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
~QgsAnimatedMarkerSymbolLayer() override
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
QImage fetchImage(QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity) const override
Fetches the image to render.
QgsAnimatedMarkerSymbolLayer(const QString &path=QString(), double size=DEFAULT_RASTERMARKER_SIZE, double angle=DEFAULT_RASTERMARKER_ANGLE)
Constructor for animated marker symbol layer using the specified source image path.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates an animated marker symbol layer from a string map of properties.
QString layerType() const override
Returns a string that represents this layer type.
static QgsImageCache * imageCache()
Returns the application's image cache, used for caching resampled versions of raster images.
static QgsSvgCache * svgCache()
Returns the application's SVG cache, used for caching SVG images and handling parameter replacement w...
static QgsFontManager * fontManager()
Returns the application font manager, which manages available fonts and font installation for the QGI...
Exports QGIS layers to the DXF format.
void writeFilledCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius)
Write filled circle (as hatch)
void writeCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius, const QString &lineStyleName, double width)
Write circle (as polyline)
static double mapUnitScaleFactor(double scale, Qgis::RenderUnit symbolUnits, Qgis::DistanceUnit mapUnits, double mapUnitsPerPixel=1.0)
Returns scale factor for conversion to map units.
void writeLine(const QgsPoint &pt1, const QgsPoint &pt2, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Write line (as a polyline)
void writePolygon(const QgsRingSequence &polygon, const QString &layer, const QString &hatchPattern, const QColor &color)
Draw dxf filled polygon (HATCH)
Qgis::DistanceUnit mapUnits() const
Retrieve map units.
double symbologyScale() const
Returns the reference scale for output.
void clipValueToMapUnitScale(double &value, const QgsMapUnitScale &scale, double pixelToMMFactor) const
Clips value to scale minimum/maximum.
void writePolyline(const QgsPointSequence &line, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Draw dxf primitives (LWPOLYLINE)
A paint device for drawing into dxf files.
void setShift(QPointF shift)
void setLayer(const QString &layer)
void setOutputSize(const QRectF &r)
void setDrawingSize(QSizeF size)
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:56
QgsGeometry geometry
Definition qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
static QgsFillSymbol * createSimple(const QVariantMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties.
Filled marker symbol layer, consisting of a shape which is rendered using a QgsFillSymbol.
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QColor color() const override
Returns the "representative" color of the symbol layer.
QgsFilledMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QgsSymbol * subSymbol() override
Returns the symbol's sub symbol, if present.
~QgsFilledMarkerSymbolLayer() override
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer's subsymbol. takes ownership of the passed symbol.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
void setColor(const QColor &c) override
Sets the "representative" color for the symbol layer.
QString layerType() const override
Returns a string that represents this layer type.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsFilledMarkerSymbolLayer.
QgsFilledMarkerSymbolLayer(Qgis::MarkerShape shape=Qgis::MarkerShape::Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructor for QgsFilledMarkerSymbolLayer.
QString processFontFamilyName(const QString &name) const
Processes a font family name, applying any matching fontFamilyReplacements() to the name.
void setStrokeWidthUnit(Qgis::RenderUnit unit)
Sets the stroke width unit.
~QgsFontMarkerSymbolLayer() override
void setStrokeColor(const QColor &color) override
Sets the stroke color for the symbol layer.
QgsFontMarkerSymbolLayer(const QString &fontFamily=DEFAULT_FONTMARKER_FONT, QString chr=DEFAULT_FONTMARKER_CHR, double pointSize=DEFAULT_FONTMARKER_SIZE, const QColor &color=DEFAULT_FONTMARKER_COLOR, double angle=DEFAULT_FONTMARKER_ANGLE)
Constructs a font marker symbol layer.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
Sets the stroke width map unit scale.
double strokeWidth() const
Returns the marker's stroke width.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
void setFontStyle(const QString &style)
Sets the font style for the font which will be used to render the point.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QString fontStyle() const
Returns the font style for the associated font which will be used to render the point.
QString fontFamily() const
Returns the font family name for the associated font which will be used to render the point.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
void writeSldMarker(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Writes the symbol layer definition as a SLD XML element.
void setStrokeWidth(double width)
Set's the marker's stroke width.
static void resolveFonts(const QVariantMap &properties, const QgsReadWriteContext &context)
Resolves fonts from a properties map, raising warnings in the specified context if the required fonts...
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
void setPenJoinStyle(Qt::PenJoinStyle style)
Sets the stroke join style.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsFontMarkerSymbolLayer from an SLD XML element.
QgsFontMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
QString layerType() const override
Returns a string that represents this layer type.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsFontMarkerSymbolLayer from a property map (see properties())
static QString translateNamedStyle(const QString &namedStyle)
Returns the localized named style of a font, if such a translation is available.
static QFont createFont(const QString &family, int pointSize=-1, int weight=-1, bool italic=false)
Creates a font with the specified family.
static bool fontFamilyMatchOnSystem(const QString &family, QString *chosen=nullptr, bool *match=nullptr)
Check whether font family is on system.
static bool updateFontViaStyle(QFont &f, const QString &fontstyle, bool fallback=false)
Updates font with named style and retain all font properties.
static void setFontFamily(QFont &font, const QString &family)
Sets the family for a font object.
Qgis::GeometryType type
QSize originalSize(const QString &path, bool blocking=false) const
Returns the original size (in pixels) of the image at the specified path.
int totalFrameCount(const QString &path, bool blocking=false)
Returns the total frame count of the image at the specified path.
QImage pathAsImage(const QString &path, const QSize size, const bool keepAspectRatio, const double opacity, bool &fitsInCache, bool blocking=false, double targetDpi=96, int frameNumber=-1, bool *isMissing=nullptr)
Returns the specified path rendered as an image.
void prepareAnimation(const QString &path)
Prepares for optimized retrieval of frames for the animation at the given path.
static void overlayColor(QImage &image, const QColor &color)
Overlays a color onto an image.
Perform transforms between map coordinates and device coordinates.
double mapUnitsPerPixel() const
Returns the current map units per pixel.
double mapRotation() const
Returns the current map rotation in degrees (clockwise).
Struct for storing maximum and minimum scales for measurements in map units.
Abstract base class for marker symbol layers.
double mSize
Marker size.
Qgis::RenderUnit mOffsetUnit
Offset units.
QPointF offset() const
Returns the marker's offset, which is the horizontal and vertical displacement which the rendered mar...
double mLineAngle
Line rotation angle (see setLineAngle() for details)
HorizontalAnchorPoint
Symbol horizontal anchor points.
void setOffsetUnit(Qgis::RenderUnit unit)
Sets the units for the symbol's offset.
void setAngle(double angle)
Sets the rotation angle for the marker.
Qgis::ScaleMethod scaleMethod() const
Returns the method to use for scaling the marker's size.
Qgis::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
void setVerticalAnchorPoint(VerticalAnchorPoint v)
Sets the vertical anchor point for positioning the symbol.
QPointF mOffset
Marker offset.
void setHorizontalAnchorPoint(HorizontalAnchorPoint h)
Sets the horizontal anchor point for positioning the symbol.
QgsMapUnitScale mapUnitScale() const override
void setOffset(QPointF offset)
Sets the marker's offset, which is the horizontal and vertical displacement which the rendered marker...
void setSizeMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol's size.
double size() const
Returns the symbol size.
QgsMapUnitScale mOffsetMapUnitScale
Offset map unit scale.
HorizontalAnchorPoint mHorizontalAnchorPoint
Horizontal anchor point.
static QPointF _rotatedOffset(QPointF offset, double angle)
Adjusts a marker offset to account for rotation.
Qgis::ScaleMethod mScaleMethod
Marker size scaling method.
QgsMapUnitScale mSizeMapUnitScale
Marker size map unit scale.
Qgis::RenderUnit mSizeUnit
Marker size unit.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units for the symbol's size.
VerticalAnchorPoint
Symbol vertical anchor points.
void markerOffset(QgsSymbolRenderContext &context, double &offsetX, double &offsetY) const
Calculates the required marker offset, including both the symbol offset and any displacement required...
VerticalAnchorPoint mVerticalAnchorPoint
Vertical anchor point.
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol's offset.
double mAngle
Marker rotation angle, in degrees clockwise from north.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
double angle() const
Returns the rotation angle for the marker, in degrees clockwise from north.
void setMapUnitScale(const QgsMapUnitScale &scale) override
Resolves relative paths into absolute paths and vice versa.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
QgsProperty property(int key) const override
Returns a matching property from the collection, if one exists.
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const override
Returns the calculated value of the property with the specified key from within the collection.
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
QString asExpression() const
Returns an expression string representing the state of the property, or an empty string if the proper...
static QVariantMap propertyMapToVariantMap(const QMap< QString, QgsProperty > &propertyMap)
Convert a map of QgsProperty to a map of QVariant This is useful to save a map of properties.
static QMap< QString, QgsProperty > variantMapToPropertyMap(const QVariantMap &variantMap)
Convert a map of QVariant to a map of QgsProperty This is useful to restore a map of properties.
static QgsProperty fromValue(const QVariant &value, bool isActive=true)
Returns a new StaticProperty created from the specified value.
Raster marker symbol layer class.
double mFixedAspectRatio
The marker fixed aspect ratio.
QColor color() const override
Returns the "representative" color of the symbol layer.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsMapUnitScale mapUnitScale() const override
void copyCommonProperties(QgsRasterMarkerSymbolLayer *other) const
Copies common properties to another layer.
void setOpacity(double opacity)
Set the marker opacity.
QString path() const
Returns the marker raster image path.
double calculateAspectRatio(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio) const
Calculates the marker aspect ratio between width and height.
QgsRasterMarkerSymbolLayer(const QString &path=QString(), double size=DEFAULT_SVGMARKER_SIZE, double angle=DEFAULT_SVGMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructs raster marker symbol layer with picture from given absolute path to a raster image file.
void setPath(const QString &path)
Set the marker raster image path.
virtual QImage fetchImage(QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity) const
Fetches the image to render.
double defaultAspectRatio() const
Returns the default marker aspect ratio between width and height, 0 if not yet calculated.
void setFixedAspectRatio(double ratio)
Set the marker aspect ratio between width and height to be used in rendering, if the value set is low...
void setMapUnitScale(const QgsMapUnitScale &scale) override
void setCommonProperties(const QVariantMap &properties)
Sets common class properties from a properties map.
QgsRasterMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
bool preservedAspectRatio() const
Returns the preserved aspect ratio value, true if fixed aspect ratio has been lower or equal to 0.
static void resolvePaths(QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving)
Turns relative paths in properties map to absolute when reading and vice versa when writing.
~QgsRasterMarkerSymbolLayer() override
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a raster marker symbol layer from a string map of properties.
double mOpacity
The marker default opacity.
double updateDefaultAspectRatio()
Calculates the default marker aspect ratio between width and height.
double mDefaultAspectRatio
The marker default aspect ratio.
bool setPreservedAspectRatio(bool par)
Set preserved the marker aspect ratio between width and height.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QString layerType() const override
Returns a string that represents this layer type.
double opacity() const
Returns the marker opacity.
The class is used as a container of context for various read/write operations on other objects.
void pushMessage(const QString &message, Qgis::MessageLevel level=Qgis::MessageLevel::Warning) const
Append a message to the context.
Contains information about the context of a rendering operation.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
double convertToPainterUnits(double size, Qgis::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QPainter * painter()
Returns the destination QPainter for the render operation.
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
QgsExpressionContext & expressionContext()
Gets the expression context.
bool forceVectorOutput() const
Returns true if rendering operations should use vector operations instead of any faster raster shortc...
long long currentFrame() const
Returns the current frame number of the map (in frames per second), for maps which are part of an ani...
float devicePixelRatio() const
Returns the device pixel ratio.
void setFlag(Qgis::RenderContextFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
double frameRate() const
Returns the frame rate of the map, for maps which are part of an animation.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
QColor selectionColor() const
Returns the color to use when rendering selected features.
Qgis::RenderContextFlags flags() const
Returns combination of flags used for rendering.
const QgsPathResolver & pathResolver() const
Returns the path resolver for conversion between relative and absolute paths during rendering operati...
Scoped object for saving and restoring a QPainter object's state.
Abstract base class for simple marker symbol layers.
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
void calculateOffsetAndRotation(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle) const
Calculates the marker offset and rotation.
Qgis::MarkerShape mShape
Symbol shape.
QPainterPath mPath
Painter path representing shape. If mPolygon is empty then the shape is stored in mPath.
bool shapeToPolygon(Qgis::MarkerShape shape, QPolygonF &polygon) const
Creates a polygon representing the specified shape.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
static QList< Qgis::MarkerShape > availableShapes()
Returns a list of all available shape types.
~QgsSimpleMarkerSymbolLayerBase() override
static bool shapeIsFilled(Qgis::MarkerShape shape)
Returns true if a symbol shape has a fill.
QPolygonF mPolygon
Polygon of points in shape. If polygon is empty then shape is using mPath.
Qgis::MarkerShape shape() const
Returns the shape for the rendered marker symbol.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
QgsSimpleMarkerSymbolLayerBase(Qgis::MarkerShape shape=Qgis::MarkerShape::Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructor for QgsSimpleMarkerSymbolLayerBase.
static QString encodeShape(Qgis::MarkerShape shape)
Encodes a shape to its string representation.
double calculateSize(QgsSymbolRenderContext &context, bool &hasDataDefinedSize) const
Calculates the desired size of the marker, considering data defined size overrides.
static Qgis::MarkerShape decodeShape(const QString &name, bool *ok=nullptr)
Attempts to decode a string representation of a shape name to the corresponding shape.
bool prepareMarkerPath(Qgis::MarkerShape symbol)
Prepares the layer for drawing the specified shape (QPainterPath version)
bool prepareMarkerShape(Qgis::MarkerShape shape)
Prepares the layer for drawing the specified shape (QPolygonF version)
Simple marker symbol layer, consisting of a rendered shape with solid fill color and an stroke.
QPen mSelPen
QPen to use as stroke of selected symbols.
void setColor(const QColor &color) override
Sets the "representative" color for the symbol layer.
QImage mSelCache
Cached image of selected marker, if using cached version.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QImage mCache
Cached image of marker, if using cached version.
QBrush mSelBrush
QBrush to use as fill of selected symbols.
void setFillColor(const QColor &color) override
Sets the fill color for the symbol layer.
Qt::PenJoinStyle penJoinStyle() const
Returns the marker's stroke join style (e.g., miter, bevel, etc).
void drawMarker(QPainter *p, QgsSymbolRenderContext &context)
Draws the marker shape in the specified painter.
QPen mPen
QPen corresponding to marker's stroke style.
Qgis::RenderUnit mStrokeWidthUnit
Stroke width units.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsSimpleMarkerSymbolLayer.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
void setMapUnitScale(const QgsMapUnitScale &scale) override
Qgis::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
void setStrokeWidthUnit(Qgis::RenderUnit u)
Sets the unit for the width of the marker's stroke.
QColor color() const override
Returns the "representative" color of the symbol layer.
QgsMapUnitScale mapUnitScale() const override
Qt::PenStyle mStrokeStyle
Stroke style.
QgsSimpleMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsSimpleMarkerSymbolLayer from an SLD XML element.
~QgsSimpleMarkerSymbolLayer() override
Qt::PenCapStyle mPenCapStyle
Stroke pen cap style.
QString layerType() const override
Returns a string that represents this layer type.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
Sets the map scale for the width of the marker's stroke.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
void setStrokeStyle(Qt::PenStyle strokeStyle)
Sets the marker's stroke style (e.g., solid, dashed, etc)
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QColor fillColor() const override
Returns the fill color for the symbol layer.
QColor strokeColor() const override
Returns the marker's stroke color.
QBrush mBrush
QBrush corresponding to marker's fill style.
void setStrokeWidth(double w)
Sets the width of the marker's stroke.
void setStrokeColor(const QColor &color) override
Sets the marker's stroke color.
Qt::PenStyle strokeStyle() const
Returns the marker's stroke style (e.g., solid, dashed, etc)
bool mUsingCache
true if using cached images of markers for drawing.
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
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.
bool shouldRenderUsingSelectionColor(const QgsSymbolRenderContext &context) const
Returns true if the symbol layer should be rendered using the selection color from the render context...
static const bool SELECTION_IS_OPAQUE
Whether styles for selected features ignore symbol alpha.
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns the set of attributes referenced by the layer.
void copyDataDefinedProperties(QgsSymbolLayer *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
void restoreOldDataDefinedProperties(const QVariantMap &stringMap)
Restores older data defined properties from string map.
virtual void startRender(QgsSymbolRenderContext &context)=0
Called before a set of rendering operations commences on the supplied render context.
virtual void prepareExpressions(const QgsSymbolRenderContext &context)
Prepares all data defined property expressions for evaluation.
virtual void setColor(const QColor &color)
Sets the "representative" color for the symbol layer.
virtual QColor color() const
Returns the "representative" color of the symbol layer.
virtual void setOutputUnit(Qgis::RenderUnit unit)
Sets the units to use for sizes and widths within the symbol layer.
void copyPaintEffect(QgsSymbolLayer *destLayer) const
Copies paint effect of this layer to another symbol layer.
QgsPropertyCollection mDataDefinedProperties
virtual bool hasDataDefinedProperties() const
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
const QgsFeature * feature() const
Returns the current feature being rendered.
QgsFields fields() const
Fields of the layer.
Qgis::SymbolRenderHints renderHints() const
Returns the rendering hint flags for the symbol.
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for data defined symbology.
qreal opacity() const
Returns the opacity for the symbol.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
Abstract base class for all rendered symbols.
Definition qgssymbol.h:94
Qgis::SymbolType type() const
Returns the symbol's type.
Definition qgssymbol.h:153
static Q_INVOKABLE Qgis::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
static bool isNull(const QVariant &variant)
Returns true if the specified variant should be considered a NULL value.
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:4332
QMap< QString, QString > QgsStringMap
Definition qgis.h:4877
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
#define DEG2RAD(x)
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
#define QgsDebugError(str)
Definition qgslogger.h:38
Q_GUI_EXPORT int qt_defaultDpiX()
Q_GUI_EXPORT int qt_defaultDpiY()
#define DEFAULT_FONTMARKER_JOINSTYLE
#define DEFAULT_RASTERMARKER_ANGLE
#define DEFAULT_RASTERMARKER_SIZE
#define DEFAULT_SVGMARKER_ANGLE
#define DEFAULT_SIMPLEMARKER_JOINSTYLE
#define DEFAULT_FONTMARKER_CHR
#define DEFAULT_SIMPLEMARKER_BORDERCOLOR
#define DEFAULT_SIMPLEMARKER_SIZE
#define DEFAULT_SIMPLEMARKER_NAME
#define DEFAULT_SIMPLEMARKER_ANGLE
#define DEFAULT_SVGMARKER_SIZE
#define DEFAULT_FONTMARKER_FONT
#define DEFAULT_FONTMARKER_BORDERCOLOR
#define DEFAULT_FONTMARKER_ANGLE
#define DEFAULT_FONTMARKER_COLOR
#define DEFAULT_FONTMARKER_SIZE
#define DEFAULT_SIMPLEMARKER_COLOR
#define DEFAULT_SCALE_METHOD