QGIS API Documentation 3.39.0-Master (47f7b3a4989)
Loading...
Searching...
No Matches
qgsmarkersymbollayer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmarkersymbollayer.cpp
3 ---------------------
4 begin : November 2009
5 copyright : (C) 2009 by Martin Dobias
6 email : wonder dot sk at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17#include "qgssymbollayerutils.h"
18
19#include "qgsdxfexport.h"
20#include "qgsdxfpaintdevice.h"
21#include "qgsfontutils.h"
22#include "qgsimagecache.h"
23#include "qgsimageoperation.h"
24#include "qgsrendercontext.h"
25#include "qgslogger.h"
26#include "qgssvgcache.h"
27#include "qgsunittypes.h"
28#include "qgssymbol.h"
29#include "qgsfillsymbol.h"
30#include "qgsfontmanager.h"
31#include "qgscolorutils.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
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::Property::Size );
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, Qgis::RenderUnit::Millimeters ), 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, Qgis::RenderUnit::Millimeters ), 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
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() == Qgis::GeometryType::Point )
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 = QgsColorUtils::colorFromString( props[QStringLiteral( "color" )].toString() );
1002 if ( props.contains( QStringLiteral( "color_border" ) ) )
1003 {
1004 //pre 2.5 projects use "color_border"
1005 strokeColor = QgsColorUtils::colorFromString( props[QStringLiteral( "color_border" )].toString() );
1006 }
1007 else if ( props.contains( QStringLiteral( "outline_color" ) ) )
1008 {
1009 strokeColor = QgsColorUtils::colorFromString( props[QStringLiteral( "outline_color" )].toString() );
1010 }
1011 else if ( props.contains( QStringLiteral( "line_color" ) ) )
1012 {
1013 strokeColor = QgsColorUtils::colorFromString( 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
1096
1098{
1100
1101 QColor brushColor = mColor;
1102 QColor penColor = mStrokeColor;
1103
1104 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
1105 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
1106
1107 mBrush = QBrush( brushColor );
1108 mPen = QPen( penColor );
1109 mPen.setStyle( mStrokeStyle );
1110 mPen.setCapStyle( mPenCapStyle );
1111 mPen.setJoinStyle( mPenJoinStyle );
1113
1114 QColor selBrushColor = context.renderContext().selectionColor();
1115 QColor selPenColor = selBrushColor == mColor ? selBrushColor : mStrokeColor;
1116 if ( context.opacity() < 1 && !SELECTION_IS_OPAQUE )
1117 {
1118 selBrushColor.setAlphaF( context.opacity() );
1119 selPenColor.setAlphaF( context.opacity() );
1120 }
1121 mSelBrush = QBrush( selBrushColor );
1122 mSelPen = QPen( selPenColor );
1123 mSelPen.setStyle( mStrokeStyle );
1125
1127 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
1128
1129 // use caching only when:
1130 // - size, rotation, shape, color, stroke color is not data-defined
1131 // - drawing to screen (not printer)
1132 mUsingCache = !hasDataDefinedRotation && !hasDataDefinedSize && !context.renderContext().forceVectorOutput()
1136
1137 if ( mUsingCache )
1138 mCachedOpacity = context.opacity();
1139
1140 if ( !shapeIsFilled( mShape ) )
1141 {
1142 // some markers can't be drawn as a polygon (circle, cross)
1143 // For these set the selected stroke color to the selected color
1144 mSelPen.setColor( selBrushColor );
1145 }
1146
1147
1148 if ( mUsingCache )
1149 {
1150 if ( !prepareCache( context ) )
1151 {
1152 mUsingCache = false;
1153 }
1154 }
1155 else
1156 {
1157 mCache = QImage();
1158 mSelCache = QImage();
1159 }
1160}
1161
1162
1164{
1165 double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
1166 const double deviceRatio = context.renderContext().devicePixelRatio();
1168 {
1169 // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
1170 // and clamp it to a reasonable range. It's the best we can do in this situation!
1171 scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
1172 }
1173
1174 // take into account angle (which is not data-defined otherwise cache wouldn't be used)
1175 if ( !qgsDoubleNear( mAngle, 0.0 ) )
1176 {
1177 scaledSize = ( std::abs( std::sin( mAngle * M_PI / 180 ) ) + std::abs( std::cos( mAngle * M_PI / 180 ) ) ) * scaledSize;
1178 }
1179 // calculate necessary image size for the cache
1180 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
1181 const int imageSize = ( static_cast< int >( scaledSize ) + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width
1182 const double center = imageSize / 2.0;
1183 if ( imageSize * deviceRatio > MAXIMUM_CACHE_WIDTH )
1184 {
1185 return false;
1186 }
1187
1188 mCache = QImage( QSize( imageSize * deviceRatio,
1189 imageSize * deviceRatio ), QImage::Format_ARGB32_Premultiplied );
1190 mCache.setDevicePixelRatio( context.renderContext().devicePixelRatio() );
1191 mCache.setDotsPerMeterX( std::round( context.renderContext().scaleFactor() * 1000 ) );
1192 mCache.setDotsPerMeterY( std::round( context.renderContext().scaleFactor() * 1000 ) );
1193 mCache.fill( 0 );
1194
1195 const bool needsBrush = shapeIsFilled( mShape );
1196
1197 QPainter p;
1198 p.begin( &mCache );
1199 p.setRenderHint( QPainter::Antialiasing );
1200 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1201 p.setPen( mPen );
1202 p.translate( QPointF( center, center ) );
1203 drawMarker( &p, context );
1204 p.end();
1205
1206 // Construct the selected version of the Cache
1207
1208 const QColor selColor = context.renderContext().selectionColor();
1209
1210 mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
1211 mSelCache.fill( 0 );
1212
1213 p.begin( &mSelCache );
1214 p.setRenderHint( QPainter::Antialiasing );
1215 p.setBrush( needsBrush ? mSelBrush : Qt::NoBrush );
1216 p.setPen( mSelPen );
1217 p.translate( QPointF( center, center ) );
1218 drawMarker( &p, context );
1219 p.end();
1220
1221 // Check that the selected version is different. If not, then re-render,
1222 // filling the background with the selection color and using the normal
1223 // colors for the symbol .. could be ugly!
1224
1225 if ( mSelCache == mCache )
1226 {
1227 p.begin( &mSelCache );
1228 p.setRenderHint( QPainter::Antialiasing );
1229 p.fillRect( 0, 0, imageSize, imageSize, selColor );
1230 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1231 p.setPen( mPen );
1232 p.translate( QPointF( center, center ) );
1233 drawMarker( &p, context );
1234 p.end();
1235 }
1236
1237 return true;
1238}
1239
1240void QgsSimpleMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
1241{
1242 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1243 //of the rendered point!
1244
1245 QPainter *p = context.renderContext().painter();
1246 if ( !p )
1247 {
1248 return;
1249 }
1250
1251 QColor brushColor = mColor;
1252 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
1253 mBrush.setColor( brushColor );
1254
1255 QColor penColor = mStrokeColor;
1256 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
1257 mPen.setColor( penColor );
1258
1259 bool ok = true;
1261 {
1264 if ( ok )
1265 {
1266 c.setAlphaF( c.alphaF() * context.opacity() );
1267 mBrush.setColor( c );
1268 }
1269 }
1271 {
1274 if ( ok )
1275 {
1276 c.setAlphaF( c.alphaF() * context.opacity() );
1277 mPen.setColor( c );
1278 mSelPen.setColor( c );
1279 }
1280 }
1282 {
1285 if ( ok )
1286 {
1289 }
1290 }
1292 {
1295 if ( ok )
1296 {
1299 }
1300 }
1302 {
1305 if ( ok )
1306 {
1307 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1308 mSelPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1309 }
1310 }
1312 {
1315 if ( ok )
1316 {
1317 mPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1318 mSelPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1319 }
1320 }
1321
1322 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
1323 if ( shapeIsFilled( shape ) )
1324 {
1325 p->setBrush( useSelectedColor ? mSelBrush : mBrush );
1326 }
1327 else
1328 {
1329 p->setBrush( Qt::NoBrush );
1330 }
1331 p->setPen( useSelectedColor ? mSelPen : mPen );
1332
1333 if ( !polygon.isEmpty() )
1334 p->drawPolygon( polygon );
1335 else
1336 p->drawPath( path );
1337}
1338
1340{
1341 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1342 //of the rendered point!
1343
1344 QPainter *p = context.renderContext().painter();
1345 if ( !p )
1346 {
1347 return;
1348 }
1349
1350 if ( mUsingCache && qgsDoubleNear( mCachedOpacity, context.opacity() ) )
1351 {
1352 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
1353 const QImage &img = useSelectedColor ? mSelCache : mCache;
1354 const double s = img.width() / img.devicePixelRatioF();
1355
1356 bool hasDataDefinedSize = false;
1357 const double scaledSize = calculateSize( context, hasDataDefinedSize );
1358
1359 bool hasDataDefinedRotation = false;
1360 QPointF offset;
1361 double angle = 0;
1362 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
1363
1364 p->drawImage( QRectF( point.x() - s / 2.0 + offset.x(),
1365 point.y() - s / 2.0 + offset.y(),
1366 s, s ), img );
1367 }
1368 else
1369 {
1371 }
1372}
1373
1375{
1376 QVariantMap map;
1377 map[QStringLiteral( "name" )] = encodeShape( mShape );
1378 map[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mColor );
1379 map[QStringLiteral( "outline_color" )] = QgsColorUtils::colorToString( mStrokeColor );
1380 map[QStringLiteral( "size" )] = QString::number( mSize );
1381 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1382 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1383 map[QStringLiteral( "angle" )] = QString::number( mAngle );
1384 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1385 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1386 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1387 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1388 map[QStringLiteral( "outline_style" )] = QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle );
1389 map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
1390 map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
1391 map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
1392 map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
1393 map[QStringLiteral( "cap_style" )] = QgsSymbolLayerUtils::encodePenCapStyle( mPenCapStyle );
1394 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1395 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1396 return map;
1397}
1398
1418
1419void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1420{
1421 // <Graphic>
1422 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
1423 element.appendChild( graphicElem );
1424
1426 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
1428
1429 // <Rotation>
1430 QString angleFunc;
1431
1433 {
1435 }
1436 else
1437 {
1438 bool ok;
1439 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
1440 if ( !ok )
1441 {
1442 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
1443 }
1444 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
1445 {
1446 angleFunc = QString::number( angle + mAngle );
1447 }
1448 }
1449
1450 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
1451
1452 // <Displacement>
1453 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
1455}
1456
1457QString QgsSimpleMarkerSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
1458{
1459 Q_UNUSED( mmScaleFactor )
1460 Q_UNUSED( mapUnitScaleFactor )
1461#if 0
1462 QString ogrType = "3"; //default is circle
1463 if ( mName == "square" )
1464 {
1465 ogrType = "5";
1466 }
1467 else if ( mName == "triangle" )
1468 {
1469 ogrType = "7";
1470 }
1471 else if ( mName == "star" )
1472 {
1473 ogrType = "9";
1474 }
1475 else if ( mName == "circle" )
1476 {
1477 ogrType = "3";
1478 }
1479 else if ( mName == "cross" )
1480 {
1481 ogrType = "0";
1482 }
1483 else if ( mName == "x" || mName == "cross2" )
1484 {
1485 ogrType = "1";
1486 }
1487 else if ( mName == "line" )
1488 {
1489 ogrType = "10";
1490 }
1491
1492 QString ogrString;
1493 ogrString.append( "SYMBOL(" );
1494 ogrString.append( "id:" );
1495 ogrString.append( '\"' );
1496 ogrString.append( "ogr-sym-" );
1497 ogrString.append( ogrType );
1498 ogrString.append( '\"' );
1499 ogrString.append( ",c:" );
1500 ogrString.append( mColor.name() );
1501 ogrString.append( ",o:" );
1502 ogrString.append( mStrokeColor.name() );
1503 ogrString.append( QString( ",s:%1mm" ).arg( mSize ) );
1504 ogrString.append( ')' );
1505 return ogrString;
1506#endif //0
1507
1508 QString ogrString;
1509 ogrString.append( "PEN(" );
1510 ogrString.append( "c:" );
1511 ogrString.append( mColor.name() );
1512 ogrString.append( ",w:" );
1513 ogrString.append( QString::number( mSize ) );
1514 ogrString.append( "mm" );
1515 ogrString.append( ")" );
1516 return ogrString;
1517}
1518
1520{
1521 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
1522
1523 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
1524 if ( graphicElem.isNull() )
1525 return nullptr;
1526
1527 QString name = QStringLiteral( "square" );
1528 QColor color, strokeColor;
1529 double strokeWidth, size;
1530 Qt::PenStyle strokeStyle;
1531
1533 return nullptr;
1534
1535 double angle = 0.0;
1536 QString angleFunc;
1537 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
1538 {
1539 bool ok;
1540 const double d = angleFunc.toDouble( &ok );
1541 if ( ok )
1542 angle = d;
1543 }
1544
1545 QPointF offset;
1547
1548 const Qgis::MarkerShape shape = decodeShape( name );
1549
1550 double scaleFactor = 1.0;
1551 const QString uom = element.attribute( QStringLiteral( "uom" ) );
1552 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
1553 size = size * scaleFactor;
1554 offset.setX( offset.x() * scaleFactor );
1555 offset.setY( offset.y() * scaleFactor );
1556
1558 m->setOutputUnit( sldUnitSize );
1559 m->setColor( color );
1561 m->setAngle( angle );
1562 m->setOffset( offset );
1565 return m;
1566}
1567
1569{
1570 Q_UNUSED( context )
1571
1572 if ( mPolygon.count() != 0 )
1573 {
1574 p->drawPolygon( mPolygon );
1575 }
1576 else
1577 {
1578 p->drawPath( mPath );
1579 }
1580}
1581
1582bool QgsSimpleMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
1583{
1584 //data defined size?
1585 double size = mSize;
1586
1587 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
1588
1589 //data defined size
1590 bool ok = true;
1591 if ( hasDataDefinedSize )
1592 {
1594
1595 if ( ok )
1596 {
1597 switch ( mScaleMethod )
1598 {
1600 size = std::sqrt( size );
1601 break;
1603 break;
1604 }
1605 }
1606 }
1607
1609 {
1610 size *= mmMapUnitScaleFactor;
1611 }
1612
1614 {
1616 }
1617 const double halfSize = size / 2.0;
1618
1619 //strokeWidth
1620 double strokeWidth = mStrokeWidth;
1621
1623 {
1626 }
1629 {
1631 }
1632
1633 //color
1634 QColor pc = mPen.color();
1635 QColor bc = mBrush.color();
1637 {
1640 }
1642 {
1645 }
1646
1647 //offset
1648 double offsetX = 0;
1649 double offsetY = 0;
1650 markerOffset( context, offsetX, offsetY );
1651 offsetX *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1652 offsetY *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1653
1654
1655 QPointF off( offsetX, offsetY );
1656
1657 //angle
1658 double angle = mAngle + mLineAngle;
1660 {
1663 }
1664
1667 {
1669 const QString shapeName = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::Name, context.renderContext().expressionContext(), QString(), &ok );
1670 if ( ok )
1671 {
1672 shape = decodeShape( shapeName, &ok );
1673 if ( !ok )
1674 shape = mShape;
1675 }
1676 }
1677
1678 if ( angle )
1679 off = _rotatedOffset( off, angle );
1680
1682
1683 QTransform t;
1684 t.translate( shift.x() + off.x(), shift.y() - off.y() );
1685
1686 if ( !qgsDoubleNear( angle, 0.0 ) )
1687 t.rotate( -angle );
1688
1689 QPolygonF polygon;
1690 if ( shapeToPolygon( shape, polygon ) )
1691 {
1692 t.scale( halfSize, -halfSize );
1693
1694 polygon = t.map( polygon );
1695
1697 p.reserve( polygon.size() );
1698 for ( int i = 0; i < polygon.size(); i++ )
1699 {
1700 p << QgsPoint( polygon[i] );
1701 }
1702
1703 if ( mBrush.style() != Qt::NoBrush )
1704 e.writePolygon( QgsRingSequence() << p, layerName, QStringLiteral( "SOLID" ), bc );
1705 if ( mPen.style() != Qt::NoPen )
1706 e.writePolyline( p, layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1707 }
1708 else if ( shape == Qgis::MarkerShape::Circle )
1709 {
1710 shift += QPointF( off.x(), -off.y() );
1711 if ( mBrush.style() != Qt::NoBrush )
1712 e.writeFilledCircle( layerName, bc, QgsPoint( shift ), halfSize );
1713 if ( mPen.style() != Qt::NoPen )
1714 e.writeCircle( layerName, pc, QgsPoint( shift ), halfSize, QStringLiteral( "CONTINUOUS" ), strokeWidth );
1715 }
1716 else if ( shape == Qgis::MarkerShape::Line )
1717 {
1718 const QPointF pt1 = t.map( QPointF( 0, -halfSize ) );
1719 const QPointF pt2 = t.map( QPointF( 0, halfSize ) );
1720
1721 if ( mPen.style() != Qt::NoPen )
1722 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1723 }
1724 else if ( shape == Qgis::MarkerShape::Cross )
1725 {
1726 if ( mPen.style() != Qt::NoPen )
1727 {
1728 const QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
1729 const QPointF pt2 = t.map( QPointF( halfSize, 0 ) );
1730 const QPointF pt3 = t.map( QPointF( 0, -halfSize ) );
1731 const QPointF pt4 = t.map( QPointF( 0, halfSize ) );
1732
1733 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1734 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1735 }
1736 }
1737 else if ( shape == Qgis::MarkerShape::Cross2 )
1738 {
1739 if ( mPen.style() != Qt::NoPen )
1740 {
1741 const QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
1742 const QPointF pt2 = t.map( QPointF( halfSize, halfSize ) );
1743 const QPointF pt3 = t.map( QPointF( halfSize, -halfSize ) );
1744 const QPointF pt4 = t.map( QPointF( -halfSize, halfSize ) );
1745
1746 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1747 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1748 }
1749 }
1750 else if ( shape == Qgis::MarkerShape::ArrowHead )
1751 {
1752 if ( mPen.style() != Qt::NoPen )
1753 {
1754 const QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
1755 const QPointF pt2 = t.map( QPointF( 0, 0 ) );
1756 const QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
1757
1758 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1759 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1760 }
1761 }
1762 else
1763 {
1764 QgsDebugError( QStringLiteral( "Unsupported dxf marker name %1" ).arg( encodeShape( shape ) ) );
1765 return false;
1766 }
1767
1768 return true;
1769}
1770
1771
1777
1786
1792
1801
1808
1810{
1811 QRectF symbolBounds = QgsSimpleMarkerSymbolLayerBase::bounds( point, context );
1812
1813 // need to account for stroke width
1814 double penWidth = mStrokeWidth;
1815 bool ok = true;
1817 {
1820 if ( ok )
1821 {
1822 penWidth = strokeWidth;
1823 }
1824 }
1827 {
1830 if ( ok && strokeStyle == QLatin1String( "no" ) )
1831 {
1832 penWidth = 0.0;
1833 }
1834 }
1835 else if ( mStrokeStyle == Qt::NoPen )
1836 penWidth = 0;
1837
1838 //antialiasing, add 1 pixel
1839 penWidth += 1;
1840
1841 //extend bounds by pen width / 2.0
1842 symbolBounds.adjust( -penWidth / 2.0, -penWidth / 2.0,
1843 penWidth / 2.0, penWidth / 2.0 );
1844
1845 return symbolBounds;
1846}
1847
1848void QgsSimpleMarkerSymbolLayer::setColor( const QColor &color )
1849{
1850 if ( shapeIsFilled( mShape ) )
1851 {
1853 }
1854 else
1855 {
1857 }
1858}
1859
1861{
1862 if ( shapeIsFilled( mShape ) )
1863 {
1864 return fillColor();
1865 }
1866 else
1867 {
1868 return strokeColor();
1869 }
1870}
1871
1872
1873
1874
1875//
1876// QgsFilledMarkerSymbolLayer
1877//
1878
1880 : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
1881{
1882 mFill.reset( static_cast<QgsFillSymbol *>( QgsFillSymbol::createSimple( QVariantMap() ) ) );
1883}
1884
1886
1888{
1889 QString name = DEFAULT_SIMPLEMARKER_NAME;
1893
1894 if ( props.contains( QStringLiteral( "name" ) ) )
1895 name = props[QStringLiteral( "name" )].toString();
1896 if ( props.contains( QStringLiteral( "size" ) ) )
1897 size = props[QStringLiteral( "size" )].toDouble();
1898 if ( props.contains( QStringLiteral( "angle" ) ) )
1899 angle = props[QStringLiteral( "angle" )].toDouble();
1900 if ( props.contains( QStringLiteral( "scale_method" ) ) )
1901 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1902
1904 if ( props.contains( QStringLiteral( "offset" ) ) )
1905 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1906 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1907 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1908 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1909 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1910 if ( props.contains( QStringLiteral( "size_unit" ) ) )
1911 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1912 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1913 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1914 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1915 {
1916 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1917 }
1918 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1919 {
1920 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1921 }
1922
1924
1926
1927 return m;
1928}
1929
1931{
1932 return QStringLiteral( "FilledMarker" );
1933}
1934
1936{
1937 if ( mFill )
1938 {
1939 mFill->setRenderHints( mFill->renderHints() | Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol );
1940 mFill->startRender( context.renderContext(), context.fields() );
1941 }
1942
1944}
1945
1947{
1948 if ( mFill )
1949 {
1950 mFill->stopRender( context.renderContext() );
1951 }
1952}
1953
1955{
1956 QVariantMap map;
1957 map[QStringLiteral( "name" )] = encodeShape( mShape );
1958 map[QStringLiteral( "size" )] = QString::number( mSize );
1959 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1960 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1961 map[QStringLiteral( "angle" )] = QString::number( mAngle );
1962 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1963 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1964 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1965 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1966 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1967 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1968
1969 if ( mFill )
1970 {
1971 map[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mFill->color() );
1972 }
1973 return map;
1974}
1975
1984
1986{
1987 return mFill.get();
1988}
1989
1991{
1992 if ( symbol && symbol->type() == Qgis::SymbolType::Fill )
1993 {
1994 mFill.reset( static_cast<QgsFillSymbol *>( symbol ) );
1995 return true;
1996 }
1997 else
1998 {
1999 delete symbol;
2000 return false;
2001 }
2002}
2003
2005{
2006 if ( mFill )
2007 {
2008 return QgsSymbolLayerUtils::estimateMaxSymbolBleed( mFill.get(), context );
2009 }
2010 return 0;
2011}
2012
2014{
2015 QSet<QString> attr = QgsSimpleMarkerSymbolLayerBase::usedAttributes( context );
2016 if ( mFill )
2017 attr.unite( mFill->usedAttributes( context ) );
2018 return attr;
2019}
2020
2022{
2024 return true;
2025 if ( mFill && mFill->hasDataDefinedProperties() )
2026 return true;
2027 return false;
2028}
2029
2031{
2032 mColor = c;
2033 if ( mFill )
2034 mFill->setColor( c );
2035}
2036
2038{
2039 return mFill ? mFill->color() : mColor;
2040}
2041
2048
2050{
2052 if ( mFill )
2053 mFill->setOutputUnit( unit );
2054}
2055
2056void QgsFilledMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
2057{
2058 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
2059 //of the rendered point!
2060
2061 QPainter *p = context.renderContext().painter();
2062 if ( !p )
2063 {
2064 return;
2065 }
2066
2067 const double prevOpacity = mFill->opacity();
2068 mFill->setOpacity( mFill->opacity() * context.opacity() );
2069
2070 if ( shapeIsFilled( shape ) )
2071 {
2072 p->setBrush( Qt::red );
2073 }
2074 else
2075 {
2076 p->setBrush( Qt::NoBrush );
2077 }
2078 p->setPen( Qt::black );
2079
2080 const bool prevIsSubsymbol = context.renderContext().flags() & Qgis::RenderContextFlag::RenderingSubSymbol;
2082
2083 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
2084 if ( !polygon.isEmpty() )
2085 {
2086 mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
2087 }
2088 else
2089 {
2090 const QPolygonF poly = path.toFillPolygon();
2091 mFill->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
2092 }
2093
2095
2096 mFill->setOpacity( prevOpacity );
2097}
2098
2099
2101
2102
2103QgsSvgMarkerSymbolLayer::QgsSvgMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
2104{
2105 mSize = size;
2106 mAngle = angle;
2107 mOffset = QPointF( 0, 0 );
2109 mStrokeWidth = 0.2;
2111 mColor = QColor( 35, 35, 35 );
2112 mStrokeColor = QColor( 35, 35, 35 );
2113 setPath( path );
2114}
2115
2117
2119{
2120 QString name;
2124
2125 if ( props.contains( QStringLiteral( "name" ) ) )
2126 name = props[QStringLiteral( "name" )].toString();
2127 if ( props.contains( QStringLiteral( "size" ) ) )
2128 size = props[QStringLiteral( "size" )].toDouble();
2129 if ( props.contains( QStringLiteral( "angle" ) ) )
2130 angle = props[QStringLiteral( "angle" )].toDouble();
2131 if ( props.contains( QStringLiteral( "scale_method" ) ) )
2132 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
2133
2135
2136 if ( props.contains( QStringLiteral( "size_unit" ) ) )
2137 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
2138 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
2139 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
2140 if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
2141 m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
2142 if ( props.contains( QStringLiteral( "offset" ) ) )
2143 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
2144 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
2145 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
2146 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2147 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
2148 if ( props.contains( QStringLiteral( "fill" ) ) )
2149 {
2150 //pre 2.5 projects used "fill"
2151 m->setFillColor( QgsColorUtils::colorFromString( props[QStringLiteral( "fill" )].toString() ) );
2152 }
2153 else if ( props.contains( QStringLiteral( "color" ) ) )
2154 {
2155 m->setFillColor( QgsColorUtils::colorFromString( props[QStringLiteral( "color" )].toString() ) );
2156 }
2157 if ( props.contains( QStringLiteral( "outline" ) ) )
2158 {
2159 //pre 2.5 projects used "outline"
2160 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "outline" )].toString() ) );
2161 }
2162 else if ( props.contains( QStringLiteral( "outline_color" ) ) )
2163 {
2164 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "outline_color" )].toString() ) );
2165 }
2166 else if ( props.contains( QStringLiteral( "line_color" ) ) )
2167 {
2168 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "line_color" )].toString() ) );
2169 }
2170
2171 if ( props.contains( QStringLiteral( "outline-width" ) ) )
2172 {
2173 //pre 2.5 projects used "outline-width"
2174 m->setStrokeWidth( props[QStringLiteral( "outline-width" )].toDouble() );
2175 }
2176 else if ( props.contains( QStringLiteral( "outline_width" ) ) )
2177 {
2178 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
2179 }
2180 else if ( props.contains( QStringLiteral( "line_width" ) ) )
2181 {
2182 m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
2183 }
2184
2185 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
2186 {
2187 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
2188 }
2189 else if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
2190 {
2191 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
2192 }
2193 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
2194 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
2195
2196 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2197 {
2198 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2199 }
2200 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2201 {
2202 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2203 }
2204
2206
2208
2209 if ( props.contains( QStringLiteral( "parameters" ) ) )
2210 {
2211 const QVariantMap parameters = props[QStringLiteral( "parameters" )].toMap();
2213 }
2214
2215 return m;
2216}
2217
2218void QgsSvgMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
2219{
2220 const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
2221 if ( it != properties.end() )
2222 {
2223 if ( saving )
2224 {
2225 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
2226 }
2227 else
2228 {
2229 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
2230 }
2231 }
2232}
2233
2234void QgsSvgMarkerSymbolLayer::setPath( const QString &path )
2235{
2237 mHasFillParam = false;
2238 mPath = path;
2239 QColor defaultFillColor, defaultStrokeColor;
2240 double strokeWidth, fillOpacity, strokeOpacity;
2241 bool hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
2242 bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
2243 QgsApplication::svgCache()->containsParams( path, mHasFillParam, hasDefaultFillColor, defaultFillColor,
2244 hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
2245 hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2246 hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
2247 hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
2248
2249 const double newFillOpacity = hasFillOpacityParam ? fillColor().alphaF() : 1.0;
2250 const double newStrokeOpacity = hasStrokeOpacityParam ? strokeColor().alphaF() : 1.0;
2251
2252 if ( hasDefaultFillColor )
2253 {
2254 defaultFillColor.setAlphaF( newFillOpacity );
2255 setFillColor( defaultFillColor );
2256 }
2257 if ( hasDefaultFillOpacity )
2258 {
2259 QColor c = fillColor();
2260 c.setAlphaF( fillOpacity );
2261 setFillColor( c );
2262 }
2263 if ( hasDefaultStrokeColor )
2264 {
2265 defaultStrokeColor.setAlphaF( newStrokeOpacity );
2266 setStrokeColor( defaultStrokeColor );
2267 }
2268 if ( hasDefaultStrokeWidth )
2269 {
2271 }
2272 if ( hasDefaultStrokeOpacity )
2273 {
2274 QColor c = strokeColor();
2275 c.setAlphaF( strokeOpacity );
2276 setStrokeColor( c );
2277 }
2278
2280}
2281
2283{
2284 if ( mDefaultAspectRatio == 0.0 )
2285 {
2286 //size
2287 const double size = mSize;
2288 //assume 88 dpi as standard value
2289 const double widthScaleFactor = 3.465;
2290 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( mPath, size, mColor, mStrokeColor, mStrokeWidth, widthScaleFactor );
2291 // set default aspect ratio
2292 mDefaultAspectRatio = svgViewbox.isValid() ? svgViewbox.height() / svgViewbox.width() : 0.0;
2293 }
2294 return mDefaultAspectRatio;
2295}
2296
2298{
2299 const bool aPreservedAspectRatio = preservedAspectRatio();
2300 if ( aPreservedAspectRatio && !par )
2301 {
2303 }
2304 else if ( !aPreservedAspectRatio && par )
2305 {
2306 mFixedAspectRatio = 0.0;
2307 }
2308 return preservedAspectRatio();
2309}
2310
2311void QgsSvgMarkerSymbolLayer::setParameters( const QMap<QString, QgsProperty> &parameters )
2312{
2314}
2315
2316
2318{
2319 return QStringLiteral( "SvgMarker" );
2320}
2321
2326
2328{
2329 QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
2330 Q_UNUSED( context )
2331}
2332
2334{
2335 Q_UNUSED( context )
2336}
2337
2339{
2340 QPainter *p = context.renderContext().painter();
2341 if ( !p )
2342 return;
2343
2344 bool hasDataDefinedSize = false;
2345 const double scaledWidth = calculateSize( context, hasDataDefinedSize );
2346 const double devicePixelRatio = context.renderContext().devicePixelRatio();
2347 const double width = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2348
2349 //don't render symbols with a width below one or above 10,000 pixels
2350 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2351 {
2352 return;
2353 }
2354
2355 const QgsScopedQPainterState painterState( p );
2356
2357 bool hasDataDefinedAspectRatio = false;
2358 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2359 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2360
2362
2363 double strokeWidth = mStrokeWidth;
2365 {
2368 }
2370
2371 QColor fillColor = mColor;
2372 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
2373 if ( useSelectedColor && mHasFillParam )
2374 {
2376 }
2378 {
2381 }
2382
2383 QColor strokeColor = mStrokeColor;
2385 {
2388 }
2389
2390 QString path = mPath;
2392 {
2395 context.renderContext().pathResolver() );
2397 {
2398 // adjust height of data defined path
2399 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2400 context.renderContext().scaleFactor(), aspectRatio,
2401 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2402 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2403 }
2404 }
2405
2406 QPointF outputOffset;
2407 double angle = 0.0;
2408 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2409
2410 p->translate( point + outputOffset );
2411
2412 const bool rotated = !qgsDoubleNear( angle, 0 );
2413 if ( rotated )
2414 p->rotate( angle );
2415
2416 bool fitsInCache = true;
2417 bool usePict = true;
2419 if ( ( !context.renderContext().forceVectorOutput() && !rotated ) || ( useSelectedColor && rasterizeSelected ) )
2420 {
2421 QImage img = QgsApplication::svgCache()->svgAsImage( path, width * devicePixelRatio, fillColor, strokeColor, strokeWidth,
2422 context.renderContext().scaleFactor(), fitsInCache, aspectRatio,
2423 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2424 if ( fitsInCache && img.width() > 1 )
2425 {
2426 usePict = false;
2427
2428 if ( useSelectedColor )
2429 {
2431 }
2432
2433 //consider transparency
2434 if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2435 {
2436 QImage transparentImage = img.copy();
2437 QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2438 if ( devicePixelRatio == 1 )
2439 {
2440 p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
2441 }
2442 else
2443 {
2444 p->drawImage( QRectF( -transparentImage.width() / 2.0 / devicePixelRatio, -transparentImage.height() / 2.0 / devicePixelRatio,
2445 transparentImage.width() / devicePixelRatio, transparentImage.height() / devicePixelRatio
2446 ), transparentImage );
2447 }
2448 }
2449 else
2450 {
2451 if ( devicePixelRatio == 1 )
2452 {
2453 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2454 }
2455 else
2456 {
2457 p->drawImage( QRectF( -img.width() / 2.0 / devicePixelRatio, -img.height() / 2.0 / devicePixelRatio,
2458 img.width() / devicePixelRatio, img.height() / devicePixelRatio ), img );
2459 }
2460 }
2461 }
2462 }
2463
2464 if ( usePict || !fitsInCache )
2465 {
2466 p->setOpacity( context.opacity() );
2468 context.renderContext().scaleFactor(), context.renderContext().forceVectorOutput(), aspectRatio,
2469 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2470 if ( pct.width() > 1 )
2471 {
2472 const QgsScopedQPainterState painterPictureState( p );
2473 _fixQPictureDPI( p );
2474 p->drawPicture( 0, 0, pct );
2475 }
2476 }
2477
2478 // workaround issue with nested QPictures forgetting antialiasing flag - see https://github.com/qgis/QGIS/issues/22909
2480}
2481
2482double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2483{
2484 double scaledSize = mSize;
2486
2487 bool ok = true;
2488 if ( hasDataDefinedSize )
2489 {
2492 }
2493 else
2494 {
2496 if ( hasDataDefinedSize )
2497 {
2500 }
2501 }
2502
2503 if ( hasDataDefinedSize && ok )
2504 {
2505 switch ( mScaleMethod )
2506 {
2508 scaledSize = std::sqrt( scaledSize );
2509 break;
2511 break;
2512 }
2513 }
2514
2515 return scaledSize;
2516}
2517
2518double QgsSvgMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2519{
2521 if ( !hasDataDefinedAspectRatio )
2522 return mFixedAspectRatio;
2523
2525 return 0.0;
2526
2527 double scaledAspectRatio = mDefaultAspectRatio;
2528 if ( mFixedAspectRatio > 0.0 )
2529 scaledAspectRatio = mFixedAspectRatio;
2530
2531 const double defaultHeight = mSize * scaledAspectRatio;
2532 scaledAspectRatio = defaultHeight / scaledSize;
2533
2534 bool ok = true;
2535 double scaledHeight = scaledSize * scaledAspectRatio;
2537 {
2538 context.setOriginalValueVariable( defaultHeight );
2540 }
2541
2542 if ( hasDataDefinedAspectRatio && ok )
2543 {
2544 switch ( mScaleMethod )
2545 {
2547 scaledHeight = sqrt( scaledHeight );
2548 break;
2550 break;
2551 }
2552 }
2553
2554 scaledAspectRatio = scaledHeight / scaledSize;
2555
2556 return scaledAspectRatio;
2557}
2558
2559void QgsSvgMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
2560{
2561 //offset
2562 double offsetX = 0;
2563 double offsetY = 0;
2564 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
2565 offset = QPointF( offsetX, offsetY );
2566
2569 {
2572 }
2573
2575 if ( hasDataDefinedRotation )
2576 {
2577 // For non-point markers, "dataDefinedRotation" means following the
2578 // shape (shape-data defined). For them, "field-data defined" does
2579 // not work at all. TODO: if "field-data defined" ever gets implemented
2580 // we'll need a way to distinguish here between the two, possibly
2581 // using another flag in renderHints()
2582 const QgsFeature *f = context.feature();
2583 if ( f )
2584 {
2585 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
2586 {
2587 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2588 angle += m2p.mapRotation();
2589 }
2590 }
2591 }
2592
2593 if ( angle )
2595}
2596
2597
2599{
2600 QVariantMap map;
2601 map[QStringLiteral( "name" )] = mPath;
2602 map[QStringLiteral( "size" )] = QString::number( mSize );
2603 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2604 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2605 map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2606 map[QStringLiteral( "angle" )] = QString::number( mAngle );
2607 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2608 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2609 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2610 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2611 map[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mColor );
2612 map[QStringLiteral( "outline_color" )] = QgsColorUtils::colorToString( mStrokeColor );
2613 map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
2614 map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2615 map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2616 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
2617 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
2618
2619 map[QStringLiteral( "parameters" )] = QgsProperty::propertyMapToVariantMap( mParameters );
2620
2621 return map;
2622}
2623
2630
2653
2659
2661{
2663 if ( unit != mStrokeWidthUnit )
2664 {
2666 }
2667 return unit;
2668}
2669
2675
2684
2685void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
2686{
2687 // <Graphic>
2688 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2689 element.appendChild( graphicElem );
2690
2691 // encode a parametric SVG reference
2692 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
2695
2696 // <Rotation>
2697 QString angleFunc;
2698 bool ok;
2699 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2700 if ( !ok )
2701 {
2702 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
2703 }
2704 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2705 {
2706 angleFunc = QString::number( angle + mAngle );
2707 }
2708
2709 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2710
2711 // <Displacement>
2712 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2714}
2715
2717{
2718 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
2719
2720 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
2721 if ( graphicElem.isNull() )
2722 return nullptr;
2723
2724 QString path, mimeType;
2725 // Unused and to be DEPRECATED in externalGraphicFromSld
2726 QColor fillColor_;
2727 double size;
2728
2729 if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor_, size ) )
2730 return nullptr;
2731
2732 double scaleFactor = 1.0;
2733 const QString uom = element.attribute( QStringLiteral( "uom" ) );
2734 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
2735 size = size * scaleFactor;
2736
2737 if ( mimeType != QLatin1String( "image/svg+xml" ) )
2738 return nullptr;
2739
2740 double angle = 0.0;
2741 QString angleFunc;
2742 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2743 {
2744 bool ok;
2745 const double d = angleFunc.toDouble( &ok );
2746 if ( ok )
2747 angle = d;
2748 }
2749
2750 QPointF offset;
2752
2753 // Extract parameters from URL
2754 QString realPath { path };
2755 QUrl svgUrl { path };
2756
2757 // Because color definition can start with '#', the url parsing won't recognize the query string entirely
2758 QUrlQuery queryString;
2759
2760 if ( svgUrl.hasQuery() && svgUrl.hasFragment() )
2761 {
2762 const QString queryPart { path.mid( path.indexOf( '?' ) + 1 ) };
2763 queryString.setQuery( queryPart );
2764 }
2765
2766 // Remove query for simple file paths
2767 if ( svgUrl.scheme().isEmpty() || svgUrl.isLocalFile() )
2768 {
2769 svgUrl.setQuery( QString() );
2770 realPath = svgUrl.path();
2771 }
2772
2774
2775 QMap<QString, QgsProperty> params;
2776
2777 bool ok;
2778
2779 if ( queryString.hasQueryItem( QStringLiteral( "fill" ) ) )
2780 {
2781 const QColor fillColor { queryString.queryItemValue( QStringLiteral( "fill" ) ) };
2782 m->setFillColor( fillColor );
2783 }
2784
2785 if ( queryString.hasQueryItem( QStringLiteral( "fill-opacity" ) ) )
2786 {
2787 const double alpha { queryString.queryItemValue( QStringLiteral( "fill-opacity" ) ).toDouble( &ok ) };
2788 if ( ok )
2789 {
2790 params.insert( QStringLiteral( "fill-opacity" ), QgsProperty::fromValue( alpha ) );
2791 }
2792 }
2793
2794 if ( queryString.hasQueryItem( QStringLiteral( "outline" ) ) )
2795 {
2796 const QColor strokeColor { queryString.queryItemValue( QStringLiteral( "outline" ) ) };
2798 }
2799
2800 if ( queryString.hasQueryItem( QStringLiteral( "outline-opacity" ) ) )
2801 {
2802 const double alpha { queryString.queryItemValue( QStringLiteral( "outline-opacity" ) ).toDouble( &ok ) };
2803 if ( ok )
2804 {
2805 params.insert( QStringLiteral( "outline-opacity" ), QgsProperty::fromValue( alpha ) );
2806 }
2807 }
2808
2809 if ( queryString.hasQueryItem( QStringLiteral( "outline-width" ) ) )
2810 {
2811 const int width { queryString.queryItemValue( QStringLiteral( "outline-width" ) ).toInt( &ok )};
2812 if ( ok )
2813 {
2814 m->setStrokeWidth( width );
2815 }
2816 }
2817
2818 if ( ! params.isEmpty() )
2819 {
2820 m->setParameters( params );
2821 }
2822
2823 m->setOutputUnit( sldUnitSize );
2824 m->setAngle( angle );
2825 m->setOffset( offset );
2826 return m;
2827}
2828
2829bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
2830{
2831 //size
2832 double size = mSize;
2833
2834 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
2835
2836 bool ok = true;
2837 if ( hasDataDefinedSize )
2838 {
2841 }
2842
2843 if ( hasDataDefinedSize && ok )
2844 {
2845 switch ( mScaleMethod )
2846 {
2848 size = std::sqrt( size );
2849 break;
2851 break;
2852 }
2853 }
2854
2856 {
2857 size *= mmMapUnitScaleFactor;
2858 }
2859
2860//offset, angle
2861 QPointF offset = mOffset;
2862
2864 {
2867 const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
2868 if ( ok )
2869 offset = res;
2870 }
2871 const double offsetX = offset.x();
2872 const double offsetY = offset.y();
2873
2874 QPointF outputOffset( offsetX, offsetY );
2875
2876 double angle = mAngle + mLineAngle;
2878 {
2881 }
2882
2883 if ( angle )
2884 outputOffset = _rotatedOffset( outputOffset, angle );
2885
2887
2888 QString path = mPath;
2890 {
2893 context.renderContext().pathResolver() );
2894 }
2895
2896 double strokeWidth = mStrokeWidth;
2898 {
2901 }
2903
2904 QColor fillColor = mColor;
2906 {
2909 }
2910
2911 QColor strokeColor = mStrokeColor;
2913 {
2916 }
2917
2919
2920 const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, size, fillColor, strokeColor, strokeWidth,
2922 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2923
2924 QSvgRenderer r( svgContent );
2925 if ( !r.isValid() )
2926 return false;
2927
2928 QgsDxfPaintDevice pd( &e );
2929 pd.setDrawingSize( QSizeF( r.defaultSize() ) );
2930
2931 QSizeF outSize( r.defaultSize() );
2932 outSize.scale( size, size, Qt::KeepAspectRatio );
2933
2934 QPainter p;
2935 p.begin( &pd );
2936 if ( !qgsDoubleNear( angle, 0.0 ) )
2937 {
2938 p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
2939 p.rotate( angle );
2940 p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
2941 }
2942 pd.setShift( shift + QPointF( outputOffset.x(), -outputOffset.y() ) );
2943 pd.setOutputSize( QRectF( -outSize.width() / 2.0, -outSize.height() / 2.0, outSize.width(), outSize.height() ) );
2944 pd.setLayer( layerName );
2945 r.render( &p );
2946 p.end();
2947 return true;
2948}
2949
2951{
2952 bool hasDataDefinedSize = false;
2953 double scaledWidth = calculateSize( context, hasDataDefinedSize );
2954
2955 bool hasDataDefinedAspectRatio = false;
2956 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2957 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2958
2959 scaledWidth = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2960 scaledHeight = context.renderContext().convertToPainterUnits( scaledHeight, mSizeUnit, mSizeMapUnitScale );
2961
2962 //don't render symbols with size below one or above 10,000 pixels
2963 if ( static_cast< int >( scaledWidth ) < 1 || 10000.0 < scaledWidth )
2964 {
2965 return QRectF();
2966 }
2967
2968 QPointF outputOffset;
2969 double angle = 0.0;
2970 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2971
2972 double strokeWidth = mStrokeWidth;
2974 {
2977 }
2979
2980 QString path = mPath;
2982 {
2985 context.renderContext().pathResolver() );
2987 {
2988 // need to get colors to take advantage of cached SVGs
2989 QColor fillColor = mColor;
2991 {
2994 }
2995
2996 const QColor strokeColor = mStrokeColor;
2998 {
3001 }
3002
3004
3005 // adjust height of data defined path
3006 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
3007 context.renderContext().scaleFactor(), aspectRatio,
3008 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
3009 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
3010 }
3011 }
3012
3013 QTransform transform;
3014 // move to the desired position
3015 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3016
3017 if ( !qgsDoubleNear( angle, 0.0 ) )
3018 transform.rotate( angle );
3019
3020 //antialiasing
3021 strokeWidth += 1.0 / 2.0;
3022
3023 QRectF symbolBounds = transform.mapRect( QRectF( -scaledWidth / 2.0,
3024 -scaledHeight / 2.0,
3025 scaledWidth,
3026 scaledHeight ) );
3027
3028 //extend bounds by pen width / 2.0
3029 symbolBounds.adjust( -strokeWidth / 2.0, -strokeWidth / 2.0,
3030 strokeWidth / 2.0, strokeWidth / 2.0 );
3031
3032 return symbolBounds;
3033}
3034
3036
3037QgsRasterMarkerSymbolLayer::QgsRasterMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
3038 : mPath( path )
3039{
3040 mSize = size;
3041 mAngle = angle;
3042 mOffset = QPointF( 0, 0 );
3045}
3046
3048
3050{
3051 QString path;
3055
3056 if ( props.contains( QStringLiteral( "imageFile" ) ) )
3057 path = props[QStringLiteral( "imageFile" )].toString();
3058 if ( props.contains( QStringLiteral( "size" ) ) )
3059 size = props[QStringLiteral( "size" )].toDouble();
3060 if ( props.contains( QStringLiteral( "angle" ) ) )
3061 angle = props[QStringLiteral( "angle" )].toDouble();
3062 if ( props.contains( QStringLiteral( "scale_method" ) ) )
3063 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
3064
3065 std::unique_ptr< QgsRasterMarkerSymbolLayer > m = std::make_unique< QgsRasterMarkerSymbolLayer >( path, size, angle, scaleMethod );
3066 m->setCommonProperties( props );
3067 return m.release();
3068}
3069
3070void QgsRasterMarkerSymbolLayer::setCommonProperties( const QVariantMap &properties )
3071{
3072 if ( properties.contains( QStringLiteral( "alpha" ) ) )
3073 {
3074 setOpacity( properties[QStringLiteral( "alpha" )].toDouble() );
3075 }
3076
3077 if ( properties.contains( QStringLiteral( "size_unit" ) ) )
3078 setSizeUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "size_unit" )].toString() ) );
3079 if ( properties.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3080 setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3081 if ( properties.contains( QStringLiteral( "fixedAspectRatio" ) ) )
3082 setFixedAspectRatio( properties[QStringLiteral( "fixedAspectRatio" )].toDouble() );
3083
3084 if ( properties.contains( QStringLiteral( "offset" ) ) )
3085 setOffset( QgsSymbolLayerUtils::decodePoint( properties[QStringLiteral( "offset" )].toString() ) );
3086 if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
3087 setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )].toString() ) );
3088 if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3089 setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3090
3091 if ( properties.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3092 {
3093 setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( properties[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3094 }
3095 if ( properties.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3096 {
3097 setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( properties[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3098 }
3099
3102}
3103
3104void QgsRasterMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
3105{
3106 const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
3107 if ( it != properties.end() && it.value().userType() == QMetaType::Type::QString )
3108 {
3109 if ( saving )
3110 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
3111 else
3112 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
3113 }
3114}
3115
3116void QgsRasterMarkerSymbolLayer::setPath( const QString &path )
3117{
3118 mPath = path;
3120}
3121
3123{
3124 const bool aPreservedAspectRatio = preservedAspectRatio();
3125 if ( aPreservedAspectRatio && !par )
3126 {
3128 }
3129 else if ( !aPreservedAspectRatio && par )
3130 {
3131 mFixedAspectRatio = 0.0;
3132 }
3133 return preservedAspectRatio();
3134}
3135
3137{
3138 if ( mDefaultAspectRatio == 0.0 )
3139 {
3141 mDefaultAspectRatio = ( !size.isNull() && size.isValid() && size.width() > 0 ) ? static_cast< double >( size.height() ) / static_cast< double >( size.width() ) : 0.0;
3142 }
3143 return mDefaultAspectRatio;
3144}
3145
3147{
3148 return QStringLiteral( "RasterMarker" );
3149}
3150
3155
3157{
3158 QPainter *p = context.renderContext().painter();
3159 if ( !p )
3160 return;
3161
3162 QString path = mPath;
3164 {
3167 }
3168
3169 if ( path.isEmpty() )
3170 return;
3171
3172 double width = 0.0;
3173 double height = 0.0;
3174
3175 bool hasDataDefinedSize = false;
3176 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3177
3178 bool hasDataDefinedAspectRatio = false;
3179 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3180
3181 QPointF outputOffset;
3182 double angle = 0.0;
3183
3184 // RenderPercentage Unit Type takes original image size
3186 {
3188 if ( size.isEmpty() )
3189 return;
3190
3191 width = ( scaledSize * static_cast< double >( size.width() ) ) / 100.0;
3192 height = ( scaledSize * static_cast< double >( size.height() ) ) / 100.0;
3193
3194 // don't render symbols with size below one or above 10,000 pixels
3195 if ( static_cast< int >( width ) < 1 || 10000.0 < width || static_cast< int >( height ) < 1 || 10000.0 < height )
3196 return;
3197
3198 calculateOffsetAndRotation( context, width, height, outputOffset, angle );
3199 }
3200 else
3201 {
3202 width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3203 height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3204
3205 if ( preservedAspectRatio() && path != mPath )
3206 {
3208 if ( !size.isNull() && size.isValid() && size.width() > 0 )
3209 {
3210 height = width * ( static_cast< double >( size.height() ) / static_cast< double >( size.width() ) );
3211 }
3212 }
3213
3214 // don't render symbols with size below one or above 10,000 pixels
3215 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
3216 return;
3217
3218 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3219 }
3220
3221 const QgsScopedQPainterState painterState( p );
3222 p->translate( point + outputOffset );
3223
3224 const bool rotated = !qgsDoubleNear( angle, 0 );
3225 if ( rotated )
3226 p->rotate( angle );
3227
3228 double opacity = mOpacity;
3230 {
3233 }
3234 opacity *= context.opacity();
3235
3236 QImage img = fetchImage( context.renderContext(), path, QSize( width, preservedAspectRatio() ? 0 : width * aspectRatio ), preservedAspectRatio(), opacity );
3237 if ( !img.isNull() )
3238 {
3239 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
3240 if ( useSelectedColor )
3241 {
3243 }
3244
3245 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
3246 }
3247}
3248
3249QImage QgsRasterMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
3250{
3251 bool cached = false;
3252 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking );
3253}
3254
3255double QgsRasterMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
3256{
3257 double scaledSize = mSize;
3259
3260 bool ok = true;
3261 if ( hasDataDefinedSize )
3262 {
3265 }
3266 else
3267 {
3269 if ( hasDataDefinedSize )
3270 {
3273 }
3274 }
3275
3276 if ( hasDataDefinedSize && ok )
3277 {
3278 switch ( mScaleMethod )
3279 {
3281 scaledSize = std::sqrt( scaledSize );
3282 break;
3284 break;
3285 }
3286 }
3287
3288 return scaledSize;
3289}
3290
3291double QgsRasterMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
3292{
3294 if ( !hasDataDefinedAspectRatio )
3295 return mFixedAspectRatio;
3296
3298 return 0.0;
3299
3300 double scaledAspectRatio = mDefaultAspectRatio;
3301 if ( mFixedAspectRatio > 0.0 )
3302 scaledAspectRatio = mFixedAspectRatio;
3303
3304 const double defaultHeight = mSize * scaledAspectRatio;
3305 scaledAspectRatio = defaultHeight / scaledSize;
3306
3307 bool ok = true;
3308 double scaledHeight = scaledSize * scaledAspectRatio;
3310 {
3311 context.setOriginalValueVariable( defaultHeight );
3313 }
3314
3315 if ( hasDataDefinedAspectRatio && ok )
3316 {
3317 switch ( mScaleMethod )
3318 {
3320 scaledHeight = sqrt( scaledHeight );
3321 break;
3323 break;
3324 }
3325 }
3326
3327 scaledAspectRatio = scaledHeight / scaledSize;
3328
3329 return scaledAspectRatio;
3330}
3331
3332void QgsRasterMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
3333{
3334 //offset
3335 double offsetX = 0;
3336 double offsetY = 0;
3337 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
3338 offset = QPointF( offsetX, offsetY );
3339
3342 {
3345 }
3346
3348 if ( hasDataDefinedRotation )
3349 {
3350 const QgsFeature *f = context.feature();
3351 if ( f )
3352 {
3353 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3354 {
3355 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3356 angle += m2p.mapRotation();
3357 }
3358 }
3359 }
3360
3361 if ( angle )
3363}
3364
3365
3367{
3368 QVariantMap map;
3369 map[QStringLiteral( "imageFile" )] = mPath;
3370 map[QStringLiteral( "size" )] = QString::number( mSize );
3371 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3372 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3373 map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
3374 map[QStringLiteral( "angle" )] = QString::number( mAngle );
3375 map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
3376 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3377 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3378 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3379 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
3380 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3381 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3382 return map;
3383}
3384
3386{
3387 std::unique_ptr< QgsRasterMarkerSymbolLayer > m = std::make_unique< QgsRasterMarkerSymbolLayer >( mPath, mSize, mAngle );
3388 copyCommonProperties( m.get() );
3389 return m.release();
3390}
3391
3392
3407
3413
3415{
3416 return QColor();
3417}
3418
3423
3428
3430{
3431 bool hasDataDefinedSize = false;
3432 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3433 const double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3434 bool hasDataDefinedAspectRatio = false;
3435 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3436 const double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3437
3438 //don't render symbols with size below one or above 10,000 pixels
3439 if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
3440 {
3441 return QRectF();
3442 }
3443
3444 QPointF outputOffset;
3445 double angle = 0.0;
3446 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3447
3448 QTransform transform;
3449
3450 // move to the desired position
3451 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3452
3453 if ( !qgsDoubleNear( angle, 0.0 ) )
3454 transform.rotate( angle );
3455
3456 QRectF symbolBounds = transform.mapRect( QRectF( -width / 2.0,
3457 -height / 2.0,
3458 width,
3459 height ) );
3460
3461 return symbolBounds;
3462}
3463
3465
3466QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QString chr, double pointSize, const QColor &color, double angle )
3467{
3468 mFontFamily = fontFamily;
3469 mString = chr;
3470 mColor = color;
3471 mAngle = angle;
3472 mSize = pointSize;
3473 mOrigSize = pointSize;
3475 mOffset = QPointF( 0, 0 );
3477 mStrokeColor = DEFAULT_FONTMARKER_BORDERCOLOR;
3478 mStrokeWidth = 0.0;
3479 mStrokeWidthUnit = Qgis::RenderUnit::Millimeters;
3480 mPenJoinStyle = DEFAULT_FONTMARKER_JOINSTYLE;
3481}
3482
3484
3486{
3488 QString string = DEFAULT_FONTMARKER_CHR;
3489 double pointSize = DEFAULT_FONTMARKER_SIZE;
3492
3493 if ( props.contains( QStringLiteral( "font" ) ) )
3494 fontFamily = props[QStringLiteral( "font" )].toString();
3495 if ( props.contains( QStringLiteral( "chr" ) ) && props[QStringLiteral( "chr" )].toString().length() > 0 )
3496 {
3497 string = props["chr"].toString();
3498 const thread_local QRegularExpression charRegExp( QStringLiteral( "%1([0-9]+)%1" ).arg( FONTMARKER_CHR_FIX ) );
3499 QRegularExpressionMatch match = charRegExp.match( string );
3500 while ( match.hasMatch() )
3501 {
3502 QChar replacement = QChar( match.captured( 1 ).toUShort() );
3503 string = string.mid( 0, match.capturedStart( 0 ) ) + replacement + string.mid( match.capturedEnd( 0 ) );
3504 match = charRegExp.match( string );
3505 }
3506 }
3507
3508 if ( props.contains( QStringLiteral( "size" ) ) )
3509 pointSize = props[QStringLiteral( "size" )].toDouble();
3510 if ( props.contains( QStringLiteral( "color" ) ) )
3511 color = QgsColorUtils::colorFromString( props[QStringLiteral( "color" )].toString() );
3512 if ( props.contains( QStringLiteral( "angle" ) ) )
3513 angle = props[QStringLiteral( "angle" )].toDouble();
3514
3516
3517 if ( props.contains( QStringLiteral( "font_style" ) ) )
3518 m->setFontStyle( props[QStringLiteral( "font_style" )].toString() );
3519 if ( props.contains( QStringLiteral( "outline_color" ) ) )
3520 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "outline_color" )].toString() ) );
3521 if ( props.contains( QStringLiteral( "outline_width" ) ) )
3522 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
3523 if ( props.contains( QStringLiteral( "offset" ) ) )
3524 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
3525 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
3526 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
3527 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3528 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3529 if ( props.contains( QStringLiteral( "size_unit" ) ) )
3530 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
3531 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3532 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3533 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
3534 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
3535 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
3536 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
3537 if ( props.contains( QStringLiteral( "joinstyle" ) ) )
3538 m->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() ) );
3539 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3540 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3541 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3542 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3543
3545
3546 return m;
3547}
3548
3550{
3551 return QStringLiteral( "FontMarker" );
3552}
3553
3558
3560{
3561 QColor brushColor = mColor;
3562 QColor penColor = mStrokeColor;
3563
3564 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
3565 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
3566
3567 mBrush = QBrush( brushColor );
3568 mPen = QPen( penColor );
3569 mPen.setJoinStyle( mPenJoinStyle );
3570 mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
3571
3572 mFont = QgsFontUtils::createFont( QgsApplication::fontManager()->processFontFamilyName( mFontFamily ) );
3573 if ( !mFontStyle.isEmpty() )
3574 {
3575 mFont.setStyleName( QgsFontUtils::translateNamedStyle( mFontStyle ) );
3576 }
3577
3578 double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3579 mNonZeroFontSize = !qgsDoubleNear( sizePixels, 0.0 );
3580
3581 if ( mNonZeroFontSize && sizePixels > MAX_FONT_CHARACTER_SIZE_IN_PIXELS )
3582 {
3583 // if font is too large (e.g using map units and map is very zoomed in), then we limit
3584 // the font size and instead scale up the painter.
3585 // this avoids issues with massive font sizes (eg https://github.com/qgis/QGIS/issues/42270)
3586 mFontSizeScale = sizePixels / MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3587 sizePixels = MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3588 }
3589 else
3590 mFontSizeScale = 1.0;
3591
3592 // if a non zero, but small pixel size results, round up to 2 pixels so that a "dot" is at least visible
3593 // (if we set a <=1 pixel size here Qt will reset the font to a default size, leading to much too large symbols)
3594 mFont.setPixelSize( std::max( 2, static_cast< int >( std::round( sizePixels ) ) ) );
3595 mFontMetrics.reset( new QFontMetrics( mFont ) );
3596 mChrWidth = mFontMetrics->horizontalAdvance( mString );
3597 mChrOffset = QPointF( mChrWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3598 mOrigSize = mSize; // save in case the size would be data defined
3599
3600 // use caching only when not using a data defined character
3604 if ( mUseCachedPath )
3605 {
3606 QPointF chrOffset = mChrOffset;
3607 double chrWidth;
3608 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3609 mCachedPath = QPainterPath();
3610 mCachedPath.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3611 }
3612}
3613
3615{
3616 Q_UNUSED( context )
3617}
3618
3619QString QgsFontMarkerSymbolLayer::characterToRender( QgsSymbolRenderContext &context, QPointF &charOffset, double &charWidth )
3620{
3621 charOffset = mChrOffset;
3622 QString stringToRender = mString;
3624 {
3625 context.setOriginalValueVariable( mString );
3627 if ( stringToRender != mString )
3628 {
3629 charWidth = mFontMetrics->horizontalAdvance( stringToRender );
3630 charOffset = QPointF( charWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3631 }
3632 }
3633 return stringToRender;
3634}
3635
3636void QgsFontMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
3637 double scaledSize,
3638 bool &hasDataDefinedRotation,
3639 QPointF &offset,
3640 double &angle ) const
3641{
3642 //offset
3643 double offsetX = 0;
3644 double offsetY = 0;
3645 markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
3646 offset = QPointF( offsetX, offsetY );
3647
3648 //angle
3649 bool ok = true;
3652 {
3655
3656 // If the expression evaluation was not successful, fallback to static value
3657 if ( !ok )
3659 }
3660
3661 hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation;
3662 if ( hasDataDefinedRotation )
3663 {
3664 // For non-point markers, "dataDefinedRotation" means following the
3665 // shape (shape-data defined). For them, "field-data defined" does
3666 // not work at all. TODO: if "field-data defined" ever gets implemented
3667 // we'll need a way to distinguish here between the two, possibly
3668 // using another flag in renderHints()
3669 const QgsFeature *f = context.feature();
3670 if ( f )
3671 {
3672 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3673 {
3674 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3675 angle += m2p.mapRotation();
3676 }
3677 }
3678 }
3679
3680 if ( angle )
3682}
3683
3684double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context )
3685{
3686 double scaledSize = mSize;
3687 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
3688
3689 bool ok = true;
3690 if ( hasDataDefinedSize )
3691 {
3694 }
3695
3696 if ( hasDataDefinedSize && ok )
3697 {
3698 switch ( mScaleMethod )
3699 {
3701 scaledSize = std::sqrt( scaledSize );
3702 break;
3704 break;
3705 }
3706 }
3707 return scaledSize;
3708}
3709
3711{
3712 QPainter *p = context.renderContext().painter();
3713 if ( !p || !mNonZeroFontSize )
3714 return;
3715
3716 QTransform transform;
3717
3718 bool ok;
3719 QColor brushColor = mColor;
3721 {
3724 }
3725 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
3726 brushColor = useSelectedColor ? context.renderContext().selectionColor() : brushColor;
3727 if ( !useSelectedColor || !SELECTION_IS_OPAQUE )
3728 {
3729 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
3730 }
3731 mBrush.setColor( brushColor );
3732
3733 QColor penColor = mStrokeColor;
3735 {
3738 }
3739 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
3740
3741 double penWidth = context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3743 {
3744 context.setOriginalValueVariable( mStrokeWidth );
3746 if ( ok )
3747 {
3748 penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3749 }
3750 }
3751
3753 {
3756 if ( ok )
3757 {
3758 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
3759 }
3760 }
3761
3762 const QgsScopedQPainterState painterState( p );
3763 p->setBrush( mBrush );
3764 if ( !qgsDoubleNear( penWidth, 0.0 ) )
3765 {
3766 mPen.setColor( penColor );
3767 mPen.setWidthF( penWidth );
3768 p->setPen( mPen );
3769 }
3770 else
3771 {
3772 p->setPen( Qt::NoPen );
3773 }
3774
3776 {
3777 context.setOriginalValueVariable( mFontFamily );
3779 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( ok ? fontFamily : mFontFamily );
3780 QgsFontUtils::setFontFamily( mFont, processedFamily );
3781 }
3783 {
3784 context.setOriginalValueVariable( mFontStyle );
3787 }
3789 {
3790 mFontMetrics.reset( new QFontMetrics( mFont ) );
3791 }
3792
3793 QPointF chrOffset = mChrOffset;
3794 double chrWidth;
3795 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3796
3797 const double sizeToRender = calculateSize( context );
3798
3799 bool hasDataDefinedRotation = false;
3800 QPointF offset;
3801 double angle = 0;
3802 calculateOffsetAndRotation( context, sizeToRender, hasDataDefinedRotation, offset, angle );
3803
3804 p->translate( point.x() + offset.x(), point.y() + offset.y() );
3805
3806 if ( !qgsDoubleNear( angle, 0.0 ) )
3807 transform.rotate( angle );
3808
3809 if ( !qgsDoubleNear( sizeToRender, mOrigSize ) )
3810 {
3811 const double s = sizeToRender / mOrigSize;
3812 transform.scale( s, s );
3813 }
3814
3815 if ( !qgsDoubleNear( mFontSizeScale, 1.0 ) )
3816 transform.scale( mFontSizeScale, mFontSizeScale );
3817
3818 if ( mUseCachedPath )
3819 {
3820 p->drawPath( transform.map( mCachedPath ) );
3821 }
3822 else
3823 {
3824 QPainterPath path;
3825 path.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3826 p->drawPath( transform.map( path ) );
3827 }
3828}
3829
3831{
3832 QVariantMap props;
3833 props[QStringLiteral( "font" )] = mFontFamily;
3834 props[QStringLiteral( "font_style" )] = mFontStyle;
3835 QString chr = mString;
3836 for ( int i = 0; i < 32; i++ )
3837 {
3838 if ( i == 9 || i == 10 || i == 13 )
3839 {
3840 continue;
3841 }
3842 chr.replace( QChar( i ), QStringLiteral( "%1%2%1" ).arg( FONTMARKER_CHR_FIX, QString::number( i ) ) );
3843 }
3844 props[QStringLiteral( "chr" )] = chr;
3845 props[QStringLiteral( "size" )] = QString::number( mSize );
3846 props[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3847 props[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3848 props[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mColor );
3849 props[QStringLiteral( "outline_color" )] = QgsColorUtils::colorToString( mStrokeColor );
3850 props[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
3851 props[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
3852 props[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
3853 props[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
3854 props[QStringLiteral( "angle" )] = QString::number( mAngle );
3855 props[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3856 props[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3857 props[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3858 props[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3859 props[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3860 return props;
3861}
3862
3864{
3865 QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( mFontFamily, mString, mSize, mColor, mAngle );
3866 m->setFontStyle( mFontStyle );
3867 m->setStrokeColor( mStrokeColor );
3868 m->setStrokeWidth( mStrokeWidth );
3869 m->setStrokeWidthUnit( mStrokeWidthUnit );
3870 m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
3871 m->setPenJoinStyle( mPenJoinStyle );
3872 m->setOffset( mOffset );
3875 m->setSizeUnit( mSizeUnit );
3880 copyPaintEffect( m );
3881 return m;
3882}
3883
3884void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
3885{
3886 // <Graphic>
3887 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
3888 element.appendChild( graphicElem );
3889
3890 const QString fontPath = QStringLiteral( "ttf://%1" ).arg( mFontFamily );
3891 int markIndex = !mString.isEmpty() ? mString.at( 0 ).unicode() : 0;
3892 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
3893 QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, QStringLiteral( "ttf" ), &markIndex, mColor, size );
3894
3895 // <Rotation>
3896 QString angleFunc;
3897 bool ok;
3898 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
3899 if ( !ok )
3900 {
3901 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
3902 }
3903 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
3904 {
3905 angleFunc = QString::number( angle + mAngle );
3906 }
3907 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
3908
3909 // <Displacement>
3910 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
3912}
3913
3920
3922{
3924 mStrokeWidthUnit = unit;
3925}
3926
3928{
3929 QPointF chrOffset = mChrOffset;
3930 double chrWidth = mChrWidth;
3931 //calculate width of rendered character
3932 ( void )characterToRender( context, chrOffset, chrWidth );
3933
3934 if ( !mFontMetrics )
3935 mFontMetrics.reset( new QFontMetrics( mFont ) );
3936
3937 double scaledSize = calculateSize( context );
3938 if ( !qgsDoubleNear( scaledSize, mOrigSize ) )
3939 {
3940 chrWidth *= scaledSize / mOrigSize;
3941 }
3942 chrWidth *= mFontSizeScale;
3943
3944 bool hasDataDefinedRotation = false;
3945 QPointF offset;
3946 double angle = 0;
3947 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
3948 scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3949
3950 QTransform transform;
3951
3952 // move to the desired position
3953 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
3954
3955 if ( !qgsDoubleNear( angle, 0.0 ) )
3956 transform.rotate( angle );
3957
3958 QRectF symbolBounds = transform.mapRect( QRectF( -chrWidth / 2.0,
3959 -scaledSize / 2.0,
3960 chrWidth,
3961 scaledSize ) );
3962 return symbolBounds;
3963}
3964
3966{
3967 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
3968
3969 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
3970 if ( graphicElem.isNull() )
3971 return nullptr;
3972
3973 QString name, format;
3974 QColor color;
3975 double size;
3976 int chr;
3977
3978 if ( !QgsSymbolLayerUtils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
3979 return nullptr;
3980
3981 if ( !name.startsWith( QLatin1String( "ttf://" ) ) || format != QLatin1String( "ttf" ) )
3982 return nullptr;
3983
3984 const QString fontFamily = name.mid( 6 );
3985
3986 double angle = 0.0;
3987 QString angleFunc;
3988 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
3989 {
3990 bool ok;
3991 const double d = angleFunc.toDouble( &ok );
3992 if ( ok )
3993 angle = d;
3994 }
3995
3996 QPointF offset;
3998
3999 double scaleFactor = 1.0;
4000 const QString uom = element.attribute( QStringLiteral( "uom" ) );
4001 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
4002 offset.setX( offset.x() * scaleFactor );
4003 offset.setY( offset.y() * scaleFactor );
4004 size = size * scaleFactor;
4005
4007 m->setOutputUnit( sldUnitSize );
4008 m->setAngle( angle );
4009 m->setOffset( offset );
4010 return m;
4011}
4012
4013void QgsFontMarkerSymbolLayer::resolveFonts( const QVariantMap &properties, const QgsReadWriteContext &context )
4014{
4015 const QString fontFamily = properties.value( QStringLiteral( "font" ), DEFAULT_FONTMARKER_FONT ).toString();
4016 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( fontFamily );
4017 QString matched;
4018 if ( !QgsFontUtils::fontFamilyMatchOnSystem( processedFamily )
4019 && !QgsApplication::fontManager()->tryToDownloadFontFamily( processedFamily, matched ) )
4020 {
4021 context.pushMessage( QObject::tr( "Font “%1” not available on system" ).arg( processedFamily ) );
4022 }
4023}
4024
4026{
4027 QMap<QString, QgsProperty>::iterator it = mParameters.begin();
4028 for ( ; it != mParameters.end(); ++it )
4029 it.value().prepare( context.renderContext().expressionContext() );
4030
4032}
4033
4034
4036{
4037 QSet<QString> attrs = QgsMarkerSymbolLayer::usedAttributes( context );
4038
4039 QMap<QString, QgsProperty>::const_iterator it = mParameters.constBegin();
4040 for ( ; it != mParameters.constEnd(); ++it )
4041 {
4042 attrs.unite( it.value().referencedFields( context.expressionContext(), true ) );
4043 }
4044
4045 return attrs;
4046}
4047
4048//
4049// QgsAnimatedMarkerSymbolLayer
4050//
4051
4052QgsAnimatedMarkerSymbolLayer::QgsAnimatedMarkerSymbolLayer( const QString &path, double size, double angle )
4053 : QgsRasterMarkerSymbolLayer( path, size, angle )
4054{
4055
4056}
4057
4059
4061{
4062 QString path;
4065
4066 if ( properties.contains( QStringLiteral( "imageFile" ) ) )
4067 path = properties[QStringLiteral( "imageFile" )].toString();
4068 if ( properties.contains( QStringLiteral( "size" ) ) )
4069 size = properties[QStringLiteral( "size" )].toDouble();
4070 if ( properties.contains( QStringLiteral( "angle" ) ) )
4071 angle = properties[QStringLiteral( "angle" )].toDouble();
4072
4073 std::unique_ptr< QgsAnimatedMarkerSymbolLayer > m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( path, size, angle );
4074 m->setFrameRate( properties.value( QStringLiteral( "frameRate" ), QStringLiteral( "10" ) ).toDouble() );
4075
4076 m->setCommonProperties( properties );
4077 return m.release();
4078}
4079
4081{
4082 return QStringLiteral( "AnimatedMarker" );
4083}
4084
4086{
4087 QVariantMap res = QgsRasterMarkerSymbolLayer::properties();
4088 res.insert( QStringLiteral( "frameRate" ), mFrameRateFps );
4089 return res;
4090}
4091
4093{
4094 std::unique_ptr< QgsAnimatedMarkerSymbolLayer > m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( mPath, mSize, mAngle );
4095 m->setFrameRate( mFrameRateFps );
4096 copyCommonProperties( m.get() );
4097 return m.release();
4098}
4099
4101{
4103
4104 mPreparedPaths.clear();
4106 {
4108 mStaticPath = true;
4109 }
4110 else
4111 {
4112 mStaticPath = false;
4113 }
4114}
4115
4116QImage QgsAnimatedMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
4117{
4118 if ( !mStaticPath && !mPreparedPaths.contains( path ) )
4119 {
4121 mPreparedPaths.insert( path );
4122 }
4123
4124 const long long mapFrameNumber = context.currentFrame();
4126 const double markerAnimationDuration = totalFrameCount / mFrameRateFps;
4127
4128 double animationTimeSeconds = 0;
4129 if ( mapFrameNumber >= 0 && context.frameRate() > 0 )
4130 {
4131 // render is part of an animation, so we base the calculated frame on that
4132 animationTimeSeconds = mapFrameNumber / context.frameRate();
4133 }
4134 else
4135 {
4136 // render is outside of animation, so base the calculated frame on the current epoch
4137 animationTimeSeconds = QDateTime::currentMSecsSinceEpoch() / 1000.0;
4138 }
4139
4140 const double markerAnimationProgressSeconds = std::fmod( animationTimeSeconds, markerAnimationDuration );
4141 const int movieFrame = static_cast< int >( std::floor( markerAnimationProgressSeconds * mFrameRateFps ) );
4142
4143 bool cached = false;
4144 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking, 96, movieFrame );
4145}
4146
@ DynamicRotation
Rotation of symbol may be changed during rendering and symbol should not be cached.
@ IsSymbolLayerSubSymbol
Symbol is being rendered as a sub-symbol of a QgsSymbolLayer (since QGIS 3.38)
@ CanCalculateMaskGeometryPerFeature
If present, indicates that mask geometry can safely be calculated per feature for the symbol layer....
ScaleMethod
Scale methods.
Definition qgis.h:434
@ ScaleDiameter
Calculate scale by the diameter.
@ ScaleArea
Calculate scale by the area.
QFlags< SymbolLayerFlag > SymbolLayerFlags
Symbol layer flags.
Definition qgis.h:644
MarkerShape
Marker shapes.
Definition qgis.h:2633
@ Pentagon
Pentagon.
@ EquilateralTriangle
Equilateral triangle.
@ SemiCircle
Semi circle (top half)
@ QuarterCircle
Quarter circle (top left quarter)
@ LeftHalfTriangle
Left half of triangle.
@ ArrowHead
Right facing arrow head (unfilled, lines only)
@ ParallelogramRight
Parallelogram that slants right (since QGIS 3.28)
@ AsteriskFill
A filled asterisk shape (since QGIS 3.18)
@ Octagon
Octagon (since QGIS 3.18)
@ HalfArc
A line-only half arc (since QGIS 3.20)
@ Line
Vertical line.
@ QuarterSquare
Quarter square (top left quarter)
@ Triangle
Triangle.
@ Cross2
Rotated cross (lines only), 'x' shape.
@ Trapezoid
Trapezoid (since QGIS 3.28)
@ ArrowHeadFilled
Right facing filled arrow head.
@ Shield
A shape consisting of a triangle attached to a rectangle (since QGIS 3.28)
@ HalfSquare
Half square (left half)
@ CrossFill
Solid filled cross.
@ Decagon
Decagon (since QGIS 3.28)
@ RoundedSquare
A square with rounded corners (since QGIS 3.28)
@ RightHalfTriangle
Right half of triangle.
@ ThirdCircle
One third circle (top left third)
@ ThirdArc
A line-only one third arc (since QGIS 3.20)
@ SquareWithCorners
A square with diagonal corners (since QGIS 3.18)
@ QuarterArc
A line-only one quarter arc (since QGIS 3.20)
@ DiamondStar
A 4-sided star (since QGIS 3.28)
@ Cross
Cross (lines only)
@ ParallelogramLeft
Parallelogram that slants left (since QGIS 3.28)
@ Heart
Heart (since QGIS 3.28)
@ DiagonalHalfSquare
Diagonal half square (bottom left half)
RenderUnit
Rendering size units.
Definition qgis.h:4494
@ Percentage
Percentage of another measurement (e.g., canvas size, feature size)
@ Millimeters
Millimeters.
@ Unknown
Mixed or unknown units.
@ MapUnits
Map units.
@ MetersInMapUnits
Meters value as Map units.
@ RenderingSubSymbol
Set whenever a sub-symbol of a parent symbol is currently being rendered. Can be used during symbol a...
@ RenderSymbolPreview
The render is for a symbol preview only and map based properties may not be available,...
@ RenderBlocking
Render and load remote sources in the same thread to ensure rendering remote sources (svg and images)...
@ Fill
Fill symbol.
QColor valueAsColor(int key, const QgsExpressionContext &context, const QColor &defaultColor=QColor(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a color.
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double.
QString valueAsString(int key, const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a string.
Animated marker symbol layer class.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsAnimatedMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
~QgsAnimatedMarkerSymbolLayer() override
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
QImage fetchImage(QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity) const override
Fetches the image to render.
QgsAnimatedMarkerSymbolLayer(const QString &path=QString(), double size=DEFAULT_RASTERMARKER_SIZE, double angle=DEFAULT_RASTERMARKER_ANGLE)
Constructor for animated marker symbol layer using the specified source image path.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates an animated marker symbol layer from a string map of properties.
QString layerType() const override
Returns a string that represents this layer type.
static QgsImageCache * imageCache()
Returns the application's image cache, used for caching resampled versions of raster images.
static QgsSvgCache * svgCache()
Returns the application's SVG cache, used for caching SVG images and handling parameter replacement w...
static QgsFontManager * fontManager()
Returns the application font manager, which manages available fonts and font installation for the QGI...
static QColor colorFromString(const QString &string)
Decodes a string into a color value.
static QString colorToString(const QColor &color)
Encodes a color into a string value.
Exports QGIS layers to the DXF format.
void writeFilledCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius)
Write filled circle (as hatch)
void writeCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius, const QString &lineStyleName, double width)
Write circle (as polyline)
static double mapUnitScaleFactor(double scale, Qgis::RenderUnit symbolUnits, Qgis::DistanceUnit mapUnits, double mapUnitsPerPixel=1.0)
Returns scale factor for conversion to map units.
void writeLine(const QgsPoint &pt1, const QgsPoint &pt2, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Write line (as a polyline)
void writePolygon(const QgsRingSequence &polygon, const QString &layer, const QString &hatchPattern, const QColor &color)
Draw dxf filled polygon (HATCH)
Qgis::DistanceUnit mapUnits() const
Retrieve map units.
double symbologyScale() const
Returns the reference scale for output.
void clipValueToMapUnitScale(double &value, const QgsMapUnitScale &scale, double pixelToMMFactor) const
Clips value to scale minimum/maximum.
void writePolyline(const QgsPointSequence &line, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Draw dxf primitives (LWPOLYLINE)
A paint device for drawing into dxf files.
void setShift(QPointF shift)
void setLayer(const QString &layer)
void setOutputSize(const QRectF &r)
void setDrawingSize(QSizeF size)
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
QgsGeometry geometry
Definition qgsfeature.h:69
bool hasGeometry() const
Returns true if the feature has an associated geometry.
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
static QgsFillSymbol * createSimple(const QVariantMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties.
Filled marker symbol layer, consisting of a shape which is rendered using a QgsFillSymbol.
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QColor color() const override
Returns the "representative" color of the symbol layer.
QgsFilledMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QgsSymbol * subSymbol() override
Returns the symbol's sub symbol, if present.
~QgsFilledMarkerSymbolLayer() override
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer's subsymbol. takes ownership of the passed symbol.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
void setColor(const QColor &c) override
Sets the "representative" color for the symbol layer.
QString layerType() const override
Returns a string that represents this layer type.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsFilledMarkerSymbolLayer.
QgsFilledMarkerSymbolLayer(Qgis::MarkerShape shape=Qgis::MarkerShape::Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructor for QgsFilledMarkerSymbolLayer.
QString processFontFamilyName(const QString &name) const
Processes a font family name, applying any matching fontFamilyReplacements() to the name.
void setStrokeWidthUnit(Qgis::RenderUnit unit)
Sets the stroke width unit.
~QgsFontMarkerSymbolLayer() override
void setStrokeColor(const QColor &color) override
Sets the stroke color for the symbol layer.
QgsFontMarkerSymbolLayer(const QString &fontFamily=DEFAULT_FONTMARKER_FONT, QString chr=DEFAULT_FONTMARKER_CHR, double pointSize=DEFAULT_FONTMARKER_SIZE, const QColor &color=DEFAULT_FONTMARKER_COLOR, double angle=DEFAULT_FONTMARKER_ANGLE)
Constructs a font marker symbol layer.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
Sets the stroke width map unit scale.
double strokeWidth() const
Returns the marker's stroke width.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
void setFontStyle(const QString &style)
Sets the font style for the font which will be used to render the point.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QString fontStyle() const
Returns the font style for the associated font which will be used to render the point.
QString fontFamily() const
Returns the font family name for the associated font which will be used to render the point.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
void writeSldMarker(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Writes the symbol layer definition as a SLD XML element.
void setStrokeWidth(double width)
Set's the marker's stroke width.
static void resolveFonts(const QVariantMap &properties, const QgsReadWriteContext &context)
Resolves fonts from a properties map, raising warnings in the specified context if the required fonts...
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
void setPenJoinStyle(Qt::PenJoinStyle style)
Sets the stroke join style.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsFontMarkerSymbolLayer from an SLD XML element.
QgsFontMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
QString layerType() const override
Returns a string that represents this layer type.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsFontMarkerSymbolLayer from a property map (see properties())
Qgis::SymbolLayerFlags flags() const override
Returns flags which control the symbol layer's behavior.
static QString translateNamedStyle(const QString &namedStyle)
Returns the localized named style of a font, if such a translation is available.
static QFont createFont(const QString &family, int pointSize=-1, int weight=-1, bool italic=false)
Creates a font with the specified family.
static bool fontFamilyMatchOnSystem(const QString &family, QString *chosen=nullptr, bool *match=nullptr)
Check whether font family is on system.
static bool updateFontViaStyle(QFont &f, const QString &fontstyle, bool fallback=false)
Updates font with named style and retain all font properties.
static void setFontFamily(QFont &font, const QString &family)
Sets the family for a font object.
Qgis::GeometryType type
QSize originalSize(const QString &path, bool blocking=false) const
Returns the original size (in pixels) of the image at the specified path.
int totalFrameCount(const QString &path, bool blocking=false)
Returns the total frame count of the image at the specified path.
QImage pathAsImage(const QString &path, const QSize size, const bool keepAspectRatio, const double opacity, bool &fitsInCache, bool blocking=false, double targetDpi=96, int frameNumber=-1, bool *isMissing=nullptr)
Returns the specified path rendered as an image.
void prepareAnimation(const QString &path)
Prepares for optimized retrieval of frames for the animation at the given path.
static void overlayColor(QImage &image, const QColor &color)
Overlays a color onto an image.
Perform transforms between map coordinates and device coordinates.
double mapUnitsPerPixel() const
Returns the current map units per pixel.
double mapRotation() const
Returns the current map rotation in degrees (clockwise).
Struct for storing maximum and minimum scales for measurements in map units.
Abstract base class for marker symbol layers.
double mSize
Marker size.
Qgis::RenderUnit mOffsetUnit
Offset units.
QPointF offset() const
Returns the marker's offset, which is the horizontal and vertical displacement which the rendered mar...
double mLineAngle
Line rotation angle (see setLineAngle() for details)
HorizontalAnchorPoint
Symbol horizontal anchor points.
void setOffsetUnit(Qgis::RenderUnit unit)
Sets the units for the symbol's offset.
void setAngle(double angle)
Sets the rotation angle for the marker.
Qgis::ScaleMethod scaleMethod() const
Returns the method to use for scaling the marker's size.
Qgis::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
void setVerticalAnchorPoint(VerticalAnchorPoint v)
Sets the vertical anchor point for positioning the symbol.
QPointF mOffset
Marker offset.
void setHorizontalAnchorPoint(HorizontalAnchorPoint h)
Sets the horizontal anchor point for positioning the symbol.
QgsMapUnitScale mapUnitScale() const override
void setOffset(QPointF offset)
Sets the marker's offset, which is the horizontal and vertical displacement which the rendered marker...
void setSizeMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol's size.
double size() const
Returns the symbol size.
QgsMapUnitScale mOffsetMapUnitScale
Offset map unit scale.
HorizontalAnchorPoint mHorizontalAnchorPoint
Horizontal anchor point.
static QPointF _rotatedOffset(QPointF offset, double angle)
Adjusts a marker offset to account for rotation.
Qgis::ScaleMethod mScaleMethod
Marker size scaling method.
QgsMapUnitScale mSizeMapUnitScale
Marker size map unit scale.
Qgis::RenderUnit mSizeUnit
Marker size unit.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units for the symbol's size.
VerticalAnchorPoint
Symbol vertical anchor points.
void markerOffset(QgsSymbolRenderContext &context, double &offsetX, double &offsetY) const
Calculates the required marker offset, including both the symbol offset and any displacement required...
VerticalAnchorPoint mVerticalAnchorPoint
Vertical anchor point.
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol's offset.
double mAngle
Marker rotation angle, in degrees clockwise from north.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
double angle() const
Returns the rotation angle for the marker, in degrees clockwise from north.
void setMapUnitScale(const QgsMapUnitScale &scale) override
Resolves relative paths into absolute paths and vice versa.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const final
Returns the calculated value of the property with the specified key from within the collection.
bool isActive(int key) const final
Returns true if the collection contains an active property with the specified key.
QgsProperty property(int key) const final
Returns a matching property from the collection, if one exists.
QString asExpression() const
Returns an expression string representing the state of the property, or an empty string if the proper...
static QVariantMap propertyMapToVariantMap(const QMap< QString, QgsProperty > &propertyMap)
Convert a map of QgsProperty to a map of QVariant This is useful to save a map of properties.
static QMap< QString, QgsProperty > variantMapToPropertyMap(const QVariantMap &variantMap)
Convert a map of QVariant to a map of QgsProperty This is useful to restore a map of properties.
static QgsProperty fromValue(const QVariant &value, bool isActive=true)
Returns a new StaticProperty created from the specified value.
Raster marker symbol layer class.
double mFixedAspectRatio
The marker fixed aspect ratio.
QColor color() const override
Returns the "representative" color of the symbol layer.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsMapUnitScale mapUnitScale() const override
void copyCommonProperties(QgsRasterMarkerSymbolLayer *other) const
Copies common properties to another layer.
void setOpacity(double opacity)
Set the marker opacity.
QString path() const
Returns the marker raster image path.
double calculateAspectRatio(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio) const
Calculates the marker aspect ratio between width and height.
QgsRasterMarkerSymbolLayer(const QString &path=QString(), double size=DEFAULT_SVGMARKER_SIZE, double angle=DEFAULT_SVGMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructs raster marker symbol layer with picture from given absolute path to a raster image file.
void setPath(const QString &path)
Set the marker raster image path.
virtual QImage fetchImage(QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity) const
Fetches the image to render.
double defaultAspectRatio() const
Returns the default marker aspect ratio between width and height, 0 if not yet calculated.
Qgis::SymbolLayerFlags flags() const override
Returns flags which control the symbol layer's behavior.
void setFixedAspectRatio(double ratio)
Set the marker aspect ratio between width and height to be used in rendering, if the value set is low...
void setMapUnitScale(const QgsMapUnitScale &scale) override
void setCommonProperties(const QVariantMap &properties)
Sets common class properties from a properties map.
QgsRasterMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
bool preservedAspectRatio() const
Returns the preserved aspect ratio value, true if fixed aspect ratio has been lower or equal to 0.
static void resolvePaths(QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving)
Turns relative paths in properties map to absolute when reading and vice versa when writing.
~QgsRasterMarkerSymbolLayer() override
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a raster marker symbol layer from a string map of properties.
double mOpacity
The marker default opacity.
double updateDefaultAspectRatio()
Calculates the default marker aspect ratio between width and height.
double mDefaultAspectRatio
The marker default aspect ratio.
bool setPreservedAspectRatio(bool par)
Set preserved the marker aspect ratio between width and height.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QString layerType() const override
Returns a string that represents this layer type.
double opacity() const
Returns the marker opacity.
The class is used as a container of context for various read/write operations on other objects.
void pushMessage(const QString &message, Qgis::MessageLevel level=Qgis::MessageLevel::Warning) const
Append a message to the context.
Contains information about the context of a rendering operation.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
double convertToPainterUnits(double size, Qgis::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QPainter * painter()
Returns the destination QPainter for the render operation.
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
QgsExpressionContext & expressionContext()
Gets the expression context.
bool forceVectorOutput() const
Returns true if rendering operations should use vector operations instead of any faster raster shortc...
long long currentFrame() const
Returns the current frame number of the map (in frames per second), for maps which are part of an ani...
float devicePixelRatio() const
Returns the device pixel ratio.
void setFlag(Qgis::RenderContextFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
double frameRate() const
Returns the frame rate of the map, for maps which are part of an animation.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
QColor selectionColor() const
Returns the color to use when rendering selected features.
Qgis::RenderContextFlags flags() const
Returns combination of flags used for rendering.
const QgsPathResolver & pathResolver() const
Returns the path resolver for conversion between relative and absolute paths during rendering operati...
Scoped object for saving and restoring a QPainter object's state.
Abstract base class for simple marker symbol layers.
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
void calculateOffsetAndRotation(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle) const
Calculates the marker offset and rotation.
Qgis::MarkerShape mShape
Symbol shape.
QPainterPath mPath
Painter path representing shape. If mPolygon is empty then the shape is stored in mPath.
bool shapeToPolygon(Qgis::MarkerShape shape, QPolygonF &polygon) const
Creates a polygon representing the specified shape.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
static QList< Qgis::MarkerShape > availableShapes()
Returns a list of all available shape types.
~QgsSimpleMarkerSymbolLayerBase() override
static bool shapeIsFilled(Qgis::MarkerShape shape)
Returns true if a symbol shape has a fill.
QPolygonF mPolygon
Polygon of points in shape. If polygon is empty then shape is using mPath.
Qgis::MarkerShape shape() const
Returns the shape for the rendered marker symbol.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
QgsSimpleMarkerSymbolLayerBase(Qgis::MarkerShape shape=Qgis::MarkerShape::Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructor for QgsSimpleMarkerSymbolLayerBase.
static QString encodeShape(Qgis::MarkerShape shape)
Encodes a shape to its string representation.
double calculateSize(QgsSymbolRenderContext &context, bool &hasDataDefinedSize) const
Calculates the desired size of the marker, considering data defined size overrides.
static Qgis::MarkerShape decodeShape(const QString &name, bool *ok=nullptr)
Attempts to decode a string representation of a shape name to the corresponding shape.
bool prepareMarkerPath(Qgis::MarkerShape symbol)
Prepares the layer for drawing the specified shape (QPainterPath version)
bool prepareMarkerShape(Qgis::MarkerShape shape)
Prepares the layer for drawing the specified shape (QPolygonF version)
Simple marker symbol layer, consisting of a rendered shape with solid fill color and an stroke.
QPen mSelPen
QPen to use as stroke of selected symbols.
void setColor(const QColor &color) override
Sets the "representative" color for the symbol layer.
QImage mSelCache
Cached image of selected marker, if using cached version.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QImage mCache
Cached image of marker, if using cached version.
QBrush mSelBrush
QBrush to use as fill of selected symbols.
void setFillColor(const QColor &color) override
Sets the fill color for the symbol layer.
Qt::PenJoinStyle penJoinStyle() const
Returns the marker's stroke join style (e.g., miter, bevel, etc).
void drawMarker(QPainter *p, QgsSymbolRenderContext &context)
Draws the marker shape in the specified painter.
QPen mPen
QPen corresponding to marker's stroke style.
Qgis::RenderUnit mStrokeWidthUnit
Stroke width units.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsSimpleMarkerSymbolLayer.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
void setMapUnitScale(const QgsMapUnitScale &scale) override
Qgis::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
void setStrokeWidthUnit(Qgis::RenderUnit u)
Sets the unit for the width of the marker's stroke.
QColor color() const override
Returns the "representative" color of the symbol layer.
QgsMapUnitScale mapUnitScale() const override
Qt::PenStyle mStrokeStyle
Stroke style.
QgsSimpleMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsSimpleMarkerSymbolLayer from an SLD XML element.
~QgsSimpleMarkerSymbolLayer() override
Qt::PenCapStyle mPenCapStyle
Stroke pen cap style.
QString layerType() const override
Returns a string that represents this layer type.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
Sets the map scale for the width of the marker's stroke.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
void setStrokeStyle(Qt::PenStyle strokeStyle)
Sets the marker's stroke style (e.g., solid, dashed, etc)
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QColor fillColor() const override
Returns the fill color for the symbol layer.
QColor strokeColor() const override
Returns the marker's stroke color.
QBrush mBrush
QBrush corresponding to marker's fill style.
void setStrokeWidth(double w)
Sets the width of the marker's stroke.
void setStrokeColor(const QColor &color) override
Sets the marker's stroke color.
Qt::PenStyle strokeStyle() const
Returns the marker's stroke style (e.g., solid, dashed, etc)
bool mUsingCache
true if using cached images of markers for drawing.
Qgis::SymbolLayerFlags flags() const override
Returns flags which control the symbol layer's behavior.
void setPenCapStyle(Qt::PenCapStyle style)
Sets the marker's stroke cap style (e.g., flat, round, etc).
bool writeDxf(QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift=QPointF(0.0, 0.0)) const override
write as DXF
void writeSldMarker(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Writes the symbol layer definition as a SLD XML element.
static const int MAXIMUM_CACHE_WIDTH
Maximum width/height of cache image.
QString ogrFeatureStyle(double mmScaleFactor, double mapUnitScaleFactor) const override
bool prepareCache(QgsSymbolRenderContext &context)
Prepares cache image.
QgsMapUnitScale mStrokeWidthMapUnitScale
Stroke width map unit scale.
QgsSimpleMarkerSymbolLayer(Qgis::MarkerShape shape=Qgis::MarkerShape::Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD, const QColor &color=DEFAULT_SIMPLEMARKER_COLOR, const QColor &strokeColor=DEFAULT_SIMPLEMARKER_BORDERCOLOR, Qt::PenJoinStyle penJoinStyle=DEFAULT_SIMPLEMARKER_JOINSTYLE)
Constructor for QgsSimpleMarkerSymbolLayer.
double strokeWidth() const
Returns the width of the marker's stroke.
Qt::PenJoinStyle mPenJoinStyle
Stroke pen join style.
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
QSizeF svgViewboxSize(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >())
Calculates the viewbox size of a (possibly cached) SVG file.
QPicture svgAsPicture(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool forceVectorOutput=false, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >())
Returns an SVG drawing as a QPicture.
void containsParams(const QString &path, bool &hasFillParam, QColor &defaultFillColor, bool &hasStrokeParam, QColor &defaultStrokeColor, bool &hasStrokeWidthParam, double &defaultStrokeWidth, bool blocking=false) const
Tests if an SVG file contains parameters for fill, stroke color, stroke width.
QImage svgAsImage(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool &fitsInCache, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >())
Returns an SVG drawing as a QImage.
QByteArray svgContent(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >(), bool *isMissingImage=nullptr)
Gets the SVG content corresponding to the given path.
QgsSvgMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
QColor fillColor() const override
Returns the fill color for the symbol layer.
QgsMapUnitScale mapUnitScale() const override
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates the symbol.
double mDefaultAspectRatio
The marker default aspect ratio.
QString layerType() const override
Returns a string that represents this layer type.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
QString path() const
Returns the marker SVG path.
Qgis::SymbolLayerFlags flags() const override
Returns flags which control the symbol layer's behavior.
bool preservedAspectRatio() const
Returns the preserved aspect ratio value, true if fixed aspect ratio has been lower or equal to 0.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void prepareExpressions(const QgsSymbolRenderContext &context) override
Prepares all data defined property expressions for evaluation.
QMap< QString, QgsProperty > mParameters
bool setPreservedAspectRatio(bool par)
Set preserved the marker aspect ratio between width and height.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
Qgis::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
double calculateAspectRatio(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio) const
Calculates the marker aspect ratio between width and height.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
static QgsSymbolLayer * createFromSld(QDomElement &element)
void setStrokeWidthUnit(Qgis::RenderUnit unit)
Sets the units for the stroke width.
void setStrokeColor(const QColor &c) override
Sets the stroke color for the symbol layer.
void setMapUnitScale(const QgsMapUnitScale &scale) override
double updateDefaultAspectRatio()
Calculates the default marker aspect ratio between width and height.
QColor strokeColor() const override
Returns the stroke color for the symbol layer.
void setFillColor(const QColor &color) override
Sets the fill color for the symbol layer.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
QgsSvgMarkerSymbolLayer(const QString &path, double size=DEFAULT_SVGMARKER_SIZE, double angle=DEFAULT_SVGMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructs SVG marker symbol layer with picture from given absolute path to a SVG file.
void writeSldMarker(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Writes the symbol layer definition as a SLD XML element.
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
~QgsSvgMarkerSymbolLayer() override
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
static void resolvePaths(QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving)
Turns relative paths in properties map to absolute when reading and vice versa when writing.
QMap< QString, QgsProperty > parameters() const
Returns the dynamic SVG parameters.
QgsMapUnitScale mStrokeWidthMapUnitScale
void setParameters(const QMap< QString, QgsProperty > &parameters)
Sets the dynamic SVG parameters.
double mFixedAspectRatio
The marker fixed aspect ratio.
bool writeDxf(QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift=QPointF(0.0, 0.0)) const override
write as DXF
void setFixedAspectRatio(double ratio)
Set the marker aspect ratio between width and height to be used in rendering, if the value set is low...
void setPath(const QString &path)
Set the marker SVG path.
static bool externalMarkerFromSld(QDomElement &element, QString &path, QString &format, int &markIndex, QColor &color, double &size)
static bool rotationFromSldElement(QDomElement &element, QString &rotationFunc)
static QString encodePenStyle(Qt::PenStyle style)
static Qt::PenJoinStyle decodePenJoinStyle(const QString &str)
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
static QgsStringMap evaluatePropertiesMap(const QMap< QString, QgsProperty > &propertiesMap, const QgsExpressionContext &context)
Evaluates a map of properties using the given context and returns a variant map with evaluated expres...
static bool displacementFromSldElement(QDomElement &element, QPointF &offset)
static QString svgSymbolPathToName(const QString &path, const QgsPathResolver &pathResolver)
Determines an SVG symbol's name from its path.
static QPointF toPoint(const QVariant &value, bool *ok=nullptr)
Converts a value to a point.
static void multiplyImageOpacity(QImage *image, qreal opacity)
Multiplies opacity of image pixel values with a (global) transparency value.
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
static double rescaleUom(double size, Qgis::RenderUnit unit, const QVariantMap &props)
Rescales the given size based on the uomScale found in the props, if any is found,...
static Qt::PenCapStyle decodePenCapStyle(const QString &str)
static bool externalGraphicFromSld(QDomElement &element, QString &path, QString &mime, QColor &color, double &size)
static void parametricSvgToSld(QDomDocument &doc, QDomElement &graphicElem, const QString &path, const QColor &fillColor, double size, const QColor &strokeColor, double strokeWidth)
Encodes a reference to a parametric SVG into SLD, as a succession of parametric SVG using URL paramet...
static Qgis::ScaleMethod decodeScaleMethod(const QString &str)
Decodes a symbol scale method from a string.
static QString encodePenCapStyle(Qt::PenCapStyle style)
static void externalMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &path, const QString &format, int *markIndex=nullptr, const QColor &color=QColor(), double size=-1)
static bool wellKnownMarkerFromSld(QDomElement &element, QString &name, QColor &color, QColor &strokeColor, Qt::PenStyle &strokeStyle, double &strokeWidth, double &size)
static void createDisplacementElement(QDomDocument &doc, QDomElement &element, QPointF offset)
static QString svgSymbolNameToPath(const QString &name, const QgsPathResolver &pathResolver)
Determines an SVG symbol's path from its name.
static QString encodeColor(const QColor &color)
static Qgis::RenderUnit decodeSldUom(const QString &str, double *scaleFactor=nullptr)
Decodes a SLD unit of measure string to a render unit.
static double estimateMaxSymbolBleed(QgsSymbol *symbol, const QgsRenderContext &context)
Returns the maximum estimated bleed for the symbol.
static void wellKnownMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &name, const QColor &color, const QColor &strokeColor, Qt::PenStyle strokeStyle, double strokeWidth=-1, double size=-1)
static QString encodeScaleMethod(Qgis::ScaleMethod scaleMethod)
Encodes a symbol scale method to a string.
static Qt::PenStyle decodePenStyle(const QString &str)
static void createRotationElement(QDomDocument &doc, QDomElement &element, const QString &rotationFunc)
static QString encodePoint(QPointF point)
Encodes a QPointF to a string.
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
static QPointF decodePoint(const QString &string)
Decodes a QSizeF from a string.
bool shouldRenderUsingSelectionColor(const QgsSymbolRenderContext &context) const
Returns true if the symbol layer should be rendered using the selection color from the render context...
static const bool SELECTION_IS_OPAQUE
Whether styles for selected features ignore symbol alpha.
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns the set of attributes referenced by the layer.
void copyDataDefinedProperties(QgsSymbolLayer *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
@ StrokeStyle
Stroke style (eg solid, dashed)
@ Name
Name, eg shape name for simple markers.
@ Character
Character, eg for font marker symbol layers.
@ StrokeColor
Stroke color.
@ CapStyle
Line cap style.
@ JoinStyle
Line join style.
@ StrokeWidth
Stroke width.
@ FontFamily
Font family.
@ Offset
Symbol offset.
@ Height
Symbol height.
void restoreOldDataDefinedProperties(const QVariantMap &stringMap)
Restores older data defined properties from string map.
virtual void startRender(QgsSymbolRenderContext &context)=0
Called before a set of rendering operations commences on the supplied render context.
virtual void prepareExpressions(const QgsSymbolRenderContext &context)
Prepares all data defined property expressions for evaluation.
virtual void setColor(const QColor &color)
Sets the "representative" color for the symbol layer.
virtual QColor color() const
Returns the "representative" color of the symbol layer.
virtual void setOutputUnit(Qgis::RenderUnit unit)
Sets the units to use for sizes and widths within the symbol layer.
void copyPaintEffect(QgsSymbolLayer *destLayer) const
Copies paint effect of this layer to another symbol layer.
virtual Qgis::SymbolLayerFlags flags() const
Returns flags which control the symbol layer's behavior.
QgsPropertyCollection mDataDefinedProperties
virtual bool hasDataDefinedProperties() const
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
const QgsFeature * feature() const
Returns the current feature being rendered.
QgsFields fields() const
Fields of the layer.
Qgis::SymbolRenderHints renderHints() const
Returns the rendering hint flags for the symbol.
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for data defined symbology.
qreal opacity() const
Returns the opacity for the symbol.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
Abstract base class for all rendered symbols.
Definition qgssymbol.h:94
Qgis::SymbolType type() const
Returns the symbol's type.
Definition qgssymbol.h:156
static Q_INVOKABLE Qgis::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:5465
QMap< QString, QString > QgsStringMap
Definition qgis.h:6003
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
#define DEG2RAD(x)
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
#define QgsDebugError(str)
Definition qgslogger.h:38
Q_GUI_EXPORT int qt_defaultDpiX()
Q_GUI_EXPORT int qt_defaultDpiY()
#define DEFAULT_FONTMARKER_JOINSTYLE
#define DEFAULT_RASTERMARKER_ANGLE
#define DEFAULT_RASTERMARKER_SIZE
#define DEFAULT_SVGMARKER_ANGLE
#define DEFAULT_SIMPLEMARKER_JOINSTYLE
#define DEFAULT_FONTMARKER_CHR
#define DEFAULT_SIMPLEMARKER_BORDERCOLOR
#define DEFAULT_SIMPLEMARKER_SIZE
#define DEFAULT_SIMPLEMARKER_NAME
#define DEFAULT_SIMPLEMARKER_ANGLE
#define DEFAULT_SVGMARKER_SIZE
#define DEFAULT_FONTMARKER_FONT
#define DEFAULT_FONTMARKER_BORDERCOLOR
#define DEFAULT_FONTMARKER_ANGLE
#define DEFAULT_FONTMARKER_COLOR
#define DEFAULT_FONTMARKER_SIZE
#define DEFAULT_SIMPLEMARKER_COLOR
#define DEFAULT_SCALE_METHOD
#define FONTMARKER_CHR_FIX