QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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 "qgsexpression.h"
22#include "qgsfontutils.h"
23#include "qgsimagecache.h"
24#include "qgsimageoperation.h"
25#include "qgsrendercontext.h"
26#include "qgslogger.h"
27#include "qgssvgcache.h"
28#include "qgsunittypes.h"
29#include "qgssymbol.h"
30#include "qgsfillsymbol.h"
31#include "qgsfontmanager.h"
32
33#include <QPainter>
34#include <QSvgRenderer>
35#include <QFileInfo>
36#include <QDir>
37#include <QDomDocument>
38#include <QDomElement>
39#include <QUrlQuery>
40
41#include <cmath>
42
43Q_GUI_EXPORT extern int qt_defaultDpiX();
44Q_GUI_EXPORT extern int qt_defaultDpiY();
45
46static constexpr int MAX_FONT_CHARACTER_SIZE_IN_PIXELS = 500;
47
48static void _fixQPictureDPI( QPainter *p )
49{
50 // QPicture makes an assumption that we drawing to it with system DPI.
51 // Then when being drawn, it scales the painter. The following call
52 // negates the effect. There is no way of setting QPicture's DPI.
53 // See QTBUG-20361
54 p->scale( static_cast< double >( qt_defaultDpiX() ) / p->device()->logicalDpiX(),
55 static_cast< double >( qt_defaultDpiY() ) / p->device()->logicalDpiY() );
56}
57
58
60
61
62//
63// QgsSimpleMarkerSymbolLayerBase
64//
65
67{
68 QList< Qgis::MarkerShape > shapes;
106
107 return shapes;
108}
109
111 : mShape( shape )
112{
113 mSize = size;
114 mAngle = angle;
115 mOffset = QPointF( 0, 0 );
119}
120
122
124{
125 switch ( shape )
126 {
157 return true;
158
166 return false;
167 }
168 return true;
169}
170
172{
173 const bool hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation
175 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
176
177 // use either QPolygonF or QPainterPath for drawing
178 if ( !prepareMarkerShape( mShape ) ) // drawing as a polygon
179 {
180 prepareMarkerPath( mShape ); // drawing as a painter path
181 }
182
183 QTransform transform;
184
185 // scale the shape (if the size is not going to be modified)
186 if ( !hasDataDefinedSize )
187 {
188 double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
190 {
191 // rendering for symbol previews -- an size in meters in map units can't be calculated, so treat the size as millimeters
192 // and clamp it to a reasonable range. It's the best we can do in this situation!
193 scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, QgsUnitTypes::RenderMillimeters ), 3.0 ), 100.0 );
194 }
195
196 const double half = scaledSize / 2.0;
197 transform.scale( half, half );
198 }
199
200 // rotate if the rotation is not going to be changed during the rendering
201 if ( !hasDataDefinedRotation && !qgsDoubleNear( mAngle, 0.0 ) )
202 {
203 transform.rotate( mAngle );
204 }
205
206 if ( !mPolygon.isEmpty() )
207 mPolygon = transform.map( mPolygon );
208 else
209 mPath = transform.map( mPath );
210
212}
213
215{
216 Q_UNUSED( context )
217}
218
220{
221 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
222 //of the rendered point!
223
224 QPainter *p = context.renderContext().painter();
225 if ( !p )
226 {
227 return;
228 }
229
230 bool hasDataDefinedSize = false;
231 const double scaledSize = calculateSize( context, hasDataDefinedSize );
232
233 bool hasDataDefinedRotation = false;
234 QPointF offset;
235 double angle = 0;
236 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
237
238 //data defined shape?
239 bool createdNewPath = false;
240 bool ok = true;
241 Qgis::MarkerShape symbol = mShape;
243 {
244 context.setOriginalValueVariable( encodeShape( symbol ) );
246 if ( !QgsVariantUtils::isNull( exprVal ) )
247 {
248 const Qgis::MarkerShape decoded = decodeShape( exprVal.toString(), &ok );
249 if ( ok )
250 {
251 symbol = decoded;
252
253 if ( !prepareMarkerShape( symbol ) ) // drawing as a polygon
254 {
255 prepareMarkerPath( symbol ); // drawing as a painter path
256 }
257 createdNewPath = true;
258 }
259 }
260 else
261 {
262 symbol = mShape;
263 }
264 }
265
266 QTransform transform;
267
268 // move to the desired position
269 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
270
271 // resize if necessary
272 if ( hasDataDefinedSize || createdNewPath )
273 {
274 double s = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
276 {
277 // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
278 // and clamp it to a reasonable range. It's the best we can do in this situation!
279 s = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, QgsUnitTypes::RenderMillimeters ), 3.0 ), 100.0 );
280 }
281 const double half = s / 2.0;
282 transform.scale( half, half );
283 }
284
285 if ( !qgsDoubleNear( angle, 0.0 ) && ( hasDataDefinedRotation || createdNewPath ) )
286 {
287 transform.rotate( angle );
288 }
289
290 //need to pass: symbol, polygon, path
291
292 QPolygonF polygon;
293 QPainterPath path;
294 if ( !mPolygon.isEmpty() )
295 {
296 polygon = transform.map( mPolygon );
297 }
298 else
299 {
300 path = transform.map( mPath );
301 }
302 draw( context, symbol, polygon, path );
303}
304
306{
307 bool hasDataDefinedSize = false;
308 double scaledSize = calculateSize( context, hasDataDefinedSize );
309
310 bool hasDataDefinedRotation = false;
311 QPointF offset;
312 double angle = 0;
313 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
314
315 scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
316
317 QTransform transform;
318
319 // move to the desired position
320 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
321
322 if ( !qgsDoubleNear( angle, 0.0 ) )
323 transform.rotate( angle );
324
325 return transform.mapRect( QRectF( -scaledSize / 2.0,
326 -scaledSize / 2.0,
327 scaledSize,
328 scaledSize ) );
329}
330
332{
333 if ( ok )
334 *ok = true;
335 const QString cleaned = name.toLower().trimmed();
336
337 if ( cleaned == QLatin1String( "square" ) || cleaned == QLatin1String( "rectangle" ) )
339 else if ( cleaned == QLatin1String( "trapezoid" ) )
341 else if ( cleaned == QLatin1String( "parallelogram_right" ) )
343 else if ( cleaned == QLatin1String( "parallelogram_left" ) )
345 else if ( cleaned == QLatin1String( "square_with_corners" ) )
347 else if ( cleaned == QLatin1String( "rounded_square" ) )
349 else if ( cleaned == QLatin1String( "diamond" ) )
351 else if ( cleaned == QLatin1String( "shield" ) )
353 else if ( cleaned == QLatin1String( "pentagon" ) )
355 else if ( cleaned == QLatin1String( "hexagon" ) )
357 else if ( cleaned == QLatin1String( "octagon" ) )
359 else if ( cleaned == QLatin1String( "decagon" ) )
361 else if ( cleaned == QLatin1String( "triangle" ) )
363 else if ( cleaned == QLatin1String( "equilateral_triangle" ) )
365 else if ( cleaned == QLatin1String( "star_diamond" ) )
367 else if ( cleaned == QLatin1String( "star" ) || cleaned == QLatin1String( "regular_star" ) )
369 else if ( cleaned == QLatin1String( "heart" ) )
371 else if ( cleaned == QLatin1String( "arrow" ) )
373 else if ( cleaned == QLatin1String( "circle" ) )
375 else if ( cleaned == QLatin1String( "cross" ) )
377 else if ( cleaned == QLatin1String( "cross_fill" ) )
379 else if ( cleaned == QLatin1String( "cross2" ) || cleaned == QLatin1String( "x" ) )
381 else if ( cleaned == QLatin1String( "line" ) )
383 else if ( cleaned == QLatin1String( "arrowhead" ) )
385 else if ( cleaned == QLatin1String( "filled_arrowhead" ) )
387 else if ( cleaned == QLatin1String( "semi_circle" ) )
389 else if ( cleaned == QLatin1String( "third_circle" ) )
391 else if ( cleaned == QLatin1String( "quarter_circle" ) )
393 else if ( cleaned == QLatin1String( "quarter_square" ) )
395 else if ( cleaned == QLatin1String( "half_square" ) )
397 else if ( cleaned == QLatin1String( "diagonal_half_square" ) )
399 else if ( cleaned == QLatin1String( "right_half_triangle" ) )
401 else if ( cleaned == QLatin1String( "left_half_triangle" ) )
403 else if ( cleaned == QLatin1String( "asterisk_fill" ) )
405 else if ( cleaned == QLatin1String( "half_arc" ) )
407 else if ( cleaned == QLatin1String( "third_arc" ) )
409 else if ( cleaned == QLatin1String( "quarter_arc" ) )
411
412 if ( ok )
413 *ok = false;
415}
416
418{
419 switch ( shape )
420 {
422 return QStringLiteral( "square" );
424 return QStringLiteral( "quarter_square" );
426 return QStringLiteral( "half_square" );
428 return QStringLiteral( "diagonal_half_square" );
430 return QStringLiteral( "parallelogram_right" );
432 return QStringLiteral( "parallelogram_left" );
434 return QStringLiteral( "trapezoid" );
436 return QStringLiteral( "shield" );
438 return QStringLiteral( "diamond" );
440 return QStringLiteral( "pentagon" );
442 return QStringLiteral( "hexagon" );
444 return QStringLiteral( "octagon" );
446 return QStringLiteral( "decagon" );
448 return QStringLiteral( "square_with_corners" );
450 return QStringLiteral( "rounded_square" );
452 return QStringLiteral( "triangle" );
454 return QStringLiteral( "equilateral_triangle" );
456 return QStringLiteral( "left_half_triangle" );
458 return QStringLiteral( "right_half_triangle" );
460 return QStringLiteral( "star_diamond" );
462 return QStringLiteral( "star" );
464 return QStringLiteral( "heart" );
466 return QStringLiteral( "arrow" );
468 return QStringLiteral( "filled_arrowhead" );
470 return QStringLiteral( "cross_fill" );
472 return QStringLiteral( "circle" );
474 return QStringLiteral( "cross" );
476 return QStringLiteral( "cross2" );
478 return QStringLiteral( "line" );
480 return QStringLiteral( "arrowhead" );
482 return QStringLiteral( "semi_circle" );
484 return QStringLiteral( "third_circle" );
486 return QStringLiteral( "quarter_circle" );
488 return QStringLiteral( "asterisk_fill" );
490 return QStringLiteral( "half_arc" );
492 return QStringLiteral( "third_arc" );
494 return QStringLiteral( "quarter_arc" );
495 }
496 return QString();
497}
498
500{
501 return shapeToPolygon( shape, mPolygon );
502}
503
505{
506 polygon.clear();
507
508 switch ( shape )
509 {
511 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 1, 1 ) ) );
512 return true;
513
515 {
516 static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 0.6072;
517
518 polygon << QPointF( - VERTEX_OFFSET_FROM_ORIGIN, 1 )
519 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, 1 )
520 << QPointF( 1, VERTEX_OFFSET_FROM_ORIGIN )
521 << QPointF( 1, -VERTEX_OFFSET_FROM_ORIGIN )
522 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, -1 )
523 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, -1 )
524 << QPointF( -1, -VERTEX_OFFSET_FROM_ORIGIN )
525 << QPointF( -1, VERTEX_OFFSET_FROM_ORIGIN )
526 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 );
527 return true;
528 }
529
531 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 0 ) ) );
532 return true;
533
535 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 1 ) ) );
536 return true;
537
539 polygon << QPointF( -1, -1 ) << QPointF( 1, 1 ) << QPointF( -1, 1 ) << QPointF( -1, -1 );
540 return true;
541
543 polygon << QPointF( 0.5, -0.5 )
544 << QPointF( 1, 0.5 )
545 << QPointF( -1, 0.5 )
546 << QPointF( -0.5, -0.5 )
547 << QPointF( 0.5, -0.5 );
548 return true;
549
551 polygon << QPointF( 0.5, 0.5 )
552 << QPointF( 1, -0.5 )
553 << QPointF( -0.5, -0.5 )
554 << QPointF( -1, 0.5 )
555 << QPointF( 0.5, 0.5 );
556 return true;
557
559 polygon << QPointF( 1, 0.5 )
560 << QPointF( 0.5, -0.5 )
561 << QPointF( -1, -0.5 )
562 << QPointF( -0.5, 0.5 )
563 << QPointF( 1, 0.5 );
564 return true;
565
567 polygon << QPointF( -1, 0 ) << QPointF( 0, 1 )
568 << QPointF( 1, 0 ) << QPointF( 0, -1 ) << QPointF( -1, 0 );
569 return true;
570
572 polygon << QPointF( 1, 0.5 )
573 << QPointF( 1, -1 )
574 << QPointF( -1, -1 )
575 << QPointF( -1, 0.5 )
576 << QPointF( 0, 1 )
577 << QPointF( 1, 0.5 );
578 return true;
579
581 /* angular-representation of hardcoded values used
582 polygon << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288.0 ) ) )
583 << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) )
584 << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) )
585 << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) )
586 << QPointF( 0, -1 ); */
587 polygon << QPointF( -0.9511, -0.3090 )
588 << QPointF( -0.5878, 0.8090 )
589 << QPointF( 0.5878, 0.8090 )
590 << QPointF( 0.9511, -0.3090 )
591 << QPointF( 0, -1 )
592 << QPointF( -0.9511, -0.3090 );
593 return true;
594
596 /* angular-representation of hardcoded values used
597 polygon << QPointF( std::sin( DEG2RAD( 300.0 ) ), - std::cos( DEG2RAD( 300.0 ) ) )
598 << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
599 << QPointF( std::sin( DEG2RAD( 180.0 ) ), - std::cos( DEG2RAD( 180.0 ) ) )
600 << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
601 << QPointF( std::sin( DEG2RAD( 60.0 ) ), - std::cos( DEG2RAD( 60.0 ) ) )
602 << QPointF( 0, -1 ); */
603 polygon << QPointF( -0.8660, -0.5 )
604 << QPointF( -0.8660, 0.5 )
605 << QPointF( 0, 1 )
606 << QPointF( 0.8660, 0.5 )
607 << QPointF( 0.8660, -0.5 )
608 << QPointF( 0, -1 )
609 << QPointF( -0.8660, -0.5 );
610 return true;
611
613 {
614 static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 1.0 / ( 1 + M_SQRT2 );
615
616 polygon << QPointF( - VERTEX_OFFSET_FROM_ORIGIN, 1 )
617 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, 1 )
618 << QPointF( 1, VERTEX_OFFSET_FROM_ORIGIN )
619 << QPointF( 1, -VERTEX_OFFSET_FROM_ORIGIN )
620 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, -1 )
621 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, -1 )
622 << QPointF( -1, -VERTEX_OFFSET_FROM_ORIGIN )
623 << QPointF( -1, VERTEX_OFFSET_FROM_ORIGIN )
624 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 );
625 return true;
626 }
627
629 {
630
631 polygon << QPointF( 0.587785252, 0.809016994 )
632 << QPointF( 0.951056516, 0.309016994 )
633 << QPointF( 0.951056516, -0.309016994 )
634 << QPointF( 0.587785252, -0.809016994 )
635 << QPointF( 0, -1 )
636 << QPointF( -0.587785252, -0.809016994 )
637 << QPointF( -0.951056516, -0.309016994 )
638 << QPointF( -0.951056516, 0.309016994 )
639 << QPointF( -0.587785252, 0.809016994 )
640 << QPointF( 0, 1 )
641 << QPointF( 0.587785252, 0.809016994 );
642 return true;
643 }
644
646 polygon << QPointF( -1, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
647 return true;
648
650 /* angular-representation of hardcoded values used
651 polygon << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
652 << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
653 << QPointF( 0, -1 ); */
654 polygon << QPointF( -0.8660, 0.5 )
655 << QPointF( 0.8660, 0.5 )
656 << QPointF( 0, -1 )
657 << QPointF( -0.8660, 0.5 );
658 return true;
659
661 polygon << QPointF( 0, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( 0, 1 );
662 return true;
663
665 polygon << QPointF( -1, 1 ) << QPointF( 0, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
666 return true;
667
669 {
670 const double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
671
672 polygon << QPointF( inner_r * std::sin( DEG2RAD( 315.0 ) ), - inner_r * std::cos( DEG2RAD( 315.0 ) ) )
673 << QPointF( std::sin( DEG2RAD( 270 ) ), - std::cos( DEG2RAD( 270 ) ) )
674 << QPointF( inner_r * std::sin( DEG2RAD( 225.0 ) ), - inner_r * std::cos( DEG2RAD( 225.0 ) ) )
675 << QPointF( std::sin( DEG2RAD( 180 ) ), - std::cos( DEG2RAD( 180 ) ) )
676 << QPointF( inner_r * std::sin( DEG2RAD( 135.0 ) ), - inner_r * std::cos( DEG2RAD( 135.0 ) ) )
677 << QPointF( std::sin( DEG2RAD( 90 ) ), - std::cos( DEG2RAD( 90 ) ) )
678 << QPointF( inner_r * std::sin( DEG2RAD( 45.0 ) ), - inner_r * std::cos( DEG2RAD( 45.0 ) ) )
679 << QPointF( std::sin( DEG2RAD( 0 ) ), - std::cos( DEG2RAD( 0 ) ) );
680 return true;
681 }
682
684 {
685 const double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
686
687 polygon << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ) // 324
688 << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288 ) ) ) // 288
689 << QPointF( inner_r * std::sin( DEG2RAD( 252.0 ) ), - inner_r * std::cos( DEG2RAD( 252.0 ) ) ) // 252
690 << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) ) // 216
691 << QPointF( 0, inner_r ) // 180
692 << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) ) // 144
693 << QPointF( inner_r * std::sin( DEG2RAD( 108.0 ) ), - inner_r * std::cos( DEG2RAD( 108.0 ) ) ) // 108
694 << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) ) // 72
695 << QPointF( inner_r * std::sin( DEG2RAD( 36.0 ) ), - inner_r * std::cos( DEG2RAD( 36.0 ) ) ) // 36
696 << QPointF( 0, -1 )
697 << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ); // 324; // 0
698 return true;
699 }
700
702 polygon << QPointF( 0, -1 )
703 << QPointF( 0.5, -0.5 )
704 << QPointF( 0.25, -0.5 )
705 << QPointF( 0.25, 1 )
706 << QPointF( -0.25, 1 )
707 << QPointF( -0.25, -0.5 )
708 << QPointF( -0.5, -0.5 )
709 << QPointF( 0, -1 );
710 return true;
711
713 polygon << QPointF( 0, 0 ) << QPointF( -1, 1 ) << QPointF( -1, -1 ) << QPointF( 0, 0 );
714 return true;
715
717 polygon << QPointF( -1, -0.2 )
718 << QPointF( -1, -0.2 )
719 << QPointF( -1, 0.2 )
720 << QPointF( -0.2, 0.2 )
721 << QPointF( -0.2, 1 )
722 << QPointF( 0.2, 1 )
723 << QPointF( 0.2, 0.2 )
724 << QPointF( 1, 0.2 )
725 << QPointF( 1, -0.2 )
726 << QPointF( 0.2, -0.2 )
727 << QPointF( 0.2, -1 )
728 << QPointF( -0.2, -1 )
729 << QPointF( -0.2, -0.2 )
730 << QPointF( -1, -0.2 );
731 return true;
732
734 {
735 static constexpr double THICKNESS = 0.3;
736 static constexpr double HALF_THICKNESS = THICKNESS / 2.0;
737 static constexpr double INTERSECTION_POINT = THICKNESS / M_SQRT2;
738 static constexpr double DIAGONAL1 = M_SQRT1_2 - INTERSECTION_POINT * 0.5;
739 static constexpr double DIAGONAL2 = M_SQRT1_2 + INTERSECTION_POINT * 0.5;
740
741 polygon << QPointF( -HALF_THICKNESS, -1 )
742 << QPointF( HALF_THICKNESS, -1 )
743 << QPointF( HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
744 << QPointF( DIAGONAL1, -DIAGONAL2 )
745 << QPointF( DIAGONAL2, -DIAGONAL1 )
746 << QPointF( HALF_THICKNESS + INTERSECTION_POINT, -HALF_THICKNESS )
747 << QPointF( 1, -HALF_THICKNESS )
748 << QPointF( 1, HALF_THICKNESS )
749 << QPointF( HALF_THICKNESS + INTERSECTION_POINT, HALF_THICKNESS )
750 << QPointF( DIAGONAL2, DIAGONAL1 )
751 << QPointF( DIAGONAL1, DIAGONAL2 )
752 << QPointF( HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
753 << QPointF( HALF_THICKNESS, 1 )
754 << QPointF( -HALF_THICKNESS, 1 )
755 << QPointF( -HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
756 << QPointF( -DIAGONAL1, DIAGONAL2 )
757 << QPointF( -DIAGONAL2, DIAGONAL1 )
758 << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, HALF_THICKNESS )
759 << QPointF( -1, HALF_THICKNESS )
760 << QPointF( -1, -HALF_THICKNESS )
761 << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, -HALF_THICKNESS )
762 << QPointF( -DIAGONAL2, -DIAGONAL1 )
763 << QPointF( -DIAGONAL1, -DIAGONAL2 )
764 << QPointF( -HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
765 << QPointF( -HALF_THICKNESS, -1 );
766 return true;
767 }
768
782 return false;
783 }
784
785 return false;
786}
787
789{
790 mPath = QPainterPath();
791
792 switch ( symbol )
793 {
795
796 mPath.addEllipse( QRectF( -1, -1, 2, 2 ) ); // x,y,w,h
797 return true;
798
800 mPath.moveTo( -1, -1 );
801 mPath.addRoundedRect( -1, -1, 2, 2, 0.25, 0.25 );
802 return true;
803
805 mPath.arcTo( -1, -1, 2, 2, 0, 180 );
806 mPath.lineTo( 0, 0 );
807 return true;
808
810 mPath.arcTo( -1, -1, 2, 2, 90, 120 );
811 mPath.lineTo( 0, 0 );
812 return true;
813
815 mPath.arcTo( -1, -1, 2, 2, 90, 90 );
816 mPath.lineTo( 0, 0 );
817 return true;
818
820 mPath.moveTo( 1, 0 );
821 mPath.arcTo( -1, -1, 2, 2, 0, 180 );
822 return true;
823
825 mPath.moveTo( 0, -1 );
826 mPath.arcTo( -1, -1, 2, 2, 90, 120 );
827 return true;
828
830 mPath.moveTo( 0, -1 );
831 mPath.arcTo( -1, -1, 2, 2, 90, 90 );
832 return true;
833
835 mPath.moveTo( -1, 0 );
836 mPath.lineTo( 1, 0 ); // horizontal
837 mPath.moveTo( 0, -1 );
838 mPath.lineTo( 0, 1 ); // vertical
839 return true;
840
842 mPath.moveTo( -1, -1 );
843 mPath.lineTo( 1, 1 );
844 mPath.moveTo( 1, -1 );
845 mPath.lineTo( -1, 1 );
846 return true;
847
849 mPath.moveTo( 0, -1 );
850 mPath.lineTo( 0, 1 ); // vertical line
851 return true;
852
854 mPath.moveTo( -1, -1 );
855 mPath.lineTo( 0, 0 );
856 mPath.lineTo( -1, 1 );
857 return true;
858
860 mPath.moveTo( 0, 0.75 );
861 mPath.arcTo( 0, -1, 1, 1, -45, 210 );
862 mPath.arcTo( -1, -1, 1, 1, 15, 210 );
863 mPath.lineTo( 0, 0.75 );
864 return true;
865
890 return false;
891 }
892 return false;
893}
894
895double QgsSimpleMarkerSymbolLayerBase::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
896{
897 double scaledSize = mSize;
898
900 bool ok = true;
901 if ( hasDataDefinedSize )
902 {
905 mSize, &ok );
906 }
907
908 if ( hasDataDefinedSize && ok )
909 {
910 switch ( mScaleMethod )
911 {
913 scaledSize = std::sqrt( scaledSize );
914 break;
916 break;
917 }
918 }
919
920 return scaledSize;
921}
922
923void QgsSimpleMarkerSymbolLayerBase::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle ) const
924{
925 //offset
926 double offsetX = 0;
927 double offsetY = 0;
928 markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
929 offset = QPointF( offsetX, offsetY );
930
931 hasDataDefinedRotation = false;
932 //angle
933 bool ok = true;
936 {
939
940 // If the expression evaluation was not successful, fallback to static value
941 if ( !ok )
943
944 hasDataDefinedRotation = true;
945 }
946
947 hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || hasDataDefinedRotation;
948
949 if ( hasDataDefinedRotation )
950 {
951 // For non-point markers, "dataDefinedRotation" means following the
952 // shape (shape-data defined). For them, "field-data defined" does
953 // not work at all. TODO: if "field-data defined" ever gets implemented
954 // we'll need a way to distinguish here between the two, possibly
955 // using another flag in renderHints()
956 const QgsFeature *f = context.feature();
957 if ( f )
958 {
959 if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
960 {
961 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
962 angle += m2p.mapRotation();
963 }
964 }
965 }
966
967 if ( angle )
969}
970
971
972//
973// QgsSimpleMarkerSymbolLayer
974//
975
976QgsSimpleMarkerSymbolLayer::QgsSimpleMarkerSymbolLayer( Qgis::MarkerShape shape, double size, double angle, Qgis::ScaleMethod scaleMethod, const QColor &color, const QColor &strokeColor, Qt::PenJoinStyle penJoinStyle )
977 : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
978 , mStrokeColor( strokeColor )
979 , mPenJoinStyle( penJoinStyle )
980{
981 mColor = color;
982}
983
985
987{
995
996 if ( props.contains( QStringLiteral( "name" ) ) )
997 {
998 shape = decodeShape( props[QStringLiteral( "name" )].toString() );
999 }
1000 if ( props.contains( QStringLiteral( "color" ) ) )
1001 color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )].toString() );
1002 if ( props.contains( QStringLiteral( "color_border" ) ) )
1003 {
1004 //pre 2.5 projects use "color_border"
1005 strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color_border" )].toString() );
1006 }
1007 else if ( props.contains( QStringLiteral( "outline_color" ) ) )
1008 {
1009 strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )].toString() );
1010 }
1011 else if ( props.contains( QStringLiteral( "line_color" ) ) )
1012 {
1013 strokeColor = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )].toString() );
1014 }
1015 if ( props.contains( QStringLiteral( "joinstyle" ) ) )
1016 {
1017 penJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() );
1018 }
1019 if ( props.contains( QStringLiteral( "size" ) ) )
1020 size = props[QStringLiteral( "size" )].toDouble();
1021 if ( props.contains( QStringLiteral( "angle" ) ) )
1022 angle = props[QStringLiteral( "angle" )].toDouble();
1023 if ( props.contains( QStringLiteral( "scale_method" ) ) )
1024 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1025
1027 if ( props.contains( QStringLiteral( "offset" ) ) )
1028 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1029 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1030 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1031 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1032 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1033 if ( props.contains( QStringLiteral( "size_unit" ) ) )
1034 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1035 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1036 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1037
1038 if ( props.contains( QStringLiteral( "outline_style" ) ) )
1039 {
1040 m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "outline_style" )].toString() ) );
1041 }
1042 else if ( props.contains( QStringLiteral( "line_style" ) ) )
1043 {
1044 m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "line_style" )].toString() ) );
1045 }
1046 if ( props.contains( QStringLiteral( "outline_width" ) ) )
1047 {
1048 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
1049 }
1050 else if ( props.contains( QStringLiteral( "line_width" ) ) )
1051 {
1052 m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
1053 }
1054 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
1055 {
1056 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
1057 }
1058 if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
1059 {
1060 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
1061 }
1062 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
1063 {
1064 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
1065 }
1066
1067 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1068 {
1069 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1070 }
1071 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1072 {
1073 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1074 }
1075
1076 if ( props.contains( QStringLiteral( "cap_style" ) ) )
1077 {
1078 m->setPenCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( props[QStringLiteral( "cap_style" )].toString() ) );
1079 }
1080
1082
1083 return m;
1084}
1085
1086
1088{
1089 return QStringLiteral( "SimpleMarker" );
1090}
1091
1093{
1095
1096 QColor brushColor = mColor;
1097 QColor penColor = mStrokeColor;
1098
1099 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
1100 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
1101
1102 mBrush = QBrush( brushColor );
1103 mPen = QPen( penColor );
1104 mPen.setStyle( mStrokeStyle );
1105 mPen.setCapStyle( mPenCapStyle );
1106 mPen.setJoinStyle( mPenJoinStyle );
1108
1109 QColor selBrushColor = context.renderContext().selectionColor();
1110 QColor selPenColor = selBrushColor == mColor ? selBrushColor : mStrokeColor;
1111 if ( context.opacity() < 1 && !SELECTION_IS_OPAQUE )
1112 {
1113 selBrushColor.setAlphaF( context.opacity() );
1114 selPenColor.setAlphaF( context.opacity() );
1115 }
1116 mSelBrush = QBrush( selBrushColor );
1117 mSelPen = QPen( selPenColor );
1118 mSelPen.setStyle( mStrokeStyle );
1120
1122 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
1123
1124 // use caching only when:
1125 // - size, rotation, shape, color, stroke color is not data-defined
1126 // - drawing to screen (not printer)
1127 mUsingCache = !hasDataDefinedRotation && !hasDataDefinedSize && !context.renderContext().forceVectorOutput()
1131
1132 if ( mUsingCache )
1133 mCachedOpacity = context.opacity();
1134
1135 if ( !shapeIsFilled( mShape ) )
1136 {
1137 // some markers can't be drawn as a polygon (circle, cross)
1138 // For these set the selected stroke color to the selected color
1139 mSelPen.setColor( selBrushColor );
1140 }
1141
1142
1143 if ( mUsingCache )
1144 {
1145 if ( !prepareCache( context ) )
1146 {
1147 mUsingCache = false;
1148 }
1149 }
1150 else
1151 {
1152 mCache = QImage();
1153 mSelCache = QImage();
1154 }
1155}
1156
1157
1159{
1160 double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
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, QgsUnitTypes::RenderMillimeters ), 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 > MAXIMUM_CACHE_WIDTH )
1178 {
1179 return false;
1180 }
1181
1182 mCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
1183 mCache.fill( 0 );
1184
1185 const bool needsBrush = shapeIsFilled( mShape );
1186
1187 QPainter p;
1188 p.begin( &mCache );
1189 p.setRenderHint( QPainter::Antialiasing );
1190 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1191 p.setPen( mPen );
1192 p.translate( QPointF( center, center ) );
1193 drawMarker( &p, context );
1194 p.end();
1195
1196 // Construct the selected version of the Cache
1197
1198 const QColor selColor = context.renderContext().selectionColor();
1199
1200 mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
1201 mSelCache.fill( 0 );
1202
1203 p.begin( &mSelCache );
1204 p.setRenderHint( QPainter::Antialiasing );
1205 p.setBrush( needsBrush ? mSelBrush : Qt::NoBrush );
1206 p.setPen( mSelPen );
1207 p.translate( QPointF( center, center ) );
1208 drawMarker( &p, context );
1209 p.end();
1210
1211 // Check that the selected version is different. If not, then re-render,
1212 // filling the background with the selection color and using the normal
1213 // colors for the symbol .. could be ugly!
1214
1215 if ( mSelCache == mCache )
1216 {
1217 p.begin( &mSelCache );
1218 p.setRenderHint( QPainter::Antialiasing );
1219 p.fillRect( 0, 0, imageSize, imageSize, selColor );
1220 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1221 p.setPen( mPen );
1222 p.translate( QPointF( center, center ) );
1223 drawMarker( &p, context );
1224 p.end();
1225 }
1226
1227 return true;
1228}
1229
1230void QgsSimpleMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
1231{
1232 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1233 //of the rendered point!
1234
1235 QPainter *p = context.renderContext().painter();
1236 if ( !p )
1237 {
1238 return;
1239 }
1240
1241 QColor brushColor = mColor;
1242 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
1243 mBrush.setColor( brushColor );
1244
1245 QColor penColor = mStrokeColor;
1246 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
1247 mPen.setColor( penColor );
1248
1249 bool ok = true;
1251 {
1254 if ( ok )
1255 {
1256 c.setAlphaF( c.alphaF() * context.opacity() );
1257 mBrush.setColor( c );
1258 }
1259 }
1261 {
1264 if ( ok )
1265 {
1266 c.setAlphaF( c.alphaF() * context.opacity() );
1267 mPen.setColor( c );
1268 mSelPen.setColor( c );
1269 }
1270 }
1272 {
1275 if ( ok )
1276 {
1279 }
1280 }
1282 {
1285 if ( ok )
1286 {
1289 }
1290 }
1292 {
1295 if ( ok )
1296 {
1297 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1298 mSelPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1299 }
1300 }
1302 {
1304 const QString style = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyCapStyle, context.renderContext().expressionContext(), QString(), &ok );
1305 if ( ok )
1306 {
1307 mPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1308 mSelPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1309 }
1310 }
1311
1312 if ( shapeIsFilled( shape ) )
1313 {
1314 p->setBrush( context.selected() ? mSelBrush : mBrush );
1315 }
1316 else
1317 {
1318 p->setBrush( Qt::NoBrush );
1319 }
1320 p->setPen( context.selected() ? mSelPen : mPen );
1321
1322 if ( !polygon.isEmpty() )
1323 p->drawPolygon( polygon );
1324 else
1325 p->drawPath( path );
1326}
1327
1329{
1330 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1331 //of the rendered point!
1332
1333 QPainter *p = context.renderContext().painter();
1334 if ( !p )
1335 {
1336 return;
1337 }
1338
1339 if ( mUsingCache && qgsDoubleNear( mCachedOpacity, context.opacity() ) )
1340 {
1341 const QImage &img = context.selected() ? mSelCache : mCache;
1342 const double s = img.width();
1343
1344 bool hasDataDefinedSize = false;
1345 const double scaledSize = calculateSize( context, hasDataDefinedSize );
1346
1347 bool hasDataDefinedRotation = false;
1348 QPointF offset;
1349 double angle = 0;
1350 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
1351
1352 p->drawImage( QRectF( point.x() - s / 2.0 + offset.x(),
1353 point.y() - s / 2.0 + offset.y(),
1354 s, s ), img );
1355 }
1356 else
1357 {
1359 }
1360}
1361
1363{
1364 QVariantMap map;
1365 map[QStringLiteral( "name" )] = encodeShape( mShape );
1366 map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
1367 map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
1368 map[QStringLiteral( "size" )] = QString::number( mSize );
1369 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1370 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1371 map[QStringLiteral( "angle" )] = QString::number( mAngle );
1372 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1373 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1374 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1375 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1376 map[QStringLiteral( "outline_style" )] = QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle );
1377 map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
1378 map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
1379 map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
1380 map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
1381 map[QStringLiteral( "cap_style" )] = QgsSymbolLayerUtils::encodePenCapStyle( mPenCapStyle );
1382 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1383 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1384 return map;
1385}
1386
1388{
1390 m->setOffset( mOffset );
1391 m->setSizeUnit( mSizeUnit );
1403 copyPaintEffect( m );
1404 return m;
1405}
1406
1407void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1408{
1409 // <Graphic>
1410 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
1411 element.appendChild( graphicElem );
1412
1414 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
1416
1417 // <Rotation>
1418 QString angleFunc;
1419 bool ok;
1420 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
1421 if ( !ok )
1422 {
1423 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
1424 }
1425 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
1426 {
1427 angleFunc = QString::number( angle + mAngle );
1428 }
1429 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
1430
1431 // <Displacement>
1432 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
1434}
1435
1436QString QgsSimpleMarkerSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
1437{
1438 Q_UNUSED( mmScaleFactor )
1439 Q_UNUSED( mapUnitScaleFactor )
1440#if 0
1441 QString ogrType = "3"; //default is circle
1442 if ( mName == "square" )
1443 {
1444 ogrType = "5";
1445 }
1446 else if ( mName == "triangle" )
1447 {
1448 ogrType = "7";
1449 }
1450 else if ( mName == "star" )
1451 {
1452 ogrType = "9";
1453 }
1454 else if ( mName == "circle" )
1455 {
1456 ogrType = "3";
1457 }
1458 else if ( mName == "cross" )
1459 {
1460 ogrType = "0";
1461 }
1462 else if ( mName == "x" || mName == "cross2" )
1463 {
1464 ogrType = "1";
1465 }
1466 else if ( mName == "line" )
1467 {
1468 ogrType = "10";
1469 }
1470
1471 QString ogrString;
1472 ogrString.append( "SYMBOL(" );
1473 ogrString.append( "id:" );
1474 ogrString.append( '\"' );
1475 ogrString.append( "ogr-sym-" );
1476 ogrString.append( ogrType );
1477 ogrString.append( '\"' );
1478 ogrString.append( ",c:" );
1479 ogrString.append( mColor.name() );
1480 ogrString.append( ",o:" );
1481 ogrString.append( mStrokeColor.name() );
1482 ogrString.append( QString( ",s:%1mm" ).arg( mSize ) );
1483 ogrString.append( ')' );
1484 return ogrString;
1485#endif //0
1486
1487 QString ogrString;
1488 ogrString.append( "PEN(" );
1489 ogrString.append( "c:" );
1490 ogrString.append( mColor.name() );
1491 ogrString.append( ",w:" );
1492 ogrString.append( QString::number( mSize ) );
1493 ogrString.append( "mm" );
1494 ogrString.append( ")" );
1495 return ogrString;
1496}
1497
1499{
1500 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
1501
1502 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
1503 if ( graphicElem.isNull() )
1504 return nullptr;
1505
1506 QString name = QStringLiteral( "square" );
1507 QColor color, strokeColor;
1508 double strokeWidth, size;
1509 Qt::PenStyle strokeStyle;
1510
1512 return nullptr;
1513
1514 double angle = 0.0;
1515 QString angleFunc;
1516 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
1517 {
1518 bool ok;
1519 const double d = angleFunc.toDouble( &ok );
1520 if ( ok )
1521 angle = d;
1522 }
1523
1524 QPointF offset;
1526
1527 const Qgis::MarkerShape shape = decodeShape( name );
1528
1529 double scaleFactor = 1.0;
1530 const QString uom = element.attribute( QStringLiteral( "uom" ) );
1531 QgsUnitTypes::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
1532 size = size * scaleFactor;
1533 offset.setX( offset.x() * scaleFactor );
1534 offset.setY( offset.y() * scaleFactor );
1535
1537 m->setOutputUnit( sldUnitSize );
1538 m->setColor( color );
1540 m->setAngle( angle );
1541 m->setOffset( offset );
1544 return m;
1545}
1546
1548{
1549 Q_UNUSED( context )
1550
1551 if ( mPolygon.count() != 0 )
1552 {
1553 p->drawPolygon( mPolygon );
1554 }
1555 else
1556 {
1557 p->drawPath( mPath );
1558 }
1559}
1560
1561bool QgsSimpleMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
1562{
1563 //data defined size?
1564 double size = mSize;
1565
1566 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
1567
1568 //data defined size
1569 bool ok = true;
1570 if ( hasDataDefinedSize )
1571 {
1573
1574 if ( ok )
1575 {
1576 switch ( mScaleMethod )
1577 {
1579 size = std::sqrt( size );
1580 break;
1582 break;
1583 }
1584 }
1585
1587 }
1588
1590 {
1591 size *= mmMapUnitScaleFactor;
1592 }
1593
1595 {
1597 }
1598 const double halfSize = size / 2.0;
1599
1600 //strokeWidth
1601 double strokeWidth = mStrokeWidth;
1602
1604 {
1607 }
1610 {
1612 }
1613
1614 //color
1615 QColor pc = mPen.color();
1616 QColor bc = mBrush.color();
1618 {
1621 }
1623 {
1626 }
1627
1628 //offset
1629 double offsetX = 0;
1630 double offsetY = 0;
1631 markerOffset( context, offsetX, offsetY );
1632 offsetX *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1633 offsetY *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1634
1635
1636 QPointF off( offsetX, offsetY );
1637
1638 //angle
1639 double angle = mAngle + mLineAngle;
1641 {
1644 }
1645
1648 {
1650 const QString shapeName = mDataDefinedProperties.valueAsString( QgsSymbolLayer::PropertyName, context.renderContext().expressionContext(), QString(), &ok );
1651 if ( ok )
1652 {
1653 shape = decodeShape( shapeName, &ok );
1654 if ( !ok )
1655 shape = mShape;
1656 }
1657 }
1658
1659 if ( angle )
1660 off = _rotatedOffset( off, angle );
1661
1663
1664 QTransform t;
1665 t.translate( shift.x() + off.x(), shift.y() - off.y() );
1666
1667 if ( !qgsDoubleNear( angle, 0.0 ) )
1668 t.rotate( -angle );
1669
1670 QPolygonF polygon;
1671 if ( shapeToPolygon( shape, polygon ) )
1672 {
1673 t.scale( halfSize, -halfSize );
1674
1675 polygon = t.map( polygon );
1676
1678 p.reserve( polygon.size() );
1679 for ( int i = 0; i < polygon.size(); i++ )
1680 {
1681 p << QgsPoint( polygon[i] );
1682 }
1683
1684 if ( mBrush.style() != Qt::NoBrush )
1685 e.writePolygon( QgsRingSequence() << p, layerName, QStringLiteral( "SOLID" ), bc );
1686 if ( mPen.style() != Qt::NoPen )
1687 e.writePolyline( p, layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1688 }
1689 else if ( shape == Qgis::MarkerShape::Circle )
1690 {
1691 shift += QPointF( off.x(), -off.y() );
1692 if ( mBrush.style() != Qt::NoBrush )
1693 e.writeFilledCircle( layerName, bc, QgsPoint( shift ), halfSize );
1694 if ( mPen.style() != Qt::NoPen )
1695 e.writeCircle( layerName, pc, QgsPoint( shift ), halfSize, QStringLiteral( "CONTINUOUS" ), strokeWidth );
1696 }
1697 else if ( shape == Qgis::MarkerShape::Line )
1698 {
1699 const QPointF pt1 = t.map( QPointF( 0, -halfSize ) );
1700 const QPointF pt2 = t.map( QPointF( 0, halfSize ) );
1701
1702 if ( mPen.style() != Qt::NoPen )
1703 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1704 }
1705 else if ( shape == Qgis::MarkerShape::Cross )
1706 {
1707 if ( mPen.style() != Qt::NoPen )
1708 {
1709 const QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
1710 const QPointF pt2 = t.map( QPointF( halfSize, 0 ) );
1711 const QPointF pt3 = t.map( QPointF( 0, -halfSize ) );
1712 const QPointF pt4 = t.map( QPointF( 0, halfSize ) );
1713
1714 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1715 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1716 }
1717 }
1718 else if ( shape == Qgis::MarkerShape::Cross2 )
1719 {
1720 if ( mPen.style() != Qt::NoPen )
1721 {
1722 const QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
1723 const QPointF pt2 = t.map( QPointF( halfSize, halfSize ) );
1724 const QPointF pt3 = t.map( QPointF( halfSize, -halfSize ) );
1725 const QPointF pt4 = t.map( QPointF( -halfSize, 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::ArrowHead )
1732 {
1733 if ( mPen.style() != Qt::NoPen )
1734 {
1735 const QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
1736 const QPointF pt2 = t.map( QPointF( 0, 0 ) );
1737 const QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
1738
1739 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1740 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1741 }
1742 }
1743 else
1744 {
1745 QgsDebugMsg( QStringLiteral( "Unsupported dxf marker name %1" ).arg( encodeShape( shape ) ) );
1746 return false;
1747 }
1748
1749 return true;
1750}
1751
1752
1754{
1756 mStrokeWidthUnit = unit;
1757}
1758
1760{
1762 {
1763 return mStrokeWidthUnit;
1764 }
1766}
1767
1769{
1772}
1773
1775{
1777 {
1779 }
1780 return QgsMapUnitScale();
1781}
1782
1784{
1788}
1789
1791{
1792 QRectF symbolBounds = QgsSimpleMarkerSymbolLayerBase::bounds( point, context );
1793
1794 // need to account for stroke width
1795 double penWidth = mStrokeWidth;
1796 bool ok = true;
1798 {
1801 if ( ok )
1802 {
1803 penWidth = strokeWidth;
1804 }
1805 }
1808 {
1811 if ( ok && strokeStyle == QLatin1String( "no" ) )
1812 {
1813 penWidth = 0.0;
1814 }
1815 }
1816 else if ( mStrokeStyle == Qt::NoPen )
1817 penWidth = 0;
1818
1819 //antialiasing, add 1 pixel
1820 penWidth += 1;
1821
1822 //extend bounds by pen width / 2.0
1823 symbolBounds.adjust( -penWidth / 2.0, -penWidth / 2.0,
1824 penWidth / 2.0, penWidth / 2.0 );
1825
1826 return symbolBounds;
1827}
1828
1829void QgsSimpleMarkerSymbolLayer::setColor( const QColor &color )
1830{
1831 if ( shapeIsFilled( mShape ) )
1832 {
1834 }
1835 else
1836 {
1838 }
1839}
1840
1842{
1843 if ( shapeIsFilled( mShape ) )
1844 {
1845 return fillColor();
1846 }
1847 else
1848 {
1849 return strokeColor();
1850 }
1851}
1852
1853
1854
1855
1856//
1857// QgsFilledMarkerSymbolLayer
1858//
1859
1861 : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
1862{
1863 mFill.reset( static_cast<QgsFillSymbol *>( QgsFillSymbol::createSimple( QVariantMap() ) ) );
1864}
1865
1867
1869{
1870 QString name = DEFAULT_SIMPLEMARKER_NAME;
1874
1875 if ( props.contains( QStringLiteral( "name" ) ) )
1876 name = props[QStringLiteral( "name" )].toString();
1877 if ( props.contains( QStringLiteral( "size" ) ) )
1878 size = props[QStringLiteral( "size" )].toDouble();
1879 if ( props.contains( QStringLiteral( "angle" ) ) )
1880 angle = props[QStringLiteral( "angle" )].toDouble();
1881 if ( props.contains( QStringLiteral( "scale_method" ) ) )
1882 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1883
1885 if ( props.contains( QStringLiteral( "offset" ) ) )
1886 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1887 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1888 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1889 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1890 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1891 if ( props.contains( QStringLiteral( "size_unit" ) ) )
1892 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1893 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1894 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1895 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1896 {
1897 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1898 }
1899 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1900 {
1901 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1902 }
1903
1905
1907
1908 return m;
1909}
1910
1912{
1913 return QStringLiteral( "FilledMarker" );
1914}
1915
1917{
1918 if ( mFill )
1919 {
1920 mFill->startRender( context.renderContext(), context.fields() );
1921 }
1922
1924}
1925
1927{
1928 if ( mFill )
1929 {
1930 mFill->stopRender( context.renderContext() );
1931 }
1932}
1933
1935{
1936 QVariantMap map;
1937 map[QStringLiteral( "name" )] = encodeShape( mShape );
1938 map[QStringLiteral( "size" )] = QString::number( mSize );
1939 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1940 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1941 map[QStringLiteral( "angle" )] = QString::number( mAngle );
1942 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1943 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1944 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1945 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1946 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1947 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1948
1949 if ( mFill )
1950 {
1951 map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mFill->color() );
1952 }
1953 return map;
1954}
1955
1957{
1959 copyPaintEffect( m );
1961 m->setSubSymbol( mFill->clone() );
1962 return m;
1963}
1964
1966{
1967 return mFill.get();
1968}
1969
1971{
1972 if ( symbol && symbol->type() == Qgis::SymbolType::Fill )
1973 {
1974 mFill.reset( static_cast<QgsFillSymbol *>( symbol ) );
1975 return true;
1976 }
1977 else
1978 {
1979 delete symbol;
1980 return false;
1981 }
1982}
1983
1985{
1986 if ( mFill )
1987 {
1988 return QgsSymbolLayerUtils::estimateMaxSymbolBleed( mFill.get(), context );
1989 }
1990 return 0;
1991}
1992
1994{
1995 QSet<QString> attr = QgsSimpleMarkerSymbolLayerBase::usedAttributes( context );
1996 if ( mFill )
1997 attr.unite( mFill->usedAttributes( context ) );
1998 return attr;
1999}
2000
2002{
2004 return true;
2005 if ( mFill && mFill->hasDataDefinedProperties() )
2006 return true;
2007 return false;
2008}
2009
2011{
2012 mColor = c;
2013 if ( mFill )
2014 mFill->setColor( c );
2015}
2016
2018{
2019 return mFill ? mFill->color() : mColor;
2020}
2021
2023{
2026 || ( mFill && mFill->usesMapUnits() );
2027}
2028
2030{
2032 if ( mFill )
2033 mFill->setOutputUnit( unit );
2034}
2035
2036void QgsFilledMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
2037{
2038 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
2039 //of the rendered point!
2040
2041 QPainter *p = context.renderContext().painter();
2042 if ( !p )
2043 {
2044 return;
2045 }
2046
2047 const double prevOpacity = mFill->opacity();
2048 mFill->setOpacity( mFill->opacity() * context.opacity() );
2049
2050 if ( shapeIsFilled( shape ) )
2051 {
2052 p->setBrush( Qt::red );
2053 }
2054 else
2055 {
2056 p->setBrush( Qt::NoBrush );
2057 }
2058 p->setPen( Qt::black );
2059
2060 const bool prevIsSubsymbol = context.renderContext().flags() & Qgis::RenderContextFlag::RenderingSubSymbol;
2062
2063 if ( !polygon.isEmpty() )
2064 {
2065 mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
2066 }
2067 else
2068 {
2069 const QPolygonF poly = path.toFillPolygon();
2070 mFill->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, context.selected() );
2071 }
2072
2074
2075 mFill->setOpacity( prevOpacity );
2076}
2077
2078
2080
2081
2082QgsSvgMarkerSymbolLayer::QgsSvgMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
2083{
2084 mSize = size;
2085 mAngle = angle;
2086 mOffset = QPointF( 0, 0 );
2088 mStrokeWidth = 0.2;
2090 mColor = QColor( 35, 35, 35 );
2091 mStrokeColor = QColor( 35, 35, 35 );
2092 setPath( path );
2093}
2094
2096
2098{
2099 QString name;
2103
2104 if ( props.contains( QStringLiteral( "name" ) ) )
2105 name = props[QStringLiteral( "name" )].toString();
2106 if ( props.contains( QStringLiteral( "size" ) ) )
2107 size = props[QStringLiteral( "size" )].toDouble();
2108 if ( props.contains( QStringLiteral( "angle" ) ) )
2109 angle = props[QStringLiteral( "angle" )].toDouble();
2110 if ( props.contains( QStringLiteral( "scale_method" ) ) )
2111 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
2112
2114
2115 if ( props.contains( QStringLiteral( "size_unit" ) ) )
2116 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
2117 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
2118 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
2119 if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
2120 m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
2121 if ( props.contains( QStringLiteral( "offset" ) ) )
2122 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
2123 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
2124 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
2125 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2126 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
2127 if ( props.contains( QStringLiteral( "fill" ) ) )
2128 {
2129 //pre 2.5 projects used "fill"
2130 m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "fill" )].toString() ) );
2131 }
2132 else if ( props.contains( QStringLiteral( "color" ) ) )
2133 {
2134 m->setFillColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )].toString() ) );
2135 }
2136 if ( props.contains( QStringLiteral( "outline" ) ) )
2137 {
2138 //pre 2.5 projects used "outline"
2139 m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline" )].toString() ) );
2140 }
2141 else if ( props.contains( QStringLiteral( "outline_color" ) ) )
2142 {
2143 m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )].toString() ) );
2144 }
2145 else if ( props.contains( QStringLiteral( "line_color" ) ) )
2146 {
2147 m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "line_color" )].toString() ) );
2148 }
2149
2150 if ( props.contains( QStringLiteral( "outline-width" ) ) )
2151 {
2152 //pre 2.5 projects used "outline-width"
2153 m->setStrokeWidth( props[QStringLiteral( "outline-width" )].toDouble() );
2154 }
2155 else if ( props.contains( QStringLiteral( "outline_width" ) ) )
2156 {
2157 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
2158 }
2159 else if ( props.contains( QStringLiteral( "line_width" ) ) )
2160 {
2161 m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
2162 }
2163
2164 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
2165 {
2166 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
2167 }
2168 else if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
2169 {
2170 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
2171 }
2172 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
2173 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
2174
2175 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2176 {
2177 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2178 }
2179 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2180 {
2181 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2182 }
2183
2185
2187
2188 if ( props.contains( QStringLiteral( "parameters" ) ) )
2189 {
2190 const QVariantMap parameters = props[QStringLiteral( "parameters" )].toMap();
2192 }
2193
2194 return m;
2195}
2196
2197void QgsSvgMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
2198{
2199 const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
2200 if ( it != properties.end() )
2201 {
2202 if ( saving )
2203 {
2204 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
2205 }
2206 else
2207 {
2208 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
2209 }
2210 }
2211}
2212
2213void QgsSvgMarkerSymbolLayer::setPath( const QString &path )
2214{
2216 mHasFillParam = false;
2217 mPath = path;
2218 QColor defaultFillColor, defaultStrokeColor;
2219 double strokeWidth, fillOpacity, strokeOpacity;
2220 bool hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
2221 bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
2222 QgsApplication::svgCache()->containsParams( path, mHasFillParam, hasDefaultFillColor, defaultFillColor,
2223 hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
2224 hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2225 hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
2226 hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
2227
2228 const double newFillOpacity = hasFillOpacityParam ? fillColor().alphaF() : 1.0;
2229 const double newStrokeOpacity = hasStrokeOpacityParam ? strokeColor().alphaF() : 1.0;
2230
2231 if ( hasDefaultFillColor )
2232 {
2233 defaultFillColor.setAlphaF( newFillOpacity );
2234 setFillColor( defaultFillColor );
2235 }
2236 if ( hasDefaultFillOpacity )
2237 {
2238 QColor c = fillColor();
2239 c.setAlphaF( fillOpacity );
2240 setFillColor( c );
2241 }
2242 if ( hasDefaultStrokeColor )
2243 {
2244 defaultStrokeColor.setAlphaF( newStrokeOpacity );
2245 setStrokeColor( defaultStrokeColor );
2246 }
2247 if ( hasDefaultStrokeWidth )
2248 {
2250 }
2251 if ( hasDefaultStrokeOpacity )
2252 {
2253 QColor c = strokeColor();
2254 c.setAlphaF( strokeOpacity );
2255 setStrokeColor( c );
2256 }
2257
2259}
2260
2262{
2263 if ( mDefaultAspectRatio == 0.0 )
2264 {
2265 //size
2266 const double size = mSize;
2267 //assume 88 dpi as standard value
2268 const double widthScaleFactor = 3.465;
2269 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( mPath, size, mColor, mStrokeColor, mStrokeWidth, widthScaleFactor );
2270 // set default aspect ratio
2271 mDefaultAspectRatio = svgViewbox.isValid() ? svgViewbox.height() / svgViewbox.width() : 0.0;
2272 }
2273 return mDefaultAspectRatio;
2274}
2275
2277{
2278 const bool aPreservedAspectRatio = preservedAspectRatio();
2279 if ( aPreservedAspectRatio && !par )
2280 {
2282 }
2283 else if ( !aPreservedAspectRatio && par )
2284 {
2285 mFixedAspectRatio = 0.0;
2286 }
2287 return preservedAspectRatio();
2288}
2289
2290void QgsSvgMarkerSymbolLayer::setParameters( const QMap<QString, QgsProperty> &parameters )
2291{
2293}
2294
2295
2297{
2298 return QStringLiteral( "SvgMarker" );
2299}
2300
2302{
2303 QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
2304 Q_UNUSED( context )
2305}
2306
2308{
2309 Q_UNUSED( context )
2310}
2311
2313{
2314 QPainter *p = context.renderContext().painter();
2315 if ( !p )
2316 return;
2317
2318 bool hasDataDefinedSize = false;
2319 const double scaledWidth = calculateSize( context, hasDataDefinedSize );
2320 const double width = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2321
2322 //don't render symbols with a width below one or above 10,000 pixels
2323 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2324 {
2325 return;
2326 }
2327
2328 const QgsScopedQPainterState painterState( p );
2329
2330 bool hasDataDefinedAspectRatio = false;
2331 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2332 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2333
2335
2336 double strokeWidth = mStrokeWidth;
2338 {
2341 }
2343
2344 QColor fillColor = mColor;
2345 if ( context.selected() && mHasFillParam )
2346 {
2348 }
2350 {
2353 }
2354
2355 QColor strokeColor = mStrokeColor;
2357 {
2360 }
2361
2362 QString path = mPath;
2364 {
2367 context.renderContext().pathResolver() );
2369 {
2370 // adjust height of data defined path
2371 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2372 context.renderContext().scaleFactor(), aspectRatio,
2373 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2374 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2375 }
2376 }
2377
2378 QPointF outputOffset;
2379 double angle = 0.0;
2380 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2381
2382 p->translate( point + outputOffset );
2383
2384 const bool rotated = !qgsDoubleNear( angle, 0 );
2385 if ( rotated )
2386 p->rotate( angle );
2387
2388 bool fitsInCache = true;
2389 bool usePict = true;
2390 const bool rasterizeSelected = !mHasFillParam || mDataDefinedProperties.isActive( QgsSymbolLayer::PropertyName );
2391 if ( ( !context.renderContext().forceVectorOutput() && !rotated ) || ( context.selected() && rasterizeSelected ) )
2392 {
2394 context.renderContext().scaleFactor(), fitsInCache, aspectRatio,
2395 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2396 if ( fitsInCache && img.width() > 1 )
2397 {
2398 usePict = false;
2399
2400 if ( context.selected() )
2401 {
2403 }
2404
2405 //consider transparency
2406 if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2407 {
2408 QImage transparentImage = img.copy();
2409 QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2410 p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
2411 }
2412 else
2413 {
2414 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2415 }
2416 }
2417 }
2418
2419 if ( usePict || !fitsInCache )
2420 {
2421 p->setOpacity( context.opacity() );
2423 context.renderContext().scaleFactor(), context.renderContext().forceVectorOutput(), aspectRatio,
2424 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2425 if ( pct.width() > 1 )
2426 {
2427 const QgsScopedQPainterState painterPictureState( p );
2428 _fixQPictureDPI( p );
2429 p->drawPicture( 0, 0, pct );
2430 }
2431 }
2432
2433 // workaround issue with nested QPictures forgetting antialiasing flag - see https://github.com/qgis/QGIS/issues/22909
2435}
2436
2437double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2438{
2439 double scaledSize = mSize;
2441
2442 bool ok = true;
2443 if ( hasDataDefinedSize )
2444 {
2447 }
2448 else
2449 {
2451 if ( hasDataDefinedSize )
2452 {
2455 }
2456 }
2457
2458 if ( hasDataDefinedSize && ok )
2459 {
2460 switch ( mScaleMethod )
2461 {
2463 scaledSize = std::sqrt( scaledSize );
2464 break;
2466 break;
2467 }
2468 }
2469
2470 return scaledSize;
2471}
2472
2473double QgsSvgMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2474{
2476 if ( !hasDataDefinedAspectRatio )
2477 return mFixedAspectRatio;
2478
2480 return 0.0;
2481
2482 double scaledAspectRatio = mDefaultAspectRatio;
2483 if ( mFixedAspectRatio > 0.0 )
2484 scaledAspectRatio = mFixedAspectRatio;
2485
2486 const double defaultHeight = mSize * scaledAspectRatio;
2487 scaledAspectRatio = defaultHeight / scaledSize;
2488
2489 bool ok = true;
2490 double scaledHeight = scaledSize * scaledAspectRatio;
2492 {
2493 context.setOriginalValueVariable( defaultHeight );
2495 }
2496
2497 if ( hasDataDefinedAspectRatio && ok )
2498 {
2499 switch ( mScaleMethod )
2500 {
2502 scaledHeight = sqrt( scaledHeight );
2503 break;
2505 break;
2506 }
2507 }
2508
2509 scaledAspectRatio = scaledHeight / scaledSize;
2510
2511 return scaledAspectRatio;
2512}
2513
2514void QgsSvgMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
2515{
2516 //offset
2517 double offsetX = 0;
2518 double offsetY = 0;
2519 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
2520 offset = QPointF( offsetX, offsetY );
2521
2524 {
2527 }
2528
2530 if ( hasDataDefinedRotation )
2531 {
2532 // For non-point markers, "dataDefinedRotation" means following the
2533 // shape (shape-data defined). For them, "field-data defined" does
2534 // not work at all. TODO: if "field-data defined" ever gets implemented
2535 // we'll need a way to distinguish here between the two, possibly
2536 // using another flag in renderHints()
2537 const QgsFeature *f = context.feature();
2538 if ( f )
2539 {
2540 if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
2541 {
2542 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2543 angle += m2p.mapRotation();
2544 }
2545 }
2546 }
2547
2548 if ( angle )
2550}
2551
2552
2554{
2555 QVariantMap map;
2556 map[QStringLiteral( "name" )] = mPath;
2557 map[QStringLiteral( "size" )] = QString::number( mSize );
2558 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2559 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2560 map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2561 map[QStringLiteral( "angle" )] = QString::number( mAngle );
2562 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2563 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2564 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2565 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2566 map[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
2567 map[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
2568 map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
2569 map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2570 map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2571 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
2572 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
2573
2574 map[QStringLiteral( "parameters" )] = QgsProperty::propertyMapToVariantMap( mParameters );
2575
2576 return map;
2577}
2578
2580{
2584}
2585
2587{
2590 m->setColor( mColor );
2595 m->setOffset( mOffset );
2598 m->setSizeUnit( mSizeUnit );
2603
2605 copyPaintEffect( m );
2606 return m;
2607}
2608
2610{
2612 mStrokeWidthUnit = unit;
2613}
2614
2616{
2618 if ( unit != mStrokeWidthUnit )
2619 {
2621 }
2622 return unit;
2623}
2624
2626{
2629}
2630
2632{
2634 {
2636 }
2637 return QgsMapUnitScale();
2638}
2639
2640void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
2641{
2642 // <Graphic>
2643 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2644 element.appendChild( graphicElem );
2645
2646 // encode a parametric SVG reference
2647 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
2650
2651 // <Rotation>
2652 QString angleFunc;
2653 bool ok;
2654 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2655 if ( !ok )
2656 {
2657 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
2658 }
2659 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2660 {
2661 angleFunc = QString::number( angle + mAngle );
2662 }
2663
2664 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2665
2666 // <Displacement>
2667 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2669}
2670
2672{
2673 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
2674
2675 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
2676 if ( graphicElem.isNull() )
2677 return nullptr;
2678
2679 QString path, mimeType;
2680 // Unused and to be DEPRECATED in externalGraphicFromSld
2681 QColor fillColor_;
2682 double size;
2683
2684 if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor_, size ) )
2685 return nullptr;
2686
2687 double scaleFactor = 1.0;
2688 const QString uom = element.attribute( QStringLiteral( "uom" ) );
2689 QgsUnitTypes::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
2690 size = size * scaleFactor;
2691
2692 if ( mimeType != QLatin1String( "image/svg+xml" ) )
2693 return nullptr;
2694
2695 double angle = 0.0;
2696 QString angleFunc;
2697 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2698 {
2699 bool ok;
2700 const double d = angleFunc.toDouble( &ok );
2701 if ( ok )
2702 angle = d;
2703 }
2704
2705 QPointF offset;
2707
2708 // Extract parameters from URL
2709 QString realPath { path };
2710 QUrl svgUrl { path };
2711
2712 // Because color definition can start with '#', the url parsing won't recognize the query string entirely
2713 QUrlQuery queryString;
2714
2715 if ( svgUrl.hasQuery() && svgUrl.hasFragment() )
2716 {
2717 const QString queryPart { path.mid( path.indexOf( '?' ) + 1 ) };
2718 queryString.setQuery( queryPart );
2719 }
2720
2721 // Remove query for simple file paths
2722 if ( svgUrl.scheme().isEmpty() || svgUrl.isLocalFile() )
2723 {
2724 svgUrl.setQuery( QString() );
2725 realPath = svgUrl.path();
2726 }
2727
2729
2730 QMap<QString, QgsProperty> params;
2731
2732 bool ok;
2733
2734 if ( queryString.hasQueryItem( QStringLiteral( "fill" ) ) )
2735 {
2736 const QColor fillColor { queryString.queryItemValue( QStringLiteral( "fill" ) ) };
2737 m->setFillColor( fillColor );
2738 }
2739
2740 if ( queryString.hasQueryItem( QStringLiteral( "fill-opacity" ) ) )
2741 {
2742 const double alpha { queryString.queryItemValue( QStringLiteral( "fill-opacity" ) ).toDouble( &ok ) };
2743 if ( ok )
2744 {
2745 params.insert( QStringLiteral( "fill-opacity" ), QgsProperty::fromValue( alpha ) );
2746 }
2747 }
2748
2749 if ( queryString.hasQueryItem( QStringLiteral( "outline" ) ) )
2750 {
2751 const QColor strokeColor { queryString.queryItemValue( QStringLiteral( "outline" ) ) };
2753 }
2754
2755 if ( queryString.hasQueryItem( QStringLiteral( "outline-opacity" ) ) )
2756 {
2757 const double alpha { queryString.queryItemValue( QStringLiteral( "outline-opacity" ) ).toDouble( &ok ) };
2758 if ( ok )
2759 {
2760 params.insert( QStringLiteral( "outline-opacity" ), QgsProperty::fromValue( alpha ) );
2761 }
2762 }
2763
2764 if ( queryString.hasQueryItem( QStringLiteral( "outline-width" ) ) )
2765 {
2766 const int width { queryString.queryItemValue( QStringLiteral( "outline-width" ) ).toInt( &ok )};
2767 if ( ok )
2768 {
2769 m->setStrokeWidth( width );
2770 }
2771 }
2772
2773 if ( ! params.isEmpty() )
2774 {
2775 m->setParameters( params );
2776 }
2777
2778 m->setOutputUnit( sldUnitSize );
2779 m->setAngle( angle );
2780 m->setOffset( offset );
2781 return m;
2782}
2783
2784bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
2785{
2786 //size
2787 double size = mSize;
2788
2789 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
2790
2791 bool ok = true;
2792 if ( hasDataDefinedSize )
2793 {
2796 }
2797
2798 if ( hasDataDefinedSize && ok )
2799 {
2800 switch ( mScaleMethod )
2801 {
2803 size = std::sqrt( size );
2804 break;
2806 break;
2807 }
2808 }
2809
2811 {
2812 size *= mmMapUnitScaleFactor;
2813 }
2814
2815 //offset, angle
2816 QPointF offset = mOffset;
2817
2819 {
2821 const QVariant val = mDataDefinedProperties.value( QgsSymbolLayer::PropertyOffset, context.renderContext().expressionContext(), QString() );
2822 const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
2823 if ( ok )
2824 offset = res;
2825 }
2826 const double offsetX = offset.x();
2827 const double offsetY = offset.y();
2828
2829 QPointF outputOffset( offsetX, offsetY );
2830
2831 double angle = mAngle + mLineAngle;
2833 {
2836 }
2837
2838 if ( angle )
2839 outputOffset = _rotatedOffset( outputOffset, angle );
2840
2842
2843 QString path = mPath;
2845 {
2848 context.renderContext().pathResolver() );
2849 }
2850
2851 double strokeWidth = mStrokeWidth;
2853 {
2856 }
2858
2859 QColor fillColor = mColor;
2861 {
2864 }
2865
2866 QColor strokeColor = mStrokeColor;
2868 {
2871 }
2872
2874
2875 const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, size, fillColor, strokeColor, strokeWidth,
2877 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2878
2879 QSvgRenderer r( svgContent );
2880 if ( !r.isValid() )
2881 return false;
2882
2883 QgsDxfPaintDevice pd( &e );
2884 pd.setDrawingSize( QSizeF( r.defaultSize() ) );
2885
2886 QSizeF outSize( r.defaultSize() );
2887 outSize.scale( size, size, Qt::KeepAspectRatio );
2888
2889 QPainter p;
2890 p.begin( &pd );
2891 if ( !qgsDoubleNear( angle, 0.0 ) )
2892 {
2893 p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
2894 p.rotate( angle );
2895 p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
2896 }
2897 pd.setShift( shift + QPointF( outputOffset.x(), -outputOffset.y() ) );
2898 pd.setOutputSize( QRectF( -outSize.width() / 2.0, -outSize.height() / 2.0, outSize.width(), outSize.height() ) );
2899 pd.setLayer( layerName );
2900 r.render( &p );
2901 p.end();
2902 return true;
2903}
2904
2906{
2907 bool hasDataDefinedSize = false;
2908 double scaledWidth = calculateSize( context, hasDataDefinedSize );
2909
2910 bool hasDataDefinedAspectRatio = false;
2911 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2912 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2913
2914 scaledWidth = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2915 scaledHeight = context.renderContext().convertToPainterUnits( scaledHeight, mSizeUnit, mSizeMapUnitScale );
2916
2917 //don't render symbols with size below one or above 10,000 pixels
2918 if ( static_cast< int >( scaledWidth ) < 1 || 10000.0 < scaledWidth )
2919 {
2920 return QRectF();
2921 }
2922
2923 QPointF outputOffset;
2924 double angle = 0.0;
2925 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2926
2927 double strokeWidth = mStrokeWidth;
2929 {
2932 }
2934
2935 QString path = mPath;
2937 {
2940 context.renderContext().pathResolver() );
2942 {
2943 // need to get colors to take advantage of cached SVGs
2944 QColor fillColor = mColor;
2946 {
2949 }
2950
2951 const QColor strokeColor = mStrokeColor;
2953 {
2956 }
2957
2959
2960 // adjust height of data defined path
2961 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2962 context.renderContext().scaleFactor(), aspectRatio,
2963 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2964 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2965 }
2966 }
2967
2968 QTransform transform;
2969 // move to the desired position
2970 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
2971
2972 if ( !qgsDoubleNear( angle, 0.0 ) )
2973 transform.rotate( angle );
2974
2975 //antialiasing
2976 strokeWidth += 1.0 / 2.0;
2977
2978 QRectF symbolBounds = transform.mapRect( QRectF( -scaledWidth / 2.0,
2979 -scaledHeight / 2.0,
2980 scaledWidth,
2981 scaledHeight ) );
2982
2983 //extend bounds by pen width / 2.0
2984 symbolBounds.adjust( -strokeWidth / 2.0, -strokeWidth / 2.0,
2985 strokeWidth / 2.0, strokeWidth / 2.0 );
2986
2987 return symbolBounds;
2988}
2989
2991
2992QgsRasterMarkerSymbolLayer::QgsRasterMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
2993 : mPath( path )
2994{
2995 mSize = size;
2996 mAngle = angle;
2997 mOffset = QPointF( 0, 0 );
3000}
3001
3003
3005{
3006 QString path;
3010
3011 if ( props.contains( QStringLiteral( "imageFile" ) ) )
3012 path = props[QStringLiteral( "imageFile" )].toString();
3013 if ( props.contains( QStringLiteral( "size" ) ) )
3014 size = props[QStringLiteral( "size" )].toDouble();
3015 if ( props.contains( QStringLiteral( "angle" ) ) )
3016 angle = props[QStringLiteral( "angle" )].toDouble();
3017 if ( props.contains( QStringLiteral( "scale_method" ) ) )
3018 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
3019
3020 std::unique_ptr< QgsRasterMarkerSymbolLayer > m = std::make_unique< QgsRasterMarkerSymbolLayer >( path, size, angle, scaleMethod );
3021 m->setCommonProperties( props );
3022 return m.release();
3023}
3024
3025void QgsRasterMarkerSymbolLayer::setCommonProperties( const QVariantMap &properties )
3026{
3027 if ( properties.contains( QStringLiteral( "alpha" ) ) )
3028 {
3029 setOpacity( properties[QStringLiteral( "alpha" )].toDouble() );
3030 }
3031
3032 if ( properties.contains( QStringLiteral( "size_unit" ) ) )
3033 setSizeUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "size_unit" )].toString() ) );
3034 if ( properties.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3035 setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3036 if ( properties.contains( QStringLiteral( "fixedAspectRatio" ) ) )
3037 setFixedAspectRatio( properties[QStringLiteral( "fixedAspectRatio" )].toDouble() );
3038
3039 if ( properties.contains( QStringLiteral( "offset" ) ) )
3040 setOffset( QgsSymbolLayerUtils::decodePoint( properties[QStringLiteral( "offset" )].toString() ) );
3041 if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
3042 setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )].toString() ) );
3043 if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3044 setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3045
3046 if ( properties.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3047 {
3048 setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( properties[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3049 }
3050 if ( properties.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3051 {
3052 setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( properties[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3053 }
3054
3057}
3058
3059void QgsRasterMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
3060{
3061 const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
3062 if ( it != properties.end() && it.value().type() == QVariant::String )
3063 {
3064 if ( saving )
3065 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
3066 else
3067 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
3068 }
3069}
3070
3071void QgsRasterMarkerSymbolLayer::setPath( const QString &path )
3072{
3073 mPath = path;
3075}
3076
3078{
3079 const bool aPreservedAspectRatio = preservedAspectRatio();
3080 if ( aPreservedAspectRatio && !par )
3081 {
3083 }
3084 else if ( !aPreservedAspectRatio && par )
3085 {
3086 mFixedAspectRatio = 0.0;
3087 }
3088 return preservedAspectRatio();
3089}
3090
3092{
3093 if ( mDefaultAspectRatio == 0.0 )
3094 {
3096 mDefaultAspectRatio = ( !size.isNull() && size.isValid() && size.width() > 0 ) ? static_cast< double >( size.height() ) / static_cast< double >( size.width() ) : 0.0;
3097 }
3098 return mDefaultAspectRatio;
3099}
3100
3102{
3103 return QStringLiteral( "RasterMarker" );
3104}
3105
3107{
3108 QPainter *p = context.renderContext().painter();
3109 if ( !p )
3110 return;
3111
3112 QString path = mPath;
3114 {
3117 }
3118
3119 if ( path.isEmpty() )
3120 return;
3121
3122 double width = 0.0;
3123 double height = 0.0;
3124
3125 bool hasDataDefinedSize = false;
3126 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3127
3128 bool hasDataDefinedAspectRatio = false;
3129 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3130
3131 QPointF outputOffset;
3132 double angle = 0.0;
3133
3134 // RenderPercentage Unit Type takes original image size
3136 {
3138 if ( size.isEmpty() )
3139 return;
3140
3141 width = ( scaledSize * static_cast< double >( size.width() ) ) / 100.0;
3142 height = ( scaledSize * static_cast< double >( size.height() ) ) / 100.0;
3143
3144 // don't render symbols with size below one or above 10,000 pixels
3145 if ( static_cast< int >( width ) < 1 || 10000.0 < width || static_cast< int >( height ) < 1 || 10000.0 < height )
3146 return;
3147
3148 calculateOffsetAndRotation( context, width, height, outputOffset, angle );
3149 }
3150 else
3151 {
3152 width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3153 height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3154
3155 if ( preservedAspectRatio() && path != mPath )
3156 {
3158 if ( !size.isNull() && size.isValid() && size.width() > 0 )
3159 {
3160 height = width * ( static_cast< double >( size.height() ) / static_cast< double >( size.width() ) );
3161 }
3162 }
3163
3164 // don't render symbols with size below one or above 10,000 pixels
3165 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
3166 return;
3167
3168 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3169 }
3170
3171 const QgsScopedQPainterState painterState( p );
3172 p->translate( point + outputOffset );
3173
3174 const bool rotated = !qgsDoubleNear( angle, 0 );
3175 if ( rotated )
3176 p->rotate( angle );
3177
3178 double opacity = mOpacity;
3180 {
3183 }
3184 opacity *= context.opacity();
3185
3186 QImage img = fetchImage( context.renderContext(), path, QSize( width, preservedAspectRatio() ? 0 : width * aspectRatio ), preservedAspectRatio(), opacity );
3187 if ( !img.isNull() )
3188 {
3189 if ( context.selected() )
3190 {
3192 }
3193
3194 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
3195 }
3196}
3197
3198QImage QgsRasterMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
3199{
3200 bool cached = false;
3201 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking );
3202}
3203
3204double QgsRasterMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
3205{
3206 double scaledSize = mSize;
3208
3209 bool ok = true;
3210 if ( hasDataDefinedSize )
3211 {
3214 }
3215 else
3216 {
3218 if ( hasDataDefinedSize )
3219 {
3222 }
3223 }
3224
3225 if ( hasDataDefinedSize && ok )
3226 {
3227 switch ( mScaleMethod )
3228 {
3230 scaledSize = std::sqrt( scaledSize );
3231 break;
3233 break;
3234 }
3235 }
3236
3237 return scaledSize;
3238}
3239
3240double QgsRasterMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
3241{
3243 if ( !hasDataDefinedAspectRatio )
3244 return mFixedAspectRatio;
3245
3247 return 0.0;
3248
3249 double scaledAspectRatio = mDefaultAspectRatio;
3250 if ( mFixedAspectRatio > 0.0 )
3251 scaledAspectRatio = mFixedAspectRatio;
3252
3253 const double defaultHeight = mSize * scaledAspectRatio;
3254 scaledAspectRatio = defaultHeight / scaledSize;
3255
3256 bool ok = true;
3257 double scaledHeight = scaledSize * scaledAspectRatio;
3259 {
3260 context.setOriginalValueVariable( defaultHeight );
3262 }
3263
3264 if ( hasDataDefinedAspectRatio && ok )
3265 {
3266 switch ( mScaleMethod )
3267 {
3269 scaledHeight = sqrt( scaledHeight );
3270 break;
3272 break;
3273 }
3274 }
3275
3276 scaledAspectRatio = scaledHeight / scaledSize;
3277
3278 return scaledAspectRatio;
3279}
3280
3281void QgsRasterMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
3282{
3283 //offset
3284 double offsetX = 0;
3285 double offsetY = 0;
3286 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
3287 offset = QPointF( offsetX, offsetY );
3288
3291 {
3294 }
3295
3297 if ( hasDataDefinedRotation )
3298 {
3299 const QgsFeature *f = context.feature();
3300 if ( f )
3301 {
3302 if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
3303 {
3304 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3305 angle += m2p.mapRotation();
3306 }
3307 }
3308 }
3309
3310 if ( angle )
3312}
3313
3314
3316{
3317 QVariantMap map;
3318 map[QStringLiteral( "imageFile" )] = mPath;
3319 map[QStringLiteral( "size" )] = QString::number( mSize );
3320 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3321 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3322 map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
3323 map[QStringLiteral( "angle" )] = QString::number( mAngle );
3324 map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
3325 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3326 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3327 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3328 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
3329 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3330 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3331 return map;
3332}
3333
3335{
3336 std::unique_ptr< QgsRasterMarkerSymbolLayer > m = std::make_unique< QgsRasterMarkerSymbolLayer >( mPath, mSize, mAngle );
3337 copyCommonProperties( m.get() );
3338 return m.release();
3339}
3340
3341
3343{
3345 other->setOpacity( mOpacity );
3346 other->setOffset( mOffset );
3347 other->setOffsetUnit( mOffsetUnit );
3349 other->setSizeUnit( mSizeUnit );
3354 copyPaintEffect( other );
3355}
3356
3358{
3361}
3362
3364{
3365 return QColor();
3366}
3367
3369{
3371}
3372
3374{
3376}
3377
3379{
3380 bool hasDataDefinedSize = false;
3381 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3382 const double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3383 bool hasDataDefinedAspectRatio = false;
3384 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3385 const double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3386
3387 //don't render symbols with size below one or above 10,000 pixels
3388 if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
3389 {
3390 return QRectF();
3391 }
3392
3393 QPointF outputOffset;
3394 double angle = 0.0;
3395 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3396
3397 QTransform transform;
3398
3399 // move to the desired position
3400 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3401
3402 if ( !qgsDoubleNear( angle, 0.0 ) )
3403 transform.rotate( angle );
3404
3405 QRectF symbolBounds = transform.mapRect( QRectF( -width / 2.0,
3406 -height / 2.0,
3407 width,
3408 height ) );
3409
3410 return symbolBounds;
3411}
3412
3414
3415QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QString chr, double pointSize, const QColor &color, double angle )
3416{
3417 mFontFamily = fontFamily;
3418 mString = chr;
3419 mColor = color;
3420 mAngle = angle;
3421 mSize = pointSize;
3422 mOrigSize = pointSize;
3424 mOffset = QPointF( 0, 0 );
3426 mStrokeColor = DEFAULT_FONTMARKER_BORDERCOLOR;
3427 mStrokeWidth = 0.0;
3428 mStrokeWidthUnit = QgsUnitTypes::RenderMillimeters;
3429 mPenJoinStyle = DEFAULT_FONTMARKER_JOINSTYLE;
3430}
3431
3433
3435{
3437 QString string = DEFAULT_FONTMARKER_CHR;
3438 double pointSize = DEFAULT_FONTMARKER_SIZE;
3441
3442 if ( props.contains( QStringLiteral( "font" ) ) )
3443 fontFamily = props[QStringLiteral( "font" )].toString();
3444 if ( props.contains( QStringLiteral( "chr" ) ) && props[QStringLiteral( "chr" )].toString().length() > 0 )
3445 string = props[QStringLiteral( "chr" )].toString();
3446 if ( props.contains( QStringLiteral( "size" ) ) )
3447 pointSize = props[QStringLiteral( "size" )].toDouble();
3448 if ( props.contains( QStringLiteral( "color" ) ) )
3449 color = QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "color" )].toString() );
3450 if ( props.contains( QStringLiteral( "angle" ) ) )
3451 angle = props[QStringLiteral( "angle" )].toDouble();
3452
3454
3455 if ( props.contains( QStringLiteral( "font_style" ) ) )
3456 m->setFontStyle( props[QStringLiteral( "font_style" )].toString() );
3457 if ( props.contains( QStringLiteral( "outline_color" ) ) )
3458 m->setStrokeColor( QgsSymbolLayerUtils::decodeColor( props[QStringLiteral( "outline_color" )].toString() ) );
3459 if ( props.contains( QStringLiteral( "outline_width" ) ) )
3460 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
3461 if ( props.contains( QStringLiteral( "offset" ) ) )
3462 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
3463 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
3464 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
3465 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3466 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3467 if ( props.contains( QStringLiteral( "size_unit" ) ) )
3468 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
3469 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3470 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3471 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
3472 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
3473 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
3474 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
3475 if ( props.contains( QStringLiteral( "joinstyle" ) ) )
3476 m->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() ) );
3477 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3478 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3479 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3480 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3481
3483
3484 return m;
3485}
3486
3488{
3489 return QStringLiteral( "FontMarker" );
3490}
3491
3493{
3494 QColor brushColor = mColor;
3495 QColor penColor = mStrokeColor;
3496
3497 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
3498 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
3499
3500 mBrush = QBrush( brushColor );
3501 mPen = QPen( penColor );
3502 mPen.setJoinStyle( mPenJoinStyle );
3503 mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
3504
3505 mFont = QFont( QgsApplication::fontManager()->processFontFamilyName( mFontFamily ) );
3506 if ( !mFontStyle.isEmpty() )
3507 {
3508 mFont.setStyleName( QgsFontUtils::translateNamedStyle( mFontStyle ) );
3509 }
3510
3511 double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3512 mNonZeroFontSize = !qgsDoubleNear( sizePixels, 0.0 );
3513
3514 if ( mNonZeroFontSize && sizePixels > MAX_FONT_CHARACTER_SIZE_IN_PIXELS )
3515 {
3516 // if font is too large (e.g using map units and map is very zoomed in), then we limit
3517 // the font size and instead scale up the painter.
3518 // this avoids issues with massive font sizes (eg https://github.com/qgis/QGIS/issues/42270)
3519 mFontSizeScale = sizePixels / MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3520 sizePixels = MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3521 }
3522 else
3523 mFontSizeScale = 1.0;
3524
3525 // if a non zero, but small pixel size results, round up to 2 pixels so that a "dot" is at least visible
3526 // (if we set a <=1 pixel size here Qt will reset the font to a default size, leading to much too large symbols)
3527 mFont.setPixelSize( std::max( 2, static_cast< int >( std::round( sizePixels ) ) ) );
3528 mFontMetrics.reset( new QFontMetrics( mFont ) );
3529 mChrWidth = mFontMetrics->horizontalAdvance( mString );
3530 mChrOffset = QPointF( mChrWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3531 mOrigSize = mSize; // save in case the size would be data defined
3532
3533 // use caching only when not using a data defined character
3537 if ( mUseCachedPath )
3538 {
3539 QPointF chrOffset = mChrOffset;
3540 double chrWidth;
3541 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3542 mCachedPath = QPainterPath();
3543 mCachedPath.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3544 }
3545}
3546
3548{
3549 Q_UNUSED( context )
3550}
3551
3552QString QgsFontMarkerSymbolLayer::characterToRender( QgsSymbolRenderContext &context, QPointF &charOffset, double &charWidth )
3553{
3554 charOffset = mChrOffset;
3555 QString stringToRender = mString;
3557 {
3558 context.setOriginalValueVariable( mString );
3560 if ( stringToRender != mString )
3561 {
3562 charWidth = mFontMetrics->horizontalAdvance( stringToRender );
3563 charOffset = QPointF( charWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3564 }
3565 }
3566 return stringToRender;
3567}
3568
3569void QgsFontMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
3570 double scaledSize,
3571 bool &hasDataDefinedRotation,
3572 QPointF &offset,
3573 double &angle ) const
3574{
3575 //offset
3576 double offsetX = 0;
3577 double offsetY = 0;
3578 markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
3579 offset = QPointF( offsetX, offsetY );
3580
3581 //angle
3582 bool ok = true;
3585 {
3588
3589 // If the expression evaluation was not successful, fallback to static value
3590 if ( !ok )
3592 }
3593
3594 hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation;
3595 if ( hasDataDefinedRotation )
3596 {
3597 // For non-point markers, "dataDefinedRotation" means following the
3598 // shape (shape-data defined). For them, "field-data defined" does
3599 // not work at all. TODO: if "field-data defined" ever gets implemented
3600 // we'll need a way to distinguish here between the two, possibly
3601 // using another flag in renderHints()
3602 const QgsFeature *f = context.feature();
3603 if ( f )
3604 {
3605 if ( f->hasGeometry() && f->geometry().type() == QgsWkbTypes::PointGeometry )
3606 {
3607 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3608 angle += m2p.mapRotation();
3609 }
3610 }
3611 }
3612
3613 if ( angle )
3615}
3616
3617double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context )
3618{
3619 double scaledSize = mSize;
3620 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::PropertySize );
3621
3622 bool ok = true;
3623 if ( hasDataDefinedSize )
3624 {
3627 }
3628
3629 if ( hasDataDefinedSize && ok )
3630 {
3631 switch ( mScaleMethod )
3632 {
3634 scaledSize = std::sqrt( scaledSize );
3635 break;
3637 break;
3638 }
3639 }
3640 return scaledSize;
3641}
3642
3644{
3645 QPainter *p = context.renderContext().painter();
3646 if ( !p || !mNonZeroFontSize )
3647 return;
3648
3649 QTransform transform;
3650
3651 bool ok;
3652 QColor brushColor = mColor;
3654 {
3657 }
3658 brushColor = context.selected() ? context.renderContext().selectionColor() : brushColor;
3659 if ( !context.selected() || !SELECTION_IS_OPAQUE )
3660 {
3661 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
3662 }
3663 mBrush.setColor( brushColor );
3664
3665 QColor penColor = mStrokeColor;
3667 {
3670 }
3671 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
3672
3673 double penWidth = context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3675 {
3676 context.setOriginalValueVariable( mStrokeWidth );
3678 if ( ok )
3679 {
3680 penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3681 }
3682 }
3683
3685 {
3688 if ( ok )
3689 {
3690 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
3691 }
3692 }
3693
3694 const QgsScopedQPainterState painterState( p );
3695 p->setBrush( mBrush );
3696 if ( !qgsDoubleNear( penWidth, 0.0 ) )
3697 {
3698 mPen.setColor( penColor );
3699 mPen.setWidthF( penWidth );
3700 p->setPen( mPen );
3701 }
3702 else
3703 {
3704 p->setPen( Qt::NoPen );
3705 }
3706
3708 {
3709 context.setOriginalValueVariable( mFontFamily );
3711 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( ok ? fontFamily : mFontFamily );
3712 mFont.setFamily( processedFamily );
3713 }
3715 {
3716 context.setOriginalValueVariable( mFontStyle );
3719 }
3721 {
3722 mFontMetrics.reset( new QFontMetrics( mFont ) );
3723 }
3724
3725 QPointF chrOffset = mChrOffset;
3726 double chrWidth;
3727 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3728
3729 const double sizeToRender = calculateSize( context );
3730
3731 bool hasDataDefinedRotation = false;
3732 QPointF offset;
3733 double angle = 0;
3734 calculateOffsetAndRotation( context, sizeToRender, hasDataDefinedRotation, offset, angle );
3735
3736 p->translate( point.x() + offset.x(), point.y() + offset.y() );
3737
3738 if ( !qgsDoubleNear( angle, 0.0 ) )
3739 transform.rotate( angle );
3740
3741 if ( !qgsDoubleNear( sizeToRender, mOrigSize ) )
3742 {
3743 const double s = sizeToRender / mOrigSize;
3744 transform.scale( s, s );
3745 }
3746
3747 if ( !qgsDoubleNear( mFontSizeScale, 1.0 ) )
3748 transform.scale( mFontSizeScale, mFontSizeScale );
3749
3750 if ( mUseCachedPath )
3751 {
3752 p->drawPath( transform.map( mCachedPath ) );
3753 }
3754 else
3755 {
3756 QPainterPath path;
3757 path.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3758 p->drawPath( transform.map( path ) );
3759 }
3760}
3761
3763{
3764 QVariantMap props;
3765 props[QStringLiteral( "font" )] = mFontFamily;
3766 props[QStringLiteral( "font_style" )] = mFontStyle;
3767 props[QStringLiteral( "chr" )] = mString;
3768 props[QStringLiteral( "size" )] = QString::number( mSize );
3769 props[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3770 props[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3771 props[QStringLiteral( "color" )] = QgsSymbolLayerUtils::encodeColor( mColor );
3772 props[QStringLiteral( "outline_color" )] = QgsSymbolLayerUtils::encodeColor( mStrokeColor );
3773 props[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
3774 props[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
3775 props[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
3776 props[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
3777 props[QStringLiteral( "angle" )] = QString::number( mAngle );
3778 props[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3779 props[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3780 props[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3781 props[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3782 props[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3783 return props;
3784}
3785
3787{
3788 QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( mFontFamily, mString, mSize, mColor, mAngle );
3789 m->setFontStyle( mFontStyle );
3790 m->setStrokeColor( mStrokeColor );
3791 m->setStrokeWidth( mStrokeWidth );
3792 m->setStrokeWidthUnit( mStrokeWidthUnit );
3793 m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
3794 m->setPenJoinStyle( mPenJoinStyle );
3795 m->setOffset( mOffset );
3798 m->setSizeUnit( mSizeUnit );
3803 copyPaintEffect( m );
3804 return m;
3805}
3806
3807void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
3808{
3809 // <Graphic>
3810 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
3811 element.appendChild( graphicElem );
3812
3813 const QString fontPath = QStringLiteral( "ttf://%1" ).arg( mFontFamily );
3814 int markIndex = !mString.isEmpty() ? mString.at( 0 ).unicode() : 0;
3815 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
3816 QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, QStringLiteral( "ttf" ), &markIndex, mColor, size );
3817
3818 // <Rotation>
3819 QString angleFunc;
3820 bool ok;
3821 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
3822 if ( !ok )
3823 {
3824 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
3825 }
3826 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
3827 {
3828 angleFunc = QString::number( angle + mAngle );
3829 }
3830 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
3831
3832 // <Displacement>
3833 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
3835}
3836
3838{
3840 || mStrokeWidthUnit == QgsUnitTypes::RenderMapUnits || mStrokeWidthUnit == QgsUnitTypes::RenderMetersInMapUnits
3842}
3843
3845{
3847 mStrokeWidthUnit = unit;
3848}
3849
3851{
3852 QPointF chrOffset = mChrOffset;
3853 double chrWidth = mChrWidth;
3854 //calculate width of rendered character
3855 ( void )characterToRender( context, chrOffset, chrWidth );
3856
3857 if ( !mFontMetrics )
3858 mFontMetrics.reset( new QFontMetrics( mFont ) );
3859
3860 double scaledSize = calculateSize( context );
3861 if ( !qgsDoubleNear( scaledSize, mOrigSize ) )
3862 {
3863 chrWidth *= scaledSize / mOrigSize;
3864 }
3865 chrWidth *= mFontSizeScale;
3866
3867 bool hasDataDefinedRotation = false;
3868 QPointF offset;
3869 double angle = 0;
3870 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
3871 scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3872
3873 QTransform transform;
3874
3875 // move to the desired position
3876 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
3877
3878 if ( !qgsDoubleNear( angle, 0.0 ) )
3879 transform.rotate( angle );
3880
3881 QRectF symbolBounds = transform.mapRect( QRectF( -chrWidth / 2.0,
3882 -scaledSize / 2.0,
3883 chrWidth,
3884 scaledSize ) );
3885 return symbolBounds;
3886}
3887
3889{
3890 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
3891
3892 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
3893 if ( graphicElem.isNull() )
3894 return nullptr;
3895
3896 QString name, format;
3897 QColor color;
3898 double size;
3899 int chr;
3900
3901 if ( !QgsSymbolLayerUtils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
3902 return nullptr;
3903
3904 if ( !name.startsWith( QLatin1String( "ttf://" ) ) || format != QLatin1String( "ttf" ) )
3905 return nullptr;
3906
3907 const QString fontFamily = name.mid( 6 );
3908
3909 double angle = 0.0;
3910 QString angleFunc;
3911 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
3912 {
3913 bool ok;
3914 const double d = angleFunc.toDouble( &ok );
3915 if ( ok )
3916 angle = d;
3917 }
3918
3919 QPointF offset;
3921
3922 double scaleFactor = 1.0;
3923 const QString uom = element.attribute( QStringLiteral( "uom" ) );
3924 QgsUnitTypes::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
3925 offset.setX( offset.x() * scaleFactor );
3926 offset.setY( offset.y() * scaleFactor );
3927 size = size * scaleFactor;
3928
3930 m->setOutputUnit( sldUnitSize );
3931 m->setAngle( angle );
3932 m->setOffset( offset );
3933 return m;
3934}
3935
3936void QgsFontMarkerSymbolLayer::resolveFonts( const QVariantMap &properties, const QgsReadWriteContext &context )
3937{
3938 const QString fontFamily = properties.value( QStringLiteral( "font" ), DEFAULT_FONTMARKER_FONT ).toString();
3939 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( fontFamily );
3940 QString matched;
3941 if ( !QgsFontUtils::fontFamilyMatchOnSystem( processedFamily )
3942 && !QgsApplication::fontManager()->tryToDownloadFontFamily( processedFamily, matched ) )
3943 {
3944 context.pushMessage( QObject::tr( "Font “%1” not available on system" ).arg( processedFamily ) );
3945 }
3946}
3947
3949{
3950 QMap<QString, QgsProperty>::iterator it = mParameters.begin();
3951 for ( ; it != mParameters.end(); ++it )
3952 it.value().prepare( context.renderContext().expressionContext() );
3953
3955}
3956
3957
3959{
3960 QSet<QString> attrs = QgsMarkerSymbolLayer::usedAttributes( context );
3961
3962 QMap<QString, QgsProperty>::const_iterator it = mParameters.constBegin();
3963 for ( ; it != mParameters.constEnd(); ++it )
3964 {
3965 attrs.unite( it.value().referencedFields( context.expressionContext(), true ) );
3966 }
3967
3968 return attrs;
3969}
3970
3971//
3972// QgsAnimatedMarkerSymbolLayer
3973//
3974
3976 : QgsRasterMarkerSymbolLayer( path, size, angle )
3977{
3978
3979}
3980
3982
3984{
3985 QString path;
3988
3989 if ( properties.contains( QStringLiteral( "imageFile" ) ) )
3990 path = properties[QStringLiteral( "imageFile" )].toString();
3991 if ( properties.contains( QStringLiteral( "size" ) ) )
3992 size = properties[QStringLiteral( "size" )].toDouble();
3993 if ( properties.contains( QStringLiteral( "angle" ) ) )
3994 angle = properties[QStringLiteral( "angle" )].toDouble();
3995
3996 std::unique_ptr< QgsAnimatedMarkerSymbolLayer > m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( path, size, angle );
3997 m->setFrameRate( properties.value( QStringLiteral( "frameRate" ), QStringLiteral( "10" ) ).toDouble() );
3998
3999 m->setCommonProperties( properties );
4000 return m.release();
4001}
4002
4004{
4005 return QStringLiteral( "AnimatedMarker" );
4006}
4007
4009{
4010 QVariantMap res = QgsRasterMarkerSymbolLayer::properties();
4011 res.insert( QStringLiteral( "frameRate" ), mFrameRateFps );
4012 return res;
4013}
4014
4016{
4017 std::unique_ptr< QgsAnimatedMarkerSymbolLayer > m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( mPath, mSize, mAngle );
4018 m->setFrameRate( mFrameRateFps );
4019 copyCommonProperties( m.get() );
4020 return m.release();
4021}
4022
4024{
4026
4027 mPreparedPaths.clear();
4029 {
4031 mStaticPath = true;
4032 }
4033 else
4034 {
4035 mStaticPath = false;
4036 }
4037}
4038
4039QImage QgsAnimatedMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
4040{
4041 if ( !mStaticPath && !mPreparedPaths.contains( path ) )
4042 {
4044 mPreparedPaths.insert( path );
4045 }
4046
4047 const long long mapFrameNumber = context.currentFrame();
4049 const double markerAnimationDuration = totalFrameCount / mFrameRateFps;
4050
4051 double animationTimeSeconds = 0;
4052 if ( mapFrameNumber >= 0 && context.frameRate() > 0 )
4053 {
4054 // render is part of an animation, so we base the calculated frame on that
4055 animationTimeSeconds = mapFrameNumber / context.frameRate();
4056 }
4057 else
4058 {
4059 // render is outside of animation, so base the calculated frame on the current epoch
4060 animationTimeSeconds = QDateTime::currentMSecsSinceEpoch() / 1000.0;
4061 }
4062
4063 const double markerAnimationProgressSeconds = std::fmod( animationTimeSeconds, markerAnimationDuration );
4064 const int movieFrame = static_cast< int >( std::floor( markerAnimationProgressSeconds * mFrameRateFps ) );
4065
4066 bool cached = false;
4067 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking, 96, movieFrame );
4068}
4069
@ DynamicRotation
Rotation of symbol may be changed during rendering and symbol should not be cached.
ScaleMethod
Scale methods.
Definition: qgis.h:220
@ ScaleDiameter
Calculate scale by the diameter.
@ ScaleArea
Calculate scale by the area.
MarkerShape
Marker shapes.
Definition: qgis.h:1532
@ 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)
@ RenderingSubSymbol
Set whenever a sub-symbol of a parent symbol is currently being rendered. Can be used during symbol a...
@ RenderSymbolPreview
The render is for a symbol preview only and map based properties may not be available,...
@ RenderBlocking
Render and load remote sources in the same thread to ensure rendering remote sources (svg and images)...
@ Fill
Fill symbol.
QColor valueAsColor(int key, const QgsExpressionContext &context, const QColor &defaultColor=QColor(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a color.
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double.
QString valueAsString(int key, const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a string.
Animated marker symbol layer class.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsAnimatedMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
~QgsAnimatedMarkerSymbolLayer() override
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
QImage fetchImage(QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity) const override
Fetches the image to render.
QgsAnimatedMarkerSymbolLayer(const QString &path=QString(), double size=DEFAULT_RASTERMARKER_SIZE, double angle=DEFAULT_RASTERMARKER_ANGLE)
Constructor for animated marker symbol layer using the specified source image path.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates an animated marker symbol layer from a string map of properties.
QString layerType() const override
Returns a string that represents this layer type.
static QgsImageCache * imageCache()
Returns the application's image cache, used for caching resampled versions of raster images.
static QgsSvgCache * svgCache()
Returns the application's SVG cache, used for caching SVG images and handling parameter replacement w...
static QgsFontManager * fontManager()
Returns the application font manager, which manages available fonts and font installation for the QGI...
Exports QGIS layers to the DXF format.
Definition: qgsdxfexport.h:65
void writeFilledCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius)
Write filled circle (as hatch)
void writeCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius, const QString &lineStyleName, double width)
Write circle (as polyline)
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)
static double mapUnitScaleFactor(double scale, QgsUnitTypes::RenderUnit symbolUnits, QgsUnitTypes::DistanceUnit mapUnits, double mapUnitsPerPixel=1.0)
Returns scale factor for conversion to map units.
QgsUnitTypes::DistanceUnit mapUnits() const
Retrieve map units.
void writePolygon(const QgsRingSequence &polygon, const QString &layer, const QString &hatchPattern, const QColor &color)
Draw dxf filled polygon (HATCH)
double symbologyScale() const
Returns the reference scale for output.
Definition: qgsdxfexport.h:229
void clipValueToMapUnitScale(double &value, const QgsMapUnitScale &scale, double pixelToMMFactor) const
Clips value to scale minimum/maximum.
void writePolyline(const QgsPointSequence &line, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Draw dxf primitives (LWPOLYLINE)
A paint device for drawing into dxf files.
void setShift(QPointF shift)
void setLayer(const QString &layer)
void setOutputSize(const QRectF &r)
void setDrawingSize(QSizeF size)
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsGeometry geometry
Definition: qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:233
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
Definition: qgsfillsymbol.h:30
static QgsFillSymbol * createSimple(const QVariantMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties.
Filled marker symbol layer, consisting of a shape which is rendered using a QgsFillSymbol.
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
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.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
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.
~QgsFontMarkerSymbolLayer() override
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
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.
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit unit)
Sets the stroke width unit.
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 stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
QString layerType() const override
Returns a string that represents this layer type.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsFontMarkerSymbolLayer from a property map (see properties())
static QString translateNamedStyle(const QString &namedStyle)
Returns the localized named style of a font, if such a translation is available.
static bool fontFamilyMatchOnSystem(const QString &family, QString *chosen=nullptr, bool *match=nullptr)
Check whether font family is on system.
static bool updateFontViaStyle(QFont &f, const QString &fontstyle, bool fallback=false)
Updates font with named style and retain all font properties.
QgsWkbTypes::GeometryType type
Definition: qgsgeometry.h:167
QSize originalSize(const QString &path, bool blocking=false) const
Returns the original size (in pixels) of the image at the specified path.
int totalFrameCount(const QString &path, bool blocking=false)
Returns the total frame count of the image at the specified path.
QImage pathAsImage(const QString &path, const QSize size, const bool keepAspectRatio, const double opacity, bool &fitsInCache, bool blocking=false, double targetDpi=96, int frameNumber=-1, bool *isMissing=nullptr)
Returns the specified path rendered as an image.
void prepareAnimation(const QString &path)
Prepares for optimized retrieval of frames for the animation at the given path.
static void overlayColor(QImage &image, const QColor &color)
Overlays a color onto an image.
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
double mapUnitsPerPixel() const
Returns the current map units per pixel.
double mapRotation() const
Returns the current map rotation in degrees (clockwise).
Struct for storing maximum and minimum scales for measurements in map units.
Abstract base class for marker symbol layers.
double mSize
Marker size.
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 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.
QgsUnitTypes::RenderUnit mOffsetUnit
Offset units.
void setSizeUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol's size.
void setVerticalAnchorPoint(VerticalAnchorPoint v)
Sets the vertical anchor point for positioning the symbol.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
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.
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.
QgsUnitTypes::RenderUnit mSizeUnit
Marker size unit.
void setOffsetUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the symbol's offset.
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.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
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
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.
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.
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 ...
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QgsExpressionContext & expressionContext()
Gets the expression context.
bool forceVectorOutput() const
Returns true if rendering operations should use vector operations instead of any faster raster shortc...
long long currentFrame() const
Returns the current frame number of the map (in frames per second), for maps which are part of an ani...
void setFlag(Qgis::RenderContextFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
double frameRate() const
Returns the frame rate of the map, for maps which are part of an animation.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
QColor selectionColor() const
Returns the color to use when rendering selected features.
Qgis::RenderContextFlags flags() const
Returns combination of flags used for rendering.
const QgsPathResolver & pathResolver() const
Returns the path resolver for conversion between relative and absolute paths during rendering operati...
Scoped object for saving and restoring a QPainter object's state.
Abstract base class for simple marker symbol layers.
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
void calculateOffsetAndRotation(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle) const
Calculates the marker offset and rotation.
Qgis::MarkerShape mShape
Symbol shape.
QPainterPath mPath
Painter path representing shape. If mPolygon is empty then the shape is stored in mPath.
bool shapeToPolygon(Qgis::MarkerShape shape, QPolygonF &polygon) const
Creates a polygon representing the specified shape.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
static QList< Qgis::MarkerShape > availableShapes()
Returns a list of all available shape types.
~QgsSimpleMarkerSymbolLayerBase() override
static bool shapeIsFilled(Qgis::MarkerShape shape)
Returns true if a symbol shape has a fill.
QPolygonF mPolygon
Polygon of points in shape. If polygon is empty then shape is using mPath.
Qgis::MarkerShape shape() const
Returns the shape for the rendered marker symbol.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
QgsSimpleMarkerSymbolLayerBase(Qgis::MarkerShape shape=Qgis::MarkerShape::Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructor for QgsSimpleMarkerSymbolLayerBase.
static QString encodeShape(Qgis::MarkerShape shape)
Encodes a shape to its string representation.
double calculateSize(QgsSymbolRenderContext &context, bool &hasDataDefinedSize) const
Calculates the desired size of the marker, considering data defined size overrides.
static Qgis::MarkerShape decodeShape(const QString &name, bool *ok=nullptr)
Attempts to decode a string representation of a shape name to the corresponding shape.
bool prepareMarkerPath(Qgis::MarkerShape symbol)
Prepares the layer for drawing the specified shape (QPainterPath version)
bool prepareMarkerShape(Qgis::MarkerShape shape)
Prepares the layer for drawing the specified shape (QPolygonF version)
Simple marker symbol layer, consisting of a rendered shape with solid fill color and an stroke.
QPen mSelPen
QPen to use as stroke of selected symbols.
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit u)
Sets the unit for the width of the marker's stroke.
void setColor(const QColor &color) override
Sets the "representative" color for the symbol layer.
QColor mStrokeColor
Stroke color.
QImage mSelCache
Cached image of selected marker, if using cached version.
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).
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
void drawMarker(QPainter *p, QgsSymbolRenderContext &context)
Draws the marker shape in the specified painter.
QPen mPen
QPen corresponding to marker's stroke style.
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
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.
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QString layerType() const override
Returns a string that represents this layer type.
double mStrokeWidth
Stroke width.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
Sets the map scale for the width of the marker's stroke.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
void setStrokeStyle(Qt::PenStyle strokeStyle)
Sets the marker's stroke style (e.g., solid, dashed, etc)
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QColor fillColor() const override
Returns the fill color for the symbol layer.
QColor strokeColor() const override
Returns the marker's stroke color.
QBrush mBrush
QBrush corresponding to marker's fill style.
void setStrokeWidth(double w)
Sets the width of the marker's stroke.
void setStrokeColor(const QColor &color) override
Sets the marker's stroke color.
Qt::PenStyle strokeStyle() const
Returns the marker's stroke style (e.g., solid, dashed, etc)
QgsUnitTypes::RenderUnit mStrokeWidthUnit
Stroke width units.
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.
QgsUnitTypes::RenderUnit mStrokeWidthUnit
QString layerType() const override
Returns a string that represents this layer type.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
void setOutputUnit(QgsUnitTypes::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QString path() const
Returns the marker SVG path.
QgsUnitTypes::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
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 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...
void setStrokeWidthUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the stroke width.
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 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 double rescaleUom(double size, QgsUnitTypes::RenderUnit unit, const QVariantMap &props)
Rescales the given size based on the uomScale found in the props, if any is found,...
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 Qt::PenCapStyle decodePenCapStyle(const QString &str)
static QgsUnitTypes::RenderUnit decodeSldUom(const QString &str, double *scaleFactor=nullptr)
Decodes a SLD unit of measure string to a render unit.
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 double estimateMaxSymbolBleed(QgsSymbol *symbol, const QgsRenderContext &context)
Returns the maximum estimated bleed for the symbol.
static void wellKnownMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &name, const QColor &color, const QColor &strokeColor, Qt::PenStyle strokeStyle, double strokeWidth=-1, double size=-1)
static QString encodeScaleMethod(Qgis::ScaleMethod scaleMethod)
Encodes a symbol scale method to a string.
static Qt::PenStyle decodePenStyle(const QString &str)
static void createRotationElement(QDomDocument &doc, QDomElement &element, const QString &rotationFunc)
static QString encodePoint(QPointF point)
Encodes a QPointF to a string.
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
static QPointF decodePoint(const QString &string)
Decodes a QSizeF from a string.
@ PropertyStrokeStyle
Stroke style (eg solid, dashed)
@ PropertyCapStyle
Line cap style.
@ PropertyAngle
Symbol angle.
@ PropertySize
Symbol size.
@ PropertyJoinStyle
Line join style.
@ PropertyOpacity
Opacity.
@ PropertyCharacter
Character, eg for font marker symbol layers.
@ PropertyOffset
Symbol offset.
@ PropertyStrokeWidth
Stroke width.
@ PropertyFillColor
Fill color.
@ PropertyFontStyle
Font style.
@ PropertyHeight
Symbol height.
@ PropertyFontFamily
Font family.
@ PropertyName
Name, eg shape name for simple markers.
@ PropertyStrokeColor
Stroke color.
@ PropertyWidth
Symbol width.
static const bool SELECTION_IS_OPAQUE
Whether styles for selected features ignore symbol alpha.
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns the set of attributes referenced by the layer.
void copyDataDefinedProperties(QgsSymbolLayer *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
void restoreOldDataDefinedProperties(const QVariantMap &stringMap)
Restores older data defined properties from string map.
virtual void prepareExpressions(const QgsSymbolRenderContext &context)
Prepares all data defined property expressions for evaluation.
virtual void setColor(const QColor &color)
Sets the "representative" color for the symbol layer.
virtual QColor color() const
Returns the "representative" color of the symbol layer.
void copyPaintEffect(QgsSymbolLayer *destLayer) const
Copies paint effect of this layer to another symbol layer.
QgsPropertyCollection mDataDefinedProperties
virtual bool hasDataDefinedProperties() const
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
const QgsFeature * feature() const
Returns the current feature being rendered.
QgsFields fields() const
Fields of the layer.
bool selected() const
Returns true if symbols should be rendered using the selected symbol coloring and style.
Qgis::SymbolRenderHints renderHints() const
Returns the rendering hint flags for the symbol.
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for data defined symbology.
qreal opacity() const
Returns the opacity for the symbol.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:93
Qgis::SymbolType type() const
Returns the symbol's type.
Definition: qgssymbol.h:152
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
static Q_INVOKABLE QgsUnitTypes::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
RenderUnit
Rendering size units.
Definition: qgsunittypes.h:168
@ RenderUnknownUnit
Mixed or unknown units.
Definition: qgsunittypes.h:175
@ RenderMetersInMapUnits
Meters value as Map units.
Definition: qgsunittypes.h:176
@ RenderPercentage
Percentage of another measurement (e.g., canvas size, feature size)
Definition: qgsunittypes.h:172
@ RenderMillimeters
Millimeters.
Definition: qgsunittypes.h:169
@ RenderMapUnits
Map units.
Definition: qgsunittypes.h:170
static bool isNull(const QVariant &variant)
Returns true if the specified variant should be considered a NULL value.
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2527
QMap< QString, QString > QgsStringMap
Definition: qgis.h:3022
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
#define DEG2RAD(x)
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
Q_GUI_EXPORT int qt_defaultDpiX()
Q_GUI_EXPORT int qt_defaultDpiY()
#define DEFAULT_FONTMARKER_JOINSTYLE
#define DEFAULT_RASTERMARKER_ANGLE
#define DEFAULT_RASTERMARKER_SIZE
#define DEFAULT_SVGMARKER_ANGLE
#define DEFAULT_SIMPLEMARKER_JOINSTYLE
#define DEFAULT_FONTMARKER_CHR
#define DEFAULT_SIMPLEMARKER_BORDERCOLOR
#define DEFAULT_SIMPLEMARKER_SIZE
#define DEFAULT_SIMPLEMARKER_NAME
#define DEFAULT_SIMPLEMARKER_ANGLE
#define DEFAULT_SVGMARKER_SIZE
#define DEFAULT_FONTMARKER_FONT
#define DEFAULT_FONTMARKER_BORDERCOLOR
#define DEFAULT_FONTMARKER_ANGLE
#define DEFAULT_FONTMARKER_COLOR
#define DEFAULT_FONTMARKER_SIZE
#define DEFAULT_SIMPLEMARKER_COLOR
#define DEFAULT_SCALE_METHOD