QGIS API Documentation 3.99.0-Master (a26b91b364d)
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#include "qgspainting.h"
33#include "qgssldexportcontext.h"
34
35#include <QMimeDatabase>
36#include <QMimeType>
37#include <QPainter>
38#include <QSvgRenderer>
39#include <QFileInfo>
40#include <QDir>
41#include <QDomDocument>
42#include <QDomElement>
43#include <QUrlQuery>
44
45#include <cmath>
46
47static constexpr int MAX_FONT_CHARACTER_SIZE_IN_PIXELS = 500;
48
50
51
52//
53// QgsSimpleMarkerSymbolLayerBase
54//
55
99
101 : mShape( shape )
102{
103 mSize = size;
104 mAngle = angle;
105 mOffset = QPointF( 0, 0 );
109}
110
112
114{
115 switch ( shape )
116 {
147 return true;
148
156 return false;
157 }
158 return true;
159}
160
162{
163 const bool hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation
165 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
166
167 // use either QPolygonF or QPainterPath for drawing
168 if ( !prepareMarkerShape( mShape ) ) // drawing as a polygon
169 {
170 prepareMarkerPath( mShape ); // drawing as a painter path
171 }
172
173 QTransform transform;
174
175 // scale the shape (if the size is not going to be modified)
176 if ( !hasDataDefinedSize )
177 {
178 double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
180 {
181 // rendering for symbol previews -- an size in meters in map units can't be calculated, so treat the size as millimeters
182 // and clamp it to a reasonable range. It's the best we can do in this situation!
183 scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
184 }
185
186 const double half = scaledSize / 2.0;
187 transform.scale( half, half );
188 }
189
190 // rotate if the rotation is not going to be changed during the rendering
191 if ( !hasDataDefinedRotation && !qgsDoubleNear( mAngle, 0.0 ) )
192 {
193 transform.rotate( mAngle );
194 }
195
196 if ( !mPolygon.isEmpty() )
197 mPolygon = transform.map( mPolygon );
198 else
199 mPath = transform.map( mPath );
200
202}
203
205{
206 Q_UNUSED( context )
207}
208
210{
211 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
212 //of the rendered point!
213
214 QPainter *p = context.renderContext().painter();
215 if ( !p )
216 {
217 return;
218 }
219
220 bool hasDataDefinedSize = false;
221 const double scaledSize = calculateSize( context, hasDataDefinedSize );
222
223 bool hasDataDefinedRotation = false;
224 QPointF offset;
225 double angle = 0;
226 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
227
228 //data defined shape?
229 bool createdNewPath = false;
230 bool ok = true;
231 Qgis::MarkerShape symbol = mShape;
233 {
234 context.setOriginalValueVariable( encodeShape( symbol ) );
236 if ( !QgsVariantUtils::isNull( exprVal ) )
237 {
238 const Qgis::MarkerShape decoded = decodeShape( exprVal.toString(), &ok );
239 if ( ok )
240 {
241 symbol = decoded;
242
243 if ( !prepareMarkerShape( symbol ) ) // drawing as a polygon
244 {
245 prepareMarkerPath( symbol ); // drawing as a painter path
246 }
247 createdNewPath = true;
248 }
249 }
250 else
251 {
252 symbol = mShape;
253 }
254 }
255
256 QTransform transform;
257
258 // move to the desired position
259 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
260
261 // resize if necessary
262 if ( hasDataDefinedSize || createdNewPath )
263 {
264 double s = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
266 {
267 // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
268 // and clamp it to a reasonable range. It's the best we can do in this situation!
269 s = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
270 }
271 const double half = s / 2.0;
272 transform.scale( half, half );
273 }
274
275 if ( !qgsDoubleNear( angle, 0.0 ) && ( hasDataDefinedRotation || createdNewPath ) )
276 {
277 transform.rotate( angle );
278 }
279
280 //need to pass: symbol, polygon, path
281
282 QPolygonF polygon;
283 QPainterPath path;
284 if ( !mPolygon.isEmpty() )
285 {
286 polygon = transform.map( mPolygon );
287 }
288 else
289 {
290 path = transform.map( mPath );
291 }
292 draw( context, symbol, polygon, path );
293}
294
296{
297 bool hasDataDefinedSize = false;
298 double scaledSize = calculateSize( context, hasDataDefinedSize );
299
300 bool hasDataDefinedRotation = false;
301 QPointF offset;
302 double angle = 0;
303 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
304
305 scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
306
307 QTransform transform;
308
309 // move to the desired position
310 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
311
312 if ( !qgsDoubleNear( angle, 0.0 ) )
313 transform.rotate( angle );
314
315 return transform.mapRect( QRectF( -scaledSize / 2.0,
316 -scaledSize / 2.0,
317 scaledSize,
318 scaledSize ) );
319}
320
322{
323 if ( ok )
324 *ok = true;
325 const QString cleaned = name.toLower().trimmed();
326
327 if ( cleaned == QLatin1String( "square" ) || cleaned == QLatin1String( "rectangle" ) )
329 else if ( cleaned == QLatin1String( "trapezoid" ) )
331 else if ( cleaned == QLatin1String( "parallelogram_right" ) )
333 else if ( cleaned == QLatin1String( "parallelogram_left" ) )
335 else if ( cleaned == QLatin1String( "square_with_corners" ) )
337 else if ( cleaned == QLatin1String( "rounded_square" ) )
339 else if ( cleaned == QLatin1String( "diamond" ) )
341 else if ( cleaned == QLatin1String( "shield" ) )
343 else if ( cleaned == QLatin1String( "pentagon" ) )
345 else if ( cleaned == QLatin1String( "hexagon" ) )
347 else if ( cleaned == QLatin1String( "octagon" ) )
349 else if ( cleaned == QLatin1String( "decagon" ) )
351 else if ( cleaned == QLatin1String( "triangle" ) )
353 else if ( cleaned == QLatin1String( "equilateral_triangle" ) )
355 else if ( cleaned == QLatin1String( "star_diamond" ) )
357 else if ( cleaned == QLatin1String( "star" ) || cleaned == QLatin1String( "regular_star" ) )
359 else if ( cleaned == QLatin1String( "heart" ) )
361 else if ( cleaned == QLatin1String( "arrow" ) )
363 else if ( cleaned == QLatin1String( "circle" ) )
365 else if ( cleaned == QLatin1String( "cross" ) )
367 else if ( cleaned == QLatin1String( "cross_fill" ) )
369 else if ( cleaned == QLatin1String( "cross2" ) || cleaned == QLatin1String( "x" ) )
371 else if ( cleaned == QLatin1String( "line" ) )
373 else if ( cleaned == QLatin1String( "arrowhead" ) )
375 else if ( cleaned == QLatin1String( "filled_arrowhead" ) )
377 else if ( cleaned == QLatin1String( "semi_circle" ) )
379 else if ( cleaned == QLatin1String( "third_circle" ) )
381 else if ( cleaned == QLatin1String( "quarter_circle" ) )
383 else if ( cleaned == QLatin1String( "quarter_square" ) )
385 else if ( cleaned == QLatin1String( "half_square" ) )
387 else if ( cleaned == QLatin1String( "diagonal_half_square" ) )
389 else if ( cleaned == QLatin1String( "right_half_triangle" ) )
391 else if ( cleaned == QLatin1String( "left_half_triangle" ) )
393 else if ( cleaned == QLatin1String( "asterisk_fill" ) )
395 else if ( cleaned == QLatin1String( "half_arc" ) )
397 else if ( cleaned == QLatin1String( "third_arc" ) )
399 else if ( cleaned == QLatin1String( "quarter_arc" ) )
401
402 if ( ok )
403 *ok = false;
405}
406
408{
409 switch ( shape )
410 {
412 return QStringLiteral( "square" );
414 return QStringLiteral( "quarter_square" );
416 return QStringLiteral( "half_square" );
418 return QStringLiteral( "diagonal_half_square" );
420 return QStringLiteral( "parallelogram_right" );
422 return QStringLiteral( "parallelogram_left" );
424 return QStringLiteral( "trapezoid" );
426 return QStringLiteral( "shield" );
428 return QStringLiteral( "diamond" );
430 return QStringLiteral( "pentagon" );
432 return QStringLiteral( "hexagon" );
434 return QStringLiteral( "octagon" );
436 return QStringLiteral( "decagon" );
438 return QStringLiteral( "square_with_corners" );
440 return QStringLiteral( "rounded_square" );
442 return QStringLiteral( "triangle" );
444 return QStringLiteral( "equilateral_triangle" );
446 return QStringLiteral( "left_half_triangle" );
448 return QStringLiteral( "right_half_triangle" );
450 return QStringLiteral( "star_diamond" );
452 return QStringLiteral( "star" );
454 return QStringLiteral( "heart" );
456 return QStringLiteral( "arrow" );
458 return QStringLiteral( "filled_arrowhead" );
460 return QStringLiteral( "cross_fill" );
462 return QStringLiteral( "circle" );
464 return QStringLiteral( "cross" );
466 return QStringLiteral( "cross2" );
468 return QStringLiteral( "line" );
470 return QStringLiteral( "arrowhead" );
472 return QStringLiteral( "semi_circle" );
474 return QStringLiteral( "third_circle" );
476 return QStringLiteral( "quarter_circle" );
478 return QStringLiteral( "asterisk_fill" );
480 return QStringLiteral( "half_arc" );
482 return QStringLiteral( "third_arc" );
484 return QStringLiteral( "quarter_arc" );
485 }
486 return QString();
487}
488
493
495{
496 polygon.clear();
497
498 switch ( shape )
499 {
501 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 1, 1 ) ) );
502 return true;
503
505 {
506 static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 0.6072;
507
508 polygon << QPointF( - VERTEX_OFFSET_FROM_ORIGIN, 1 )
509 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, 1 )
510 << QPointF( 1, VERTEX_OFFSET_FROM_ORIGIN )
511 << QPointF( 1, -VERTEX_OFFSET_FROM_ORIGIN )
512 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, -1 )
513 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, -1 )
514 << QPointF( -1, -VERTEX_OFFSET_FROM_ORIGIN )
515 << QPointF( -1, VERTEX_OFFSET_FROM_ORIGIN )
516 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 );
517 return true;
518 }
519
521 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 0 ) ) );
522 return true;
523
525 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 1 ) ) );
526 return true;
527
529 polygon << QPointF( -1, -1 ) << QPointF( 1, 1 ) << QPointF( -1, 1 ) << QPointF( -1, -1 );
530 return true;
531
533 polygon << QPointF( 0.5, -0.5 )
534 << QPointF( 1, 0.5 )
535 << QPointF( -1, 0.5 )
536 << QPointF( -0.5, -0.5 )
537 << QPointF( 0.5, -0.5 );
538 return true;
539
541 polygon << QPointF( 0.5, 0.5 )
542 << QPointF( 1, -0.5 )
543 << QPointF( -0.5, -0.5 )
544 << QPointF( -1, 0.5 )
545 << QPointF( 0.5, 0.5 );
546 return true;
547
549 polygon << QPointF( 1, 0.5 )
550 << QPointF( 0.5, -0.5 )
551 << QPointF( -1, -0.5 )
552 << QPointF( -0.5, 0.5 )
553 << QPointF( 1, 0.5 );
554 return true;
555
557 polygon << QPointF( -1, 0 ) << QPointF( 0, 1 )
558 << QPointF( 1, 0 ) << QPointF( 0, -1 ) << QPointF( -1, 0 );
559 return true;
560
562 polygon << QPointF( 1, 0.5 )
563 << QPointF( 1, -1 )
564 << QPointF( -1, -1 )
565 << QPointF( -1, 0.5 )
566 << QPointF( 0, 1 )
567 << QPointF( 1, 0.5 );
568 return true;
569
571 /* angular-representation of hardcoded values used
572 polygon << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288.0 ) ) )
573 << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) )
574 << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) )
575 << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) )
576 << QPointF( 0, -1 ); */
577 polygon << QPointF( -0.9511, -0.3090 )
578 << QPointF( -0.5878, 0.8090 )
579 << QPointF( 0.5878, 0.8090 )
580 << QPointF( 0.9511, -0.3090 )
581 << QPointF( 0, -1 )
582 << QPointF( -0.9511, -0.3090 );
583 return true;
584
586 /* angular-representation of hardcoded values used
587 polygon << QPointF( std::sin( DEG2RAD( 300.0 ) ), - std::cos( DEG2RAD( 300.0 ) ) )
588 << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
589 << QPointF( std::sin( DEG2RAD( 180.0 ) ), - std::cos( DEG2RAD( 180.0 ) ) )
590 << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
591 << QPointF( std::sin( DEG2RAD( 60.0 ) ), - std::cos( DEG2RAD( 60.0 ) ) )
592 << QPointF( 0, -1 ); */
593 polygon << QPointF( -0.8660, -0.5 )
594 << QPointF( -0.8660, 0.5 )
595 << QPointF( 0, 1 )
596 << QPointF( 0.8660, 0.5 )
597 << QPointF( 0.8660, -0.5 )
598 << QPointF( 0, -1 )
599 << QPointF( -0.8660, -0.5 );
600 return true;
601
603 {
604 static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 1.0 / ( 1 + M_SQRT2 );
605
606 polygon << QPointF( - VERTEX_OFFSET_FROM_ORIGIN, 1 )
607 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, 1 )
608 << QPointF( 1, VERTEX_OFFSET_FROM_ORIGIN )
609 << QPointF( 1, -VERTEX_OFFSET_FROM_ORIGIN )
610 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, -1 )
611 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, -1 )
612 << QPointF( -1, -VERTEX_OFFSET_FROM_ORIGIN )
613 << QPointF( -1, VERTEX_OFFSET_FROM_ORIGIN )
614 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 );
615 return true;
616 }
617
619 {
620
621 polygon << QPointF( 0.587785252, 0.809016994 )
622 << QPointF( 0.951056516, 0.309016994 )
623 << QPointF( 0.951056516, -0.309016994 )
624 << QPointF( 0.587785252, -0.809016994 )
625 << QPointF( 0, -1 )
626 << QPointF( -0.587785252, -0.809016994 )
627 << QPointF( -0.951056516, -0.309016994 )
628 << QPointF( -0.951056516, 0.309016994 )
629 << QPointF( -0.587785252, 0.809016994 )
630 << QPointF( 0, 1 )
631 << QPointF( 0.587785252, 0.809016994 );
632 return true;
633 }
634
636 polygon << QPointF( -1, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
637 return true;
638
640 /* angular-representation of hardcoded values used
641 polygon << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
642 << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
643 << QPointF( 0, -1 ); */
644 polygon << QPointF( -0.8660, 0.5 )
645 << QPointF( 0.8660, 0.5 )
646 << QPointF( 0, -1 )
647 << QPointF( -0.8660, 0.5 );
648 return true;
649
651 polygon << QPointF( 0, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( 0, 1 );
652 return true;
653
655 polygon << QPointF( -1, 1 ) << QPointF( 0, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
656 return true;
657
659 {
660 const double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
661
662 polygon << QPointF( inner_r * std::sin( DEG2RAD( 315.0 ) ), - inner_r * std::cos( DEG2RAD( 315.0 ) ) )
663 << QPointF( std::sin( DEG2RAD( 270 ) ), - std::cos( DEG2RAD( 270 ) ) )
664 << QPointF( inner_r * std::sin( DEG2RAD( 225.0 ) ), - inner_r * std::cos( DEG2RAD( 225.0 ) ) )
665 << QPointF( std::sin( DEG2RAD( 180 ) ), - std::cos( DEG2RAD( 180 ) ) )
666 << QPointF( inner_r * std::sin( DEG2RAD( 135.0 ) ), - inner_r * std::cos( DEG2RAD( 135.0 ) ) )
667 << QPointF( std::sin( DEG2RAD( 90 ) ), - std::cos( DEG2RAD( 90 ) ) )
668 << QPointF( inner_r * std::sin( DEG2RAD( 45.0 ) ), - inner_r * std::cos( DEG2RAD( 45.0 ) ) )
669 << QPointF( std::sin( DEG2RAD( 0 ) ), - std::cos( DEG2RAD( 0 ) ) )
670 << QPointF( inner_r * std::sin( DEG2RAD( 315.0 ) ), - inner_r * std::cos( DEG2RAD( 315.0 ) ) );
671 return true;
672 }
673
675 {
676 const double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
677
678 polygon << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ) // 324
679 << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288 ) ) ) // 288
680 << QPointF( inner_r * std::sin( DEG2RAD( 252.0 ) ), - inner_r * std::cos( DEG2RAD( 252.0 ) ) ) // 252
681 << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) ) // 216
682 << QPointF( 0, inner_r ) // 180
683 << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) ) // 144
684 << QPointF( inner_r * std::sin( DEG2RAD( 108.0 ) ), - inner_r * std::cos( DEG2RAD( 108.0 ) ) ) // 108
685 << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) ) // 72
686 << QPointF( inner_r * std::sin( DEG2RAD( 36.0 ) ), - inner_r * std::cos( DEG2RAD( 36.0 ) ) ) // 36
687 << QPointF( 0, -1 )
688 << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ); // 324; // 0
689 return true;
690 }
691
693 polygon << QPointF( 0, -1 )
694 << QPointF( 0.5, -0.5 )
695 << QPointF( 0.25, -0.5 )
696 << QPointF( 0.25, 1 )
697 << QPointF( -0.25, 1 )
698 << QPointF( -0.25, -0.5 )
699 << QPointF( -0.5, -0.5 )
700 << QPointF( 0, -1 );
701 return true;
702
704 polygon << QPointF( 0, 0 ) << QPointF( -1, 1 ) << QPointF( -1, -1 ) << QPointF( 0, 0 );
705 return true;
706
708 polygon << QPointF( -1, -0.2 )
709 << QPointF( -1, -0.2 )
710 << QPointF( -1, 0.2 )
711 << QPointF( -0.2, 0.2 )
712 << QPointF( -0.2, 1 )
713 << QPointF( 0.2, 1 )
714 << QPointF( 0.2, 0.2 )
715 << QPointF( 1, 0.2 )
716 << QPointF( 1, -0.2 )
717 << QPointF( 0.2, -0.2 )
718 << QPointF( 0.2, -1 )
719 << QPointF( -0.2, -1 )
720 << QPointF( -0.2, -0.2 )
721 << QPointF( -1, -0.2 );
722 return true;
723
725 {
726 static constexpr double THICKNESS = 0.3;
727 static constexpr double HALF_THICKNESS = THICKNESS / 2.0;
728 static constexpr double INTERSECTION_POINT = THICKNESS / M_SQRT2;
729 static constexpr double DIAGONAL1 = M_SQRT1_2 - INTERSECTION_POINT * 0.5;
730 static constexpr double DIAGONAL2 = M_SQRT1_2 + INTERSECTION_POINT * 0.5;
731
732 polygon << QPointF( -HALF_THICKNESS, -1 )
733 << QPointF( HALF_THICKNESS, -1 )
734 << QPointF( HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
735 << QPointF( DIAGONAL1, -DIAGONAL2 )
736 << QPointF( DIAGONAL2, -DIAGONAL1 )
737 << QPointF( HALF_THICKNESS + INTERSECTION_POINT, -HALF_THICKNESS )
738 << QPointF( 1, -HALF_THICKNESS )
739 << QPointF( 1, HALF_THICKNESS )
740 << QPointF( HALF_THICKNESS + INTERSECTION_POINT, HALF_THICKNESS )
741 << QPointF( DIAGONAL2, DIAGONAL1 )
742 << QPointF( DIAGONAL1, DIAGONAL2 )
743 << QPointF( HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
744 << QPointF( HALF_THICKNESS, 1 )
745 << QPointF( -HALF_THICKNESS, 1 )
746 << QPointF( -HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
747 << QPointF( -DIAGONAL1, DIAGONAL2 )
748 << QPointF( -DIAGONAL2, DIAGONAL1 )
749 << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, HALF_THICKNESS )
750 << QPointF( -1, HALF_THICKNESS )
751 << QPointF( -1, -HALF_THICKNESS )
752 << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, -HALF_THICKNESS )
753 << QPointF( -DIAGONAL2, -DIAGONAL1 )
754 << QPointF( -DIAGONAL1, -DIAGONAL2 )
755 << QPointF( -HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
756 << QPointF( -HALF_THICKNESS, -1 );
757 return true;
758 }
759
773 return false;
774 }
775
776 return false;
777}
778
780{
781 mPath = QPainterPath();
782
783 switch ( symbol )
784 {
786
787 mPath.addEllipse( QRectF( -1, -1, 2, 2 ) ); // x,y,w,h
788 return true;
789
791 mPath.moveTo( -1, -1 );
792 mPath.addRoundedRect( -1, -1, 2, 2, 0.25, 0.25 );
793 return true;
794
796 mPath.arcTo( -1, -1, 2, 2, 0, 180 );
797 mPath.lineTo( 0, 0 );
798 return true;
799
801 mPath.arcTo( -1, -1, 2, 2, 90, 120 );
802 mPath.lineTo( 0, 0 );
803 return true;
804
806 mPath.arcTo( -1, -1, 2, 2, 90, 90 );
807 mPath.lineTo( 0, 0 );
808 return true;
809
811 mPath.moveTo( 1, 0 );
812 mPath.arcTo( -1, -1, 2, 2, 0, 180 );
813 return true;
814
816 mPath.moveTo( 0, -1 );
817 mPath.arcTo( -1, -1, 2, 2, 90, 120 );
818 return true;
819
821 mPath.moveTo( 0, -1 );
822 mPath.arcTo( -1, -1, 2, 2, 90, 90 );
823 return true;
824
826 mPath.moveTo( -1, 0 );
827 mPath.lineTo( 1, 0 ); // horizontal
828 mPath.moveTo( 0, -1 );
829 mPath.lineTo( 0, 1 ); // vertical
830 return true;
831
833 mPath.moveTo( -1, -1 );
834 mPath.lineTo( 1, 1 );
835 mPath.moveTo( 1, -1 );
836 mPath.lineTo( -1, 1 );
837 return true;
838
840 mPath.moveTo( 0, -1 );
841 mPath.lineTo( 0, 1 ); // vertical line
842 return true;
843
845 mPath.moveTo( -1, -1 );
846 mPath.lineTo( 0, 0 );
847 mPath.lineTo( -1, 1 );
848 return true;
849
851 mPath.moveTo( 0, 0.75 );
852 mPath.arcTo( 0, -1, 1, 1, -45, 210 );
853 mPath.arcTo( -1, -1, 1, 1, 15, 210 );
854 mPath.lineTo( 0, 0.75 );
855 return true;
856
881 return false;
882 }
883 return false;
884}
885
886double QgsSimpleMarkerSymbolLayerBase::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
887{
888 double scaledSize = mSize;
889
891 bool ok = true;
892 if ( hasDataDefinedSize )
893 {
896 mSize, &ok );
897 }
898
899 if ( hasDataDefinedSize && ok )
900 {
901 switch ( mScaleMethod )
902 {
904 scaledSize = std::sqrt( scaledSize );
905 break;
907 break;
908 }
909 }
910
911 return scaledSize;
912}
913
914void QgsSimpleMarkerSymbolLayerBase::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle ) const
915{
916 //offset
917 double offsetX = 0;
918 double offsetY = 0;
919 markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
920 offset = QPointF( offsetX, offsetY );
921
922 hasDataDefinedRotation = false;
923 //angle
924 bool ok = true;
927 {
930
931 // If the expression evaluation was not successful, fallback to static value
932 if ( !ok )
934
935 hasDataDefinedRotation = true;
936 }
937
938 hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || hasDataDefinedRotation;
939
940 if ( hasDataDefinedRotation )
941 {
942 // For non-point markers, "dataDefinedRotation" means following the
943 // shape (shape-data defined). For them, "field-data defined" does
944 // not work at all. TODO: if "field-data defined" ever gets implemented
945 // we'll need a way to distinguish here between the two, possibly
946 // using another flag in renderHints()
947 const QgsFeature *f = context.feature();
948 if ( f )
949 {
950 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
951 {
952 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
953 angle += m2p.mapRotation();
954 }
955 }
956 }
957
958 if ( angle )
960}
961
962
963//
964// QgsSimpleMarkerSymbolLayer
965//
966
967QgsSimpleMarkerSymbolLayer::QgsSimpleMarkerSymbolLayer( Qgis::MarkerShape shape, double size, double angle, Qgis::ScaleMethod scaleMethod, const QColor &color, const QColor &strokeColor, Qt::PenJoinStyle penJoinStyle )
968 : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
969 , mStrokeColor( strokeColor )
970 , mPenJoinStyle( penJoinStyle )
971{
972 mColor = color;
973}
974
976
978{
986
987 if ( props.contains( QStringLiteral( "name" ) ) )
988 {
989 shape = decodeShape( props[QStringLiteral( "name" )].toString() );
990 }
991 if ( props.contains( QStringLiteral( "color" ) ) )
992 color = QgsColorUtils::colorFromString( props[QStringLiteral( "color" )].toString() );
993 if ( props.contains( QStringLiteral( "color_border" ) ) )
994 {
995 //pre 2.5 projects use "color_border"
996 strokeColor = QgsColorUtils::colorFromString( props[QStringLiteral( "color_border" )].toString() );
997 }
998 else if ( props.contains( QStringLiteral( "outline_color" ) ) )
999 {
1000 strokeColor = QgsColorUtils::colorFromString( props[QStringLiteral( "outline_color" )].toString() );
1001 }
1002 else if ( props.contains( QStringLiteral( "line_color" ) ) )
1003 {
1004 strokeColor = QgsColorUtils::colorFromString( props[QStringLiteral( "line_color" )].toString() );
1005 }
1006 if ( props.contains( QStringLiteral( "joinstyle" ) ) )
1007 {
1008 penJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() );
1009 }
1010 if ( props.contains( QStringLiteral( "size" ) ) )
1011 size = props[QStringLiteral( "size" )].toDouble();
1012 if ( props.contains( QStringLiteral( "angle" ) ) )
1013 angle = props[QStringLiteral( "angle" )].toDouble();
1014 if ( props.contains( QStringLiteral( "scale_method" ) ) )
1015 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1016
1018 if ( props.contains( QStringLiteral( "offset" ) ) )
1019 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1020 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1021 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1022 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1023 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1024 if ( props.contains( QStringLiteral( "size_unit" ) ) )
1025 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1026 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1027 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1028
1029 if ( props.contains( QStringLiteral( "outline_style" ) ) )
1030 {
1031 m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "outline_style" )].toString() ) );
1032 }
1033 else if ( props.contains( QStringLiteral( "line_style" ) ) )
1034 {
1035 m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "line_style" )].toString() ) );
1036 }
1037 if ( props.contains( QStringLiteral( "outline_width" ) ) )
1038 {
1039 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
1040 }
1041 else if ( props.contains( QStringLiteral( "line_width" ) ) )
1042 {
1043 m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
1044 }
1045 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
1046 {
1047 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
1048 }
1049 if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
1050 {
1051 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
1052 }
1053 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
1054 {
1055 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
1056 }
1057
1058 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1059 {
1060 m->setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1061 }
1062 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1063 {
1064 m->setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1065 }
1066
1067 if ( props.contains( QStringLiteral( "cap_style" ) ) )
1068 {
1069 m->setPenCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( props[QStringLiteral( "cap_style" )].toString() ) );
1070 }
1071
1073
1074 return m;
1075}
1076
1077
1079{
1080 return QStringLiteral( "SimpleMarker" );
1081}
1082
1087
1089{
1091
1092 QColor brushColor = mColor;
1093 QColor penColor = mStrokeColor;
1094
1095 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
1096 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
1097
1098 mBrush = QBrush( brushColor );
1099 mPen = QPen( penColor );
1100 mPen.setStyle( mStrokeStyle );
1101 mPen.setCapStyle( mPenCapStyle );
1102 mPen.setJoinStyle( mPenJoinStyle );
1104
1105 QColor selBrushColor = context.renderContext().selectionColor();
1106 QColor selPenColor = selBrushColor == mColor ? selBrushColor : mStrokeColor;
1107 if ( context.opacity() < 1 && !SELECTION_IS_OPAQUE )
1108 {
1109 selBrushColor.setAlphaF( context.opacity() );
1110 selPenColor.setAlphaF( context.opacity() );
1111 }
1112 mSelBrush = QBrush( selBrushColor );
1113 mSelPen = QPen( selPenColor );
1114 mSelPen.setStyle( mStrokeStyle );
1116
1118 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
1119
1120 // use caching only when:
1121 // - size, rotation, shape, color, stroke color is not data-defined
1122 // - drawing to screen (not printer)
1123 mUsingCache = !hasDataDefinedRotation && !hasDataDefinedSize && !context.forceVectorRendering()
1127
1128 if ( mUsingCache )
1129 mCachedOpacity = context.opacity();
1130
1131 if ( !shapeIsFilled( mShape ) )
1132 {
1133 // some markers can't be drawn as a polygon (circle, cross)
1134 // For these set the selected stroke color to the selected color
1135 mSelPen.setColor( selBrushColor );
1136 }
1137
1138
1139 if ( mUsingCache )
1140 {
1141 if ( !prepareCache( context ) )
1142 {
1143 mUsingCache = false;
1144 }
1145 }
1146 else
1147 {
1148 mCache = QImage();
1149 mSelCache = QImage();
1150 }
1151}
1152
1153
1155{
1156 double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
1157 const double deviceRatio = context.renderContext().devicePixelRatio();
1159 {
1160 // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
1161 // and clamp it to a reasonable range. It's the best we can do in this situation!
1162 scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
1163 }
1164
1165 // take into account angle (which is not data-defined otherwise cache wouldn't be used)
1166 if ( !qgsDoubleNear( mAngle, 0.0 ) )
1167 {
1168 scaledSize = ( std::abs( std::sin( mAngle * M_PI / 180 ) ) + std::abs( std::cos( mAngle * M_PI / 180 ) ) ) * scaledSize;
1169 }
1170 // calculate necessary image size for the cache
1171 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
1172 const int imageSize = ( static_cast< int >( scaledSize ) + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width
1173 const double center = imageSize / 2.0;
1174 if ( imageSize * deviceRatio > MAXIMUM_CACHE_WIDTH )
1175 {
1176 return false;
1177 }
1178
1179 mCache = QImage( QSize( imageSize * deviceRatio,
1180 imageSize * deviceRatio ), QImage::Format_ARGB32_Premultiplied );
1181 mCache.setDevicePixelRatio( context.renderContext().devicePixelRatio() );
1182 mCache.setDotsPerMeterX( std::round( context.renderContext().scaleFactor() * 1000 ) );
1183 mCache.setDotsPerMeterY( std::round( context.renderContext().scaleFactor() * 1000 ) );
1184 mCache.fill( 0 );
1185
1186 const bool needsBrush = shapeIsFilled( mShape );
1187
1188 QPainter p;
1189 p.begin( &mCache );
1190 p.setRenderHint( QPainter::Antialiasing );
1191 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1192 p.setPen( mPen );
1193 p.translate( QPointF( center, center ) );
1194 drawMarker( &p, context );
1195 p.end();
1196
1197 // Construct the selected version of the Cache
1198
1199 const QColor selColor = context.renderContext().selectionColor();
1200
1201 mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
1202 mSelCache.fill( 0 );
1203
1204 p.begin( &mSelCache );
1205 p.setRenderHint( QPainter::Antialiasing );
1206 p.setBrush( needsBrush ? mSelBrush : Qt::NoBrush );
1207 p.setPen( mSelPen );
1208 p.translate( QPointF( center, center ) );
1209 drawMarker( &p, context );
1210 p.end();
1211
1212 // Check that the selected version is different. If not, then re-render,
1213 // filling the background with the selection color and using the normal
1214 // colors for the symbol .. could be ugly!
1215
1216 if ( mSelCache == mCache )
1217 {
1218 p.begin( &mSelCache );
1219 p.setRenderHint( QPainter::Antialiasing );
1220 p.fillRect( 0, 0, imageSize, imageSize, selColor );
1221 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1222 p.setPen( mPen );
1223 p.translate( QPointF( center, center ) );
1224 drawMarker( &p, context );
1225 p.end();
1226 }
1227
1228 return true;
1229}
1230
1231void QgsSimpleMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
1232{
1233 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1234 //of the rendered point!
1235
1236 QPainter *p = context.renderContext().painter();
1237 if ( !p )
1238 {
1239 return;
1240 }
1241
1242 QColor brushColor = mColor;
1243 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
1244 mBrush.setColor( brushColor );
1245
1246 QColor penColor = mStrokeColor;
1247 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
1248 mPen.setColor( penColor );
1249
1250 bool ok = true;
1252 {
1255 if ( ok )
1256 {
1257 c.setAlphaF( c.alphaF() * context.opacity() );
1258 mBrush.setColor( c );
1259 }
1260 }
1262 {
1265 if ( ok )
1266 {
1267 c.setAlphaF( c.alphaF() * context.opacity() );
1268 mPen.setColor( c );
1269 mSelPen.setColor( c );
1270 }
1271 }
1273 {
1276 if ( ok )
1277 {
1280 }
1281 }
1283 {
1286 if ( ok )
1287 {
1290 }
1291 }
1293 {
1296 if ( ok )
1297 {
1298 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1299 mSelPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1300 }
1301 }
1303 {
1306 if ( ok )
1307 {
1308 mPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1309 mSelPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1310 }
1311 }
1312
1313 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
1314 if ( shapeIsFilled( shape ) )
1315 {
1316 p->setBrush( useSelectedColor ? mSelBrush : mBrush );
1317 }
1318 else
1319 {
1320 p->setBrush( Qt::NoBrush );
1321 }
1322 p->setPen( useSelectedColor ? mSelPen : mPen );
1323
1324 if ( !polygon.isEmpty() )
1325 p->drawPolygon( polygon );
1326 else
1327 p->drawPath( path );
1328}
1329
1331{
1332 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1333 //of the rendered point!
1334
1335 QPainter *p = context.renderContext().painter();
1336 if ( !p )
1337 {
1338 return;
1339 }
1340
1341 if ( mUsingCache && qgsDoubleNear( mCachedOpacity, context.opacity() ) )
1342 {
1343 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
1344 const QImage &img = useSelectedColor ? mSelCache : mCache;
1345 const double s = img.width() / img.devicePixelRatioF();
1346
1347 bool hasDataDefinedSize = false;
1348 const double scaledSize = calculateSize( context, hasDataDefinedSize );
1349
1350 bool hasDataDefinedRotation = false;
1351 QPointF offset;
1352 double angle = 0;
1353 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
1354
1355 p->drawImage( QRectF( point.x() - s / 2.0 + offset.x(),
1356 point.y() - s / 2.0 + offset.y(),
1357 s, s ), img );
1358 }
1359 else
1360 {
1362 }
1363}
1364
1366{
1367 QVariantMap map;
1368 map[QStringLiteral( "name" )] = encodeShape( mShape );
1369 map[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mColor );
1370 map[QStringLiteral( "outline_color" )] = QgsColorUtils::colorToString( mStrokeColor );
1371 map[QStringLiteral( "size" )] = QString::number( mSize );
1372 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1373 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1374 map[QStringLiteral( "angle" )] = QString::number( mAngle );
1375 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1376 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1377 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1378 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1379 map[QStringLiteral( "outline_style" )] = QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle );
1380 map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
1381 map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
1382 map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
1383 map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
1384 map[QStringLiteral( "cap_style" )] = QgsSymbolLayerUtils::encodePenCapStyle( mPenCapStyle );
1385 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
1386 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
1387 return map;
1388}
1389
1409
1410void QgsSimpleMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1411{
1412 QgsSldExportContext context;
1413 context.setExtraProperties( props );
1414 toSld( doc, element, context );
1415}
1416
1417bool QgsSimpleMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
1418{
1419 return QgsSimpleMarkerSymbolLayerBase::toSld( doc, element, context );
1420}
1421
1422void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1423{
1424 QgsSldExportContext context;
1425 context.setExtraProperties( props );
1426 writeSldMarker( doc, element, context );
1427}
1428
1429bool QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
1430{
1431 // <Graphic>
1432 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
1433 element.appendChild( graphicElem );
1434
1435 const QVariantMap props = context.extraProperties();
1437 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
1439
1440 // <Rotation>
1441 QString angleFunc;
1442
1444 {
1446 }
1447 else
1448 {
1449 bool ok;
1450 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
1451 if ( !ok )
1452 {
1453 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
1454 }
1455 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
1456 {
1457 angleFunc = QString::number( angle + mAngle );
1458 }
1459 }
1460
1461 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc, context );
1462
1463 // <Displacement>
1464 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
1466 return true;
1467}
1468
1469QString QgsSimpleMarkerSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
1470{
1471 Q_UNUSED( mmScaleFactor )
1472 Q_UNUSED( mapUnitScaleFactor )
1473#if 0
1474 QString ogrType = "3"; //default is circle
1475 if ( mName == "square" )
1476 {
1477 ogrType = "5";
1478 }
1479 else if ( mName == "triangle" )
1480 {
1481 ogrType = "7";
1482 }
1483 else if ( mName == "star" )
1484 {
1485 ogrType = "9";
1486 }
1487 else if ( mName == "circle" )
1488 {
1489 ogrType = "3";
1490 }
1491 else if ( mName == "cross" )
1492 {
1493 ogrType = "0";
1494 }
1495 else if ( mName == "x" || mName == "cross2" )
1496 {
1497 ogrType = "1";
1498 }
1499 else if ( mName == "line" )
1500 {
1501 ogrType = "10";
1502 }
1503
1504 QString ogrString;
1505 ogrString.append( "SYMBOL(" );
1506 ogrString.append( "id:" );
1507 ogrString.append( '\"' );
1508 ogrString.append( "ogr-sym-" );
1509 ogrString.append( ogrType );
1510 ogrString.append( '\"' );
1511 ogrString.append( ",c:" );
1512 ogrString.append( mColor.name() );
1513 ogrString.append( ",o:" );
1514 ogrString.append( mStrokeColor.name() );
1515 ogrString.append( QString( ",s:%1mm" ).arg( mSize ) );
1516 ogrString.append( ')' );
1517 return ogrString;
1518#endif //0
1519
1520 QString ogrString;
1521 ogrString.append( "PEN(" );
1522 ogrString.append( "c:" );
1523 ogrString.append( mColor.name() );
1524 ogrString.append( ",w:" );
1525 ogrString.append( QString::number( mSize ) );
1526 ogrString.append( "mm" );
1527 ogrString.append( ")" );
1528 return ogrString;
1529}
1530
1532{
1533 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
1534
1535 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
1536 if ( graphicElem.isNull() )
1537 return nullptr;
1538
1539 QString name = QStringLiteral( "square" );
1540 QColor color, strokeColor;
1541 double strokeWidth, size;
1542 Qt::PenStyle strokeStyle;
1543
1545 return nullptr;
1546
1547 double angle = 0.0;
1548 QString angleFunc;
1549 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
1550 {
1551 bool ok;
1552 const double d = angleFunc.toDouble( &ok );
1553 if ( ok )
1554 angle = d;
1555 }
1556
1557 QPointF offset;
1559
1560 const Qgis::MarkerShape shape = decodeShape( name );
1561
1562 double scaleFactor = 1.0;
1563 const QString uom = element.attribute( QStringLiteral( "uom" ) );
1564 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
1565 size = size * scaleFactor;
1566 offset.setX( offset.x() * scaleFactor );
1567 offset.setY( offset.y() * scaleFactor );
1568
1570 m->setOutputUnit( sldUnitSize );
1571 m->setColor( color );
1573 m->setAngle( angle );
1574 m->setOffset( offset );
1577 return m;
1578}
1579
1581{
1582 Q_UNUSED( context )
1583
1584 if ( mPolygon.count() != 0 )
1585 {
1586 p->drawPolygon( mPolygon );
1587 }
1588 else
1589 {
1590 p->drawPath( mPath );
1591 }
1592}
1593
1594bool QgsSimpleMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
1595{
1596 //data defined size?
1597 double size = mSize;
1598
1599 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
1600
1601 //data defined size
1602 bool ok = true;
1603 if ( hasDataDefinedSize )
1604 {
1606
1607 if ( ok )
1608 {
1609 switch ( mScaleMethod )
1610 {
1612 size = std::sqrt( size );
1613 break;
1615 break;
1616 }
1617 }
1618 }
1619
1621 {
1622 size *= mmMapUnitScaleFactor;
1623 }
1624
1626 {
1628 }
1629 const double halfSize = size / 2.0;
1630
1631 //strokeWidth
1632 double strokeWidth = mStrokeWidth;
1633
1635 {
1638 }
1641 {
1643 }
1644
1645 //color
1646 QColor pc = mPen.color();
1647 QColor bc = mBrush.color();
1649 {
1652 }
1654 {
1657 }
1658
1659 //offset
1660 double offsetX = 0;
1661 double offsetY = 0;
1662 markerOffset( context, offsetX, offsetY );
1663 offsetX *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1664 offsetY *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1665
1666
1667 QPointF off( offsetX, offsetY );
1668
1669 //angle
1670 double angle = mAngle + mLineAngle;
1672 {
1675 }
1676
1679 {
1681 const QString shapeName = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::Name, context.renderContext().expressionContext(), QString(), &ok );
1682 if ( ok )
1683 {
1684 shape = decodeShape( shapeName, &ok );
1685 if ( !ok )
1686 shape = mShape;
1687 }
1688 }
1689
1690 if ( angle )
1691 off = _rotatedOffset( off, angle );
1692
1694
1695 QTransform t;
1696 t.translate( shift.x() + off.x(), shift.y() - off.y() );
1697
1698 if ( !qgsDoubleNear( angle, 0.0 ) )
1699 t.rotate( -angle );
1700
1701 QPolygonF polygon;
1702 if ( shapeToPolygon( shape, polygon ) )
1703 {
1704 t.scale( halfSize, -halfSize );
1705
1706 polygon = t.map( polygon );
1707
1709 p.reserve( polygon.size() );
1710 for ( int i = 0; i < polygon.size(); i++ )
1711 {
1712 p << QgsPoint( polygon[i] );
1713 }
1714
1715 if ( mBrush.style() != Qt::NoBrush )
1716 e.writePolygon( QgsRingSequence() << p, layerName, QStringLiteral( "SOLID" ), bc );
1717 if ( mPen.style() != Qt::NoPen )
1718 e.writePolyline( p, layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1719 }
1720 else if ( shape == Qgis::MarkerShape::Circle )
1721 {
1722 shift += QPointF( off.x(), -off.y() );
1723 if ( mBrush.style() != Qt::NoBrush )
1724 e.writeFilledCircle( layerName, bc, QgsPoint( shift ), halfSize );
1725 if ( mPen.style() != Qt::NoPen )
1726 e.writeCircle( layerName, pc, QgsPoint( shift ), halfSize, QStringLiteral( "CONTINUOUS" ), strokeWidth );
1727 }
1728 else if ( shape == Qgis::MarkerShape::Line )
1729 {
1730 const QPointF pt1 = t.map( QPointF( 0, -halfSize ) );
1731 const QPointF pt2 = t.map( QPointF( 0, halfSize ) );
1732
1733 if ( mPen.style() != Qt::NoPen )
1734 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1735 }
1736 else if ( shape == Qgis::MarkerShape::Cross )
1737 {
1738 if ( mPen.style() != Qt::NoPen )
1739 {
1740 const QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
1741 const QPointF pt2 = t.map( QPointF( halfSize, 0 ) );
1742 const QPointF pt3 = t.map( QPointF( 0, -halfSize ) );
1743 const QPointF pt4 = t.map( QPointF( 0, halfSize ) );
1744
1745 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1746 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1747 }
1748 }
1749 else if ( shape == Qgis::MarkerShape::Cross2 )
1750 {
1751 if ( mPen.style() != Qt::NoPen )
1752 {
1753 const QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
1754 const QPointF pt2 = t.map( QPointF( halfSize, halfSize ) );
1755 const QPointF pt3 = t.map( QPointF( halfSize, -halfSize ) );
1756 const QPointF pt4 = t.map( QPointF( -halfSize, halfSize ) );
1757
1758 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1759 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1760 }
1761 }
1762 else if ( shape == Qgis::MarkerShape::ArrowHead )
1763 {
1764 if ( mPen.style() != Qt::NoPen )
1765 {
1766 const QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
1767 const QPointF pt2 = t.map( QPointF( 0, 0 ) );
1768 const QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
1769
1770 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1771 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1772 }
1773 }
1774 else
1775 {
1776 QgsDebugError( QStringLiteral( "Unsupported dxf marker name %1" ).arg( encodeShape( shape ) ) );
1777 return false;
1778 }
1779
1780 return true;
1781}
1782
1783
1789
1798
1804
1813
1820
1822{
1823 QRectF symbolBounds = QgsSimpleMarkerSymbolLayerBase::bounds( point, context );
1824
1825 // need to account for stroke width
1826 double penWidth = mStrokeWidth;
1827 bool ok = true;
1829 {
1832 if ( ok )
1833 {
1834 penWidth = strokeWidth;
1835 }
1836 }
1839 {
1842 if ( ok && strokeStyle == QLatin1String( "no" ) )
1843 {
1844 penWidth = 0.0;
1845 }
1846 }
1847 else if ( mStrokeStyle == Qt::NoPen )
1848 penWidth = 0;
1849
1850 //antialiasing, add 1 pixel
1851 penWidth += 1;
1852
1853 //extend bounds by pen width / 2.0
1854 symbolBounds.adjust( -penWidth / 2.0, -penWidth / 2.0,
1855 penWidth / 2.0, penWidth / 2.0 );
1856
1857 return symbolBounds;
1858}
1859
1860void QgsSimpleMarkerSymbolLayer::setColor( const QColor &color )
1861{
1862 if ( shapeIsFilled( mShape ) )
1863 {
1865 }
1866 else
1867 {
1869 }
1870}
1871
1873{
1874 if ( shapeIsFilled( mShape ) )
1875 {
1876 return fillColor();
1877 }
1878 else
1879 {
1880 return strokeColor();
1881 }
1882}
1883
1884
1885
1886
1887//
1888// QgsFilledMarkerSymbolLayer
1889//
1890
1892 : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
1893{
1894 mFill = QgsFillSymbol::createSimple( QVariantMap() );
1895}
1896
1898
1900{
1901 QString name = DEFAULT_SIMPLEMARKER_NAME;
1905
1906 if ( props.contains( QStringLiteral( "name" ) ) )
1907 name = props[QStringLiteral( "name" )].toString();
1908 if ( props.contains( QStringLiteral( "size" ) ) )
1909 size = props[QStringLiteral( "size" )].toDouble();
1910 if ( props.contains( QStringLiteral( "angle" ) ) )
1911 angle = props[QStringLiteral( "angle" )].toDouble();
1912 if ( props.contains( QStringLiteral( "scale_method" ) ) )
1913 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1914
1916 if ( props.contains( QStringLiteral( "offset" ) ) )
1917 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1918 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1919 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1920 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1921 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1922 if ( props.contains( QStringLiteral( "size_unit" ) ) )
1923 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1924 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1925 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1926 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1927 {
1928 m->setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1929 }
1930 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1931 {
1932 m->setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1933 }
1934
1935 m->setSubSymbol( QgsFillSymbol::createSimple( props ).release() );
1936
1938
1939 return m;
1940}
1941
1943{
1944 return QStringLiteral( "FilledMarker" );
1945}
1946
1948{
1949 if ( mFill )
1950 {
1951 mFill->setRenderHints( mFill->renderHints() | Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol );
1952 mFill->startRender( context.renderContext(), context.fields() );
1953 }
1954
1956}
1957
1959{
1960 if ( mFill )
1961 {
1962 mFill->stopRender( context.renderContext() );
1963 }
1964}
1965
1967{
1968 installMasks( context, true );
1969
1970 // The base class version passes this on to the subsymbol, but we deliberately don't do that here.
1971}
1972
1974{
1975 removeMasks( context, true );
1976
1977 // The base class version passes this on to the subsymbol, but we deliberately don't do that here.
1978}
1979
1981{
1982 QVariantMap map;
1983 map[QStringLiteral( "name" )] = encodeShape( mShape );
1984 map[QStringLiteral( "size" )] = QString::number( mSize );
1985 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1986 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1987 map[QStringLiteral( "angle" )] = QString::number( mAngle );
1988 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1989 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1990 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1991 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1992 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
1993 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
1994
1995 if ( mFill )
1996 {
1997 map[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mFill->color() );
1998 }
1999 return map;
2000}
2001
2010
2012{
2013 return mFill.get();
2014}
2015
2017{
2018 if ( symbol && symbol->type() == Qgis::SymbolType::Fill )
2019 {
2020 mFill.reset( static_cast<QgsFillSymbol *>( symbol ) );
2021 return true;
2022 }
2023 else
2024 {
2025 delete symbol;
2026 return false;
2027 }
2028}
2029
2031{
2032 if ( mFill )
2033 {
2034 return QgsSymbolLayerUtils::estimateMaxSymbolBleed( mFill.get(), context );
2035 }
2036 return 0;
2037}
2038
2040{
2041 QSet<QString> attr = QgsSimpleMarkerSymbolLayerBase::usedAttributes( context );
2042 if ( mFill )
2043 attr.unite( mFill->usedAttributes( context ) );
2044 return attr;
2045}
2046
2048{
2050 return true;
2051 if ( mFill && mFill->hasDataDefinedProperties() )
2052 return true;
2053 return false;
2054}
2055
2057{
2058 mColor = c;
2059 if ( mFill )
2060 mFill->setColor( c );
2061}
2062
2064{
2065 return mFill ? mFill->color() : mColor;
2066}
2067
2074
2076{
2078 if ( mFill )
2079 mFill->setOutputUnit( unit );
2080}
2081
2082void QgsFilledMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
2083{
2084 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
2085 //of the rendered point!
2086
2087 QPainter *p = context.renderContext().painter();
2088 if ( !p )
2089 {
2090 return;
2091 }
2092
2093 const double prevOpacity = mFill->opacity();
2094 mFill->setOpacity( mFill->opacity() * context.opacity() );
2095
2096 if ( shapeIsFilled( shape ) )
2097 {
2098 p->setBrush( Qt::red );
2099 }
2100 else
2101 {
2102 p->setBrush( Qt::NoBrush );
2103 }
2104 p->setPen( Qt::black );
2105
2106 const bool prevIsSubsymbol = context.renderContext().flags() & Qgis::RenderContextFlag::RenderingSubSymbol;
2108
2109 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
2110 if ( !polygon.isEmpty() )
2111 {
2112 mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
2113 }
2114 else
2115 {
2116 const QPolygonF poly = path.toFillPolygon();
2117 mFill->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
2118 }
2119
2121
2122 mFill->setOpacity( prevOpacity );
2123}
2124
2125
2127
2128
2129QgsSvgMarkerSymbolLayer::QgsSvgMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
2130{
2131 mSize = size;
2132 mAngle = angle;
2133 mOffset = QPointF( 0, 0 );
2135 mStrokeWidth = 0.2;
2137 mColor = QColor( 35, 35, 35 );
2138 mStrokeColor = QColor( 35, 35, 35 );
2139 setPath( path );
2140}
2141
2143 : QgsMarkerSymbolLayer( other )
2144 , mPath( other.mPath )
2145 , mDefaultAspectRatio( other.mDefaultAspectRatio )
2146 , mFixedAspectRatio( other.mFixedAspectRatio )
2147 , mHasFillParam( other.mHasFillParam )
2148 , mStrokeColor( other.mStrokeColor )
2149 , mStrokeWidth( other.mStrokeWidth )
2150 , mParameters( other.mParameters )
2151 , mStrokeWidthUnit( other.mStrokeWidthUnit )
2152 , mStrokeWidthMapUnitScale( other.mStrokeWidthMapUnitScale )
2153{
2154}
2155
2157
2159{
2160 QString name;
2164
2165 if ( props.contains( QStringLiteral( "name" ) ) )
2166 name = props[QStringLiteral( "name" )].toString();
2167 if ( props.contains( QStringLiteral( "size" ) ) )
2168 size = props[QStringLiteral( "size" )].toDouble();
2169 if ( props.contains( QStringLiteral( "angle" ) ) )
2170 angle = props[QStringLiteral( "angle" )].toDouble();
2171 if ( props.contains( QStringLiteral( "scale_method" ) ) )
2172 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
2173
2175
2176 if ( props.contains( QStringLiteral( "size_unit" ) ) )
2177 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
2178 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
2179 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
2180 if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
2181 m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
2182 if ( props.contains( QStringLiteral( "offset" ) ) )
2183 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
2184 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
2185 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
2186 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2187 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
2188 if ( props.contains( QStringLiteral( "fill" ) ) )
2189 {
2190 //pre 2.5 projects used "fill"
2191 m->setFillColor( QgsColorUtils::colorFromString( props[QStringLiteral( "fill" )].toString() ) );
2192 }
2193 else if ( props.contains( QStringLiteral( "color" ) ) )
2194 {
2195 m->setFillColor( QgsColorUtils::colorFromString( props[QStringLiteral( "color" )].toString() ) );
2196 }
2197 if ( props.contains( QStringLiteral( "outline" ) ) )
2198 {
2199 //pre 2.5 projects used "outline"
2200 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "outline" )].toString() ) );
2201 }
2202 else if ( props.contains( QStringLiteral( "outline_color" ) ) )
2203 {
2204 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "outline_color" )].toString() ) );
2205 }
2206 else if ( props.contains( QStringLiteral( "line_color" ) ) )
2207 {
2208 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "line_color" )].toString() ) );
2209 }
2210
2211 if ( props.contains( QStringLiteral( "outline-width" ) ) )
2212 {
2213 //pre 2.5 projects used "outline-width"
2214 m->setStrokeWidth( props[QStringLiteral( "outline-width" )].toDouble() );
2215 }
2216 else if ( props.contains( QStringLiteral( "outline_width" ) ) )
2217 {
2218 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
2219 }
2220 else if ( props.contains( QStringLiteral( "line_width" ) ) )
2221 {
2222 m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
2223 }
2224
2225 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
2226 {
2227 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
2228 }
2229 else if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
2230 {
2231 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
2232 }
2233 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
2234 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
2235
2236 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2237 {
2238 m->setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2239 }
2240 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2241 {
2242 m->setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2243 }
2244
2246
2248
2249 if ( props.contains( QStringLiteral( "parameters" ) ) )
2250 {
2251 const QVariantMap parameters = props[QStringLiteral( "parameters" )].toMap();
2253 }
2254
2255 return m;
2256}
2257
2258void QgsSvgMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
2259{
2260 const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
2261 if ( it != properties.end() )
2262 {
2263 if ( saving )
2264 {
2265 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
2266 }
2267 else
2268 {
2269 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
2270 }
2271 }
2272}
2273
2274void QgsSvgMarkerSymbolLayer::setPath( const QString &path )
2275{
2277 mHasFillParam = false;
2278 mPath = path;
2279 QColor defaultFillColor, defaultStrokeColor;
2280 double strokeWidth, fillOpacity, strokeOpacity;
2281 bool hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
2282 bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
2283 QgsApplication::svgCache()->containsParams( path, mHasFillParam, hasDefaultFillColor, defaultFillColor,
2284 hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
2285 hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2286 hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
2287 hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
2288
2289 const double newFillOpacity = hasFillOpacityParam ? fillColor().alphaF() : 1.0;
2290 const double newStrokeOpacity = hasStrokeOpacityParam ? strokeColor().alphaF() : 1.0;
2291
2292 if ( hasDefaultFillColor )
2293 {
2294 defaultFillColor.setAlphaF( newFillOpacity );
2295 setFillColor( defaultFillColor );
2296 }
2297 if ( hasDefaultFillOpacity )
2298 {
2299 QColor c = fillColor();
2300 c.setAlphaF( fillOpacity );
2301 setFillColor( c );
2302 }
2303 if ( hasDefaultStrokeColor )
2304 {
2305 defaultStrokeColor.setAlphaF( newStrokeOpacity );
2306 setStrokeColor( defaultStrokeColor );
2307 }
2308 if ( hasDefaultStrokeWidth )
2309 {
2311 }
2312 if ( hasDefaultStrokeOpacity )
2313 {
2314 QColor c = strokeColor();
2315 c.setAlphaF( strokeOpacity );
2316 setStrokeColor( c );
2317 }
2318
2320}
2321
2323{
2324 if ( mDefaultAspectRatio == 0.0 )
2325 {
2326 //size
2327 const double size = mSize;
2328 //assume 88 dpi as standard value
2329 const double widthScaleFactor = 3.465;
2330 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( mPath, size, mColor, mStrokeColor, mStrokeWidth, widthScaleFactor );
2331 // set default aspect ratio
2332 mDefaultAspectRatio = svgViewbox.isValid() ? svgViewbox.height() / svgViewbox.width() : 0.0;
2333 }
2334 return mDefaultAspectRatio;
2335}
2336
2338{
2339 const bool aPreservedAspectRatio = preservedAspectRatio();
2340 if ( aPreservedAspectRatio && !par )
2341 {
2343 }
2344 else if ( !aPreservedAspectRatio && par )
2345 {
2346 mFixedAspectRatio = 0.0;
2347 }
2348 return preservedAspectRatio();
2349}
2350
2351void QgsSvgMarkerSymbolLayer::setParameters( const QMap<QString, QgsProperty> &parameters )
2352{
2354}
2355
2356
2358{
2359 return QStringLiteral( "SvgMarker" );
2360}
2361
2366
2368{
2369 QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
2370 Q_UNUSED( context )
2371}
2372
2374{
2375 Q_UNUSED( context )
2376}
2377
2379{
2380 QPainter *p = context.renderContext().painter();
2381 if ( !p )
2382 return;
2383
2384 bool hasDataDefinedSize = false;
2385 const double scaledWidth = calculateSize( context, hasDataDefinedSize );
2386 const double devicePixelRatio = context.renderContext().devicePixelRatio();
2387 const double width = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2388
2389 //don't render symbols with a width below one or above 10,000 pixels
2390 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2391 {
2392 return;
2393 }
2394
2395 const QgsScopedQPainterState painterState( p );
2396
2397 bool hasDataDefinedAspectRatio = false;
2398 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2399 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2400
2402
2403 double strokeWidth = mStrokeWidth;
2405 {
2408 }
2410
2411 QColor fillColor = mColor;
2412 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
2413 if ( useSelectedColor && mHasFillParam )
2414 {
2416 }
2418 {
2421 }
2422
2423 QColor strokeColor = mStrokeColor;
2425 {
2428 }
2429
2430 QString path = mPath;
2432 {
2435 context.renderContext().pathResolver() );
2437 {
2438 // adjust height of data defined path
2439 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2440 context.renderContext().scaleFactor(), aspectRatio,
2441 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2442 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2443 }
2444 }
2445
2446 QPointF outputOffset;
2447 double angle = 0.0;
2448 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2449
2450 p->translate( point + outputOffset );
2451
2452 const bool rotated = !qgsDoubleNear( angle, 0 );
2453 if ( rotated )
2454 p->rotate( angle );
2455
2456 bool fitsInCache = true;
2457 bool usePict = true;
2459 if ( ( !context.forceVectorRendering() && !rotated ) || ( useSelectedColor && rasterizeSelected ) )
2460 {
2461 QImage img = QgsApplication::svgCache()->svgAsImage( path, width * devicePixelRatio, fillColor, strokeColor, strokeWidth,
2462 context.renderContext().scaleFactor(), fitsInCache, aspectRatio,
2463 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2464 if ( fitsInCache && img.width() > 1 )
2465 {
2466 usePict = false;
2467
2468 if ( useSelectedColor )
2469 {
2471 }
2472
2473 //consider transparency
2474 if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2475 {
2476 QImage transparentImage = img.copy();
2477 QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2478 if ( devicePixelRatio == 1 )
2479 {
2480 p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
2481 }
2482 else
2483 {
2484 p->drawImage( QRectF( -transparentImage.width() / 2.0 / devicePixelRatio, -transparentImage.height() / 2.0 / devicePixelRatio,
2485 transparentImage.width() / devicePixelRatio, transparentImage.height() / devicePixelRatio
2486 ), transparentImage );
2487 }
2488 }
2489 else
2490 {
2491 if ( devicePixelRatio == 1 )
2492 {
2493 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2494 }
2495 else
2496 {
2497 p->drawImage( QRectF( -img.width() / 2.0 / devicePixelRatio, -img.height() / 2.0 / devicePixelRatio,
2498 img.width() / devicePixelRatio, img.height() / devicePixelRatio ), img );
2499 }
2500 }
2501 }
2502 }
2503
2504 if ( usePict || !fitsInCache )
2505 {
2506 p->setOpacity( context.opacity() );
2508 context.renderContext().scaleFactor(), context.forceVectorRendering(), aspectRatio,
2509 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2510 if ( pct.width() > 1 )
2511 {
2512 QgsPainting::drawPicture( p, QPointF( 0, 0 ), pct );
2513 }
2514 }
2515
2516 // workaround issue with nested QPictures forgetting antialiasing flag - see https://github.com/qgis/QGIS/issues/22909
2518}
2519
2520double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2521{
2522 double scaledSize = mSize;
2524
2525 bool ok = true;
2526 if ( hasDataDefinedSize )
2527 {
2530 }
2531 else
2532 {
2534 if ( hasDataDefinedSize )
2535 {
2538 }
2539 }
2540
2541 if ( hasDataDefinedSize && ok )
2542 {
2543 switch ( mScaleMethod )
2544 {
2546 scaledSize = std::sqrt( scaledSize );
2547 break;
2549 break;
2550 }
2551 }
2552
2553 return scaledSize;
2554}
2555
2556double QgsSvgMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2557{
2559 if ( !hasDataDefinedAspectRatio )
2560 return mFixedAspectRatio;
2561
2563 return 0.0;
2564
2565 double scaledAspectRatio = mDefaultAspectRatio;
2566 if ( mFixedAspectRatio > 0.0 )
2567 scaledAspectRatio = mFixedAspectRatio;
2568
2569 const double defaultHeight = mSize * scaledAspectRatio;
2570 scaledAspectRatio = defaultHeight / scaledSize;
2571
2572 bool ok = true;
2573 double scaledHeight = scaledSize * scaledAspectRatio;
2575 {
2576 context.setOriginalValueVariable( defaultHeight );
2578 }
2579
2580 if ( hasDataDefinedAspectRatio && ok )
2581 {
2582 switch ( mScaleMethod )
2583 {
2585 scaledHeight = sqrt( scaledHeight );
2586 break;
2588 break;
2589 }
2590 }
2591
2592 scaledAspectRatio = scaledHeight / scaledSize;
2593
2594 return scaledAspectRatio;
2595}
2596
2597void QgsSvgMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
2598{
2599 //offset
2600 double offsetX = 0;
2601 double offsetY = 0;
2602 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
2603 offset = QPointF( offsetX, offsetY );
2604
2607 {
2610 }
2611
2613 if ( hasDataDefinedRotation )
2614 {
2615 // For non-point markers, "dataDefinedRotation" means following the
2616 // shape (shape-data defined). For them, "field-data defined" does
2617 // not work at all. TODO: if "field-data defined" ever gets implemented
2618 // we'll need a way to distinguish here between the two, possibly
2619 // using another flag in renderHints()
2620 const QgsFeature *f = context.feature();
2621 if ( f )
2622 {
2623 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
2624 {
2625 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2626 angle += m2p.mapRotation();
2627 }
2628 }
2629 }
2630
2631 if ( angle )
2633}
2634
2635
2637{
2638 QVariantMap map;
2639 map[QStringLiteral( "name" )] = mPath;
2640 map[QStringLiteral( "size" )] = QString::number( mSize );
2641 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2642 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2643 map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2644 map[QStringLiteral( "angle" )] = QString::number( mAngle );
2645 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2646 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2647 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2648 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2649 map[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mColor );
2650 map[QStringLiteral( "outline_color" )] = QgsColorUtils::colorToString( mStrokeColor );
2651 map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
2652 map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2653 map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2654 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
2655 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
2656
2657 map[QStringLiteral( "parameters" )] = QgsProperty::propertyMapToVariantMap( mParameters );
2658
2659 return map;
2660}
2661
2668
2673
2674void QgsSvgMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
2675{
2676 QgsSldExportContext context;
2677 context.setExtraProperties( props );
2678 toSld( doc, element, context );
2679}
2680
2681bool QgsSvgMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
2682{
2683 return QgsMarkerSymbolLayer::toSld( doc, element, context );
2684}
2685
2691
2693{
2695 if ( unit != mStrokeWidthUnit )
2696 {
2698 }
2699 return unit;
2700}
2701
2707
2716
2717void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
2718{
2719 QgsSldExportContext context;
2720 context.setExtraProperties( props );
2721 writeSldMarker( doc, element, context );
2722}
2723
2724bool QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
2725{
2726 // <Graphic>
2727 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2728 element.appendChild( graphicElem );
2729
2730 const QVariantMap props = context.extraProperties();
2731 // encode a parametric SVG reference
2732 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
2735
2736 // <Rotation>
2737 QString angleFunc;
2738 bool ok;
2739 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2740 if ( !ok )
2741 {
2742 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
2743 }
2744 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2745 {
2746 angleFunc = QString::number( angle + mAngle );
2747 }
2748
2749 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc, context );
2750
2751 // <Displacement>
2752 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2754 return true;
2755}
2756
2758{
2759 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
2760
2761 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
2762 if ( graphicElem.isNull() )
2763 return nullptr;
2764
2765 QString path, mimeType;
2766 // Unused and to be DEPRECATED in externalGraphicFromSld
2767 QColor fillColor_;
2768 double size;
2769
2770 if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor_, size ) )
2771 return nullptr;
2772
2773 double scaleFactor = 1.0;
2774 const QString uom = element.attribute( QStringLiteral( "uom" ) );
2775 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
2776 size = size * scaleFactor;
2777
2778 if ( mimeType != QLatin1String( "image/svg+xml" ) )
2779 return nullptr;
2780
2781 double angle = 0.0;
2782 QString angleFunc;
2783 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2784 {
2785 bool ok;
2786 const double d = angleFunc.toDouble( &ok );
2787 if ( ok )
2788 angle = d;
2789 }
2790
2791 QPointF offset;
2793
2794 // Extract parameters from URL
2795 QString realPath { path };
2796 QUrl svgUrl { path };
2797
2798 // Because color definition can start with '#', the url parsing won't recognize the query string entirely
2799 QUrlQuery queryString;
2800
2801 if ( svgUrl.hasQuery() && svgUrl.hasFragment() )
2802 {
2803 const QString queryPart { path.mid( path.indexOf( '?' ) + 1 ) };
2804 queryString.setQuery( queryPart );
2805 }
2806
2807 // Remove query for simple file paths
2808 if ( svgUrl.scheme().isEmpty() || svgUrl.isLocalFile() )
2809 {
2810 svgUrl.setQuery( QString() );
2811 realPath = svgUrl.path();
2812 }
2813
2815
2816 QMap<QString, QgsProperty> params;
2817
2818 bool ok;
2819
2820 if ( queryString.hasQueryItem( QStringLiteral( "fill" ) ) )
2821 {
2822 const QColor fillColor { queryString.queryItemValue( QStringLiteral( "fill" ) ) };
2823 m->setFillColor( fillColor );
2824 }
2825
2826 if ( queryString.hasQueryItem( QStringLiteral( "fill-opacity" ) ) )
2827 {
2828 const double alpha { queryString.queryItemValue( QStringLiteral( "fill-opacity" ) ).toDouble( &ok ) };
2829 if ( ok )
2830 {
2831 params.insert( QStringLiteral( "fill-opacity" ), QgsProperty::fromValue( alpha ) );
2832 }
2833 }
2834
2835 if ( queryString.hasQueryItem( QStringLiteral( "outline" ) ) )
2836 {
2837 const QColor strokeColor { queryString.queryItemValue( QStringLiteral( "outline" ) ) };
2839 }
2840
2841 if ( queryString.hasQueryItem( QStringLiteral( "outline-opacity" ) ) )
2842 {
2843 const double alpha { queryString.queryItemValue( QStringLiteral( "outline-opacity" ) ).toDouble( &ok ) };
2844 if ( ok )
2845 {
2846 params.insert( QStringLiteral( "outline-opacity" ), QgsProperty::fromValue( alpha ) );
2847 }
2848 }
2849
2850 if ( queryString.hasQueryItem( QStringLiteral( "outline-width" ) ) )
2851 {
2852 const int width { queryString.queryItemValue( QStringLiteral( "outline-width" ) ).toInt( &ok )};
2853 if ( ok )
2854 {
2855 m->setStrokeWidth( width );
2856 }
2857 }
2858
2859 if ( ! params.isEmpty() )
2860 {
2861 m->setParameters( params );
2862 }
2863
2864 m->setOutputUnit( sldUnitSize );
2865 m->setAngle( angle );
2866 m->setOffset( offset );
2867 return m;
2868}
2869
2870bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
2871{
2872 //size
2873 double size = mSize;
2874
2875 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
2876
2877 bool ok = true;
2878 if ( hasDataDefinedSize )
2879 {
2882 }
2883
2884 if ( hasDataDefinedSize && ok )
2885 {
2886 switch ( mScaleMethod )
2887 {
2889 size = std::sqrt( size );
2890 break;
2892 break;
2893 }
2894 }
2895
2897 {
2898 size *= mmMapUnitScaleFactor;
2899 }
2900
2901//offset, angle
2902 QPointF offset = mOffset;
2903
2905 {
2908 const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
2909 if ( ok )
2910 offset = res;
2911 }
2912 const double offsetX = offset.x();
2913 const double offsetY = offset.y();
2914
2915 QPointF outputOffset( offsetX, offsetY );
2916
2917 double angle = mAngle + mLineAngle;
2919 {
2922 }
2923
2924 if ( angle )
2925 outputOffset = _rotatedOffset( outputOffset, angle );
2926
2928
2929 QString path = mPath;
2931 {
2934 context.renderContext().pathResolver() );
2935 }
2936
2937 double strokeWidth = mStrokeWidth;
2939 {
2942 }
2944
2945 QColor fillColor = mColor;
2947 {
2950 }
2951
2952 QColor strokeColor = mStrokeColor;
2954 {
2957 }
2958
2960
2961 const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, size, fillColor, strokeColor, strokeWidth,
2963 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2964
2965 QSvgRenderer r( svgContent );
2966 if ( !r.isValid() )
2967 return false;
2968
2969 QgsDxfPaintDevice pd( &e );
2970 pd.setDrawingSize( QSizeF( r.defaultSize() ) );
2971
2972 QSizeF outSize( r.defaultSize() );
2973 outSize.scale( size, size, Qt::KeepAspectRatio );
2974
2975 QPainter p;
2976 p.begin( &pd );
2977 if ( !qgsDoubleNear( angle, 0.0 ) )
2978 {
2979 p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
2980 p.rotate( angle );
2981 p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
2982 }
2983 pd.setShift( shift + QPointF( outputOffset.x(), -outputOffset.y() ) );
2984 pd.setOutputSize( QRectF( -outSize.width() / 2.0, -outSize.height() / 2.0, outSize.width(), outSize.height() ) );
2985 pd.setLayer( layerName );
2986 r.render( &p );
2987 p.end();
2988 return true;
2989}
2990
2992{
2993 bool hasDataDefinedSize = false;
2994 double scaledWidth = calculateSize( context, hasDataDefinedSize );
2995
2996 bool hasDataDefinedAspectRatio = false;
2997 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2998 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2999
3000 scaledWidth = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
3001 scaledHeight = context.renderContext().convertToPainterUnits( scaledHeight, mSizeUnit, mSizeMapUnitScale );
3002
3003 //don't render symbols with size below one or above 10,000 pixels
3004 if ( static_cast< int >( scaledWidth ) < 1 || 10000.0 < scaledWidth )
3005 {
3006 return QRectF();
3007 }
3008
3009 QPointF outputOffset;
3010 double angle = 0.0;
3011 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
3012
3013 double strokeWidth = mStrokeWidth;
3015 {
3018 }
3020
3021 QString path = mPath;
3023 {
3026 context.renderContext().pathResolver() );
3028 {
3029 // need to get colors to take advantage of cached SVGs
3030 QColor fillColor = mColor;
3032 {
3035 }
3036
3037 const QColor strokeColor = mStrokeColor;
3039 {
3042 }
3043
3045
3046 // adjust height of data defined path
3047 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
3048 context.renderContext().scaleFactor(), aspectRatio,
3049 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
3050 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
3051 }
3052 }
3053
3054 QTransform transform;
3055 // move to the desired position
3056 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3057
3058 if ( !qgsDoubleNear( angle, 0.0 ) )
3059 transform.rotate( angle );
3060
3061 //antialiasing
3062 strokeWidth += 1.0 / 2.0;
3063
3064 QRectF symbolBounds = transform.mapRect( QRectF( -scaledWidth / 2.0,
3065 -scaledHeight / 2.0,
3066 scaledWidth,
3067 scaledHeight ) );
3068
3069 //extend bounds by pen width / 2.0
3070 symbolBounds.adjust( -strokeWidth / 2.0, -strokeWidth / 2.0,
3071 strokeWidth / 2.0, strokeWidth / 2.0 );
3072
3073 return symbolBounds;
3074}
3075
3077
3078QgsRasterMarkerSymbolLayer::QgsRasterMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
3079 : mPath( path )
3080{
3081 mSize = size;
3082 mAngle = angle;
3083 mOffset = QPointF( 0, 0 );
3086}
3087
3089
3091{
3092 QString path;
3096
3097 if ( props.contains( QStringLiteral( "imageFile" ) ) )
3098 path = props[QStringLiteral( "imageFile" )].toString();
3099 if ( props.contains( QStringLiteral( "size" ) ) )
3100 size = props[QStringLiteral( "size" )].toDouble();
3101 if ( props.contains( QStringLiteral( "angle" ) ) )
3102 angle = props[QStringLiteral( "angle" )].toDouble();
3103 if ( props.contains( QStringLiteral( "scale_method" ) ) )
3104 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
3105
3106 auto m = std::make_unique< QgsRasterMarkerSymbolLayer >( path, size, angle, scaleMethod );
3107 m->setCommonProperties( props );
3108 return m.release();
3109}
3110
3112{
3113 const QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
3114 if ( graphicElem.isNull() )
3115 return nullptr;
3116
3117 const QDomElement externalGraphicElem = graphicElem.firstChildElement( QStringLiteral( "ExternalGraphic" ) );
3118 if ( externalGraphicElem.isNull() )
3119 return nullptr;
3120
3121 const QDomElement onlineResourceElem = externalGraphicElem.firstChildElement( QStringLiteral( "OnlineResource" ) );
3122 const QDomElement inlineContentElem = externalGraphicElem.firstChildElement( QStringLiteral( "InlineContent" ) );
3123
3124 QString url;
3125 if ( !onlineResourceElem.isNull() )
3126 {
3127 url = onlineResourceElem.attribute( QStringLiteral( "href" ) );
3128 // no further processing to do, both base64 data urls and direct file/http urls are compatible with raster markers already
3129 }
3130 else if ( !inlineContentElem.isNull() && inlineContentElem.attribute( QStringLiteral( "encoding" ) ) == QLatin1String( "base64" ) )
3131 {
3132 url = QStringLiteral( "base64:" ) + inlineContentElem.text();
3133 }
3134 else
3135 {
3136 return nullptr;
3137 }
3138
3140 // TODO: parse other attributes from the SLD spec (Opacity, Size, Rotation, AnchorPoint, Displacement)
3141 return m;
3142}
3143
3144void QgsRasterMarkerSymbolLayer::setCommonProperties( const QVariantMap &properties )
3145{
3146 if ( properties.contains( QStringLiteral( "alpha" ) ) )
3147 {
3148 setOpacity( properties[QStringLiteral( "alpha" )].toDouble() );
3149 }
3150
3151 if ( properties.contains( QStringLiteral( "size_unit" ) ) )
3152 setSizeUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "size_unit" )].toString() ) );
3153 if ( properties.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3154 setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3155 if ( properties.contains( QStringLiteral( "fixedAspectRatio" ) ) )
3156 setFixedAspectRatio( properties[QStringLiteral( "fixedAspectRatio" )].toDouble() );
3157
3158 if ( properties.contains( QStringLiteral( "offset" ) ) )
3159 setOffset( QgsSymbolLayerUtils::decodePoint( properties[QStringLiteral( "offset" )].toString() ) );
3160 if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
3161 setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )].toString() ) );
3162 if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3163 setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3164
3165 if ( properties.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3166 {
3167 setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( properties[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3168 }
3169 if ( properties.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3170 {
3171 setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( properties[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3172 }
3173
3176}
3177
3178void QgsRasterMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
3179{
3180 const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
3181 if ( it != properties.end() && it.value().userType() == QMetaType::Type::QString )
3182 {
3183 if ( saving )
3184 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
3185 else
3186 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
3187 }
3188}
3189
3190void QgsRasterMarkerSymbolLayer::setPath( const QString &path )
3191{
3192 mPath = path;
3194}
3195
3197{
3198 const bool aPreservedAspectRatio = preservedAspectRatio();
3199 if ( aPreservedAspectRatio && !par )
3200 {
3202 }
3203 else if ( !aPreservedAspectRatio && par )
3204 {
3205 mFixedAspectRatio = 0.0;
3206 }
3207 return preservedAspectRatio();
3208}
3209
3211{
3212 if ( mDefaultAspectRatio == 0.0 && !mPath.isEmpty() )
3213 {
3215 mDefaultAspectRatio = ( !size.isNull() && size.isValid() && size.width() > 0 ) ? static_cast< double >( size.height() ) / static_cast< double >( size.width() ) : 0.0;
3216 }
3217 return mDefaultAspectRatio;
3218}
3219
3221{
3222 return QStringLiteral( "RasterMarker" );
3223}
3224
3229
3231{
3232 QPainter *p = context.renderContext().painter();
3233 if ( !p )
3234 return;
3235
3236 QString path = mPath;
3238 {
3241 }
3242
3243 if ( path.isEmpty() )
3244 return;
3245
3246 double width = 0.0;
3247 double height = 0.0;
3248
3249 bool hasDataDefinedSize = false;
3250 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3251
3252 bool hasDataDefinedAspectRatio = false;
3253 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3254
3255 QPointF outputOffset;
3256 double angle = 0.0;
3257
3258 // RenderPercentage Unit Type takes original image size
3260 {
3262 if ( size.isEmpty() )
3263 return;
3264
3265 width = ( scaledSize * static_cast< double >( size.width() ) ) / 100.0;
3266 height = ( scaledSize * static_cast< double >( size.height() ) ) / 100.0;
3267
3268 // don't render symbols with size below one or above 10,000 pixels
3269 if ( static_cast< int >( width ) < 1 || 10000.0 < width || static_cast< int >( height ) < 1 || 10000.0 < height )
3270 return;
3271
3272 calculateOffsetAndRotation( context, width, height, outputOffset, angle );
3273 }
3274 else
3275 {
3276 width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3277 height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3278
3279 if ( preservedAspectRatio() && path != mPath )
3280 {
3282 if ( !size.isNull() && size.isValid() && size.width() > 0 )
3283 {
3284 height = width * ( static_cast< double >( size.height() ) / static_cast< double >( size.width() ) );
3285 }
3286 }
3287
3288 // don't render symbols with size below one or above 10,000 pixels
3289 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
3290 return;
3291
3292 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3293 }
3294
3295 const QgsScopedQPainterState painterState( p );
3296 p->translate( point + outputOffset );
3297
3298 const bool rotated = !qgsDoubleNear( angle, 0 );
3299 if ( rotated )
3300 p->rotate( angle );
3301
3302 double opacity = mOpacity;
3304 {
3307 }
3308 opacity *= context.opacity();
3309
3310 QImage img = fetchImage( context.renderContext(), path, QSize( width, preservedAspectRatio() ? 0 : width * aspectRatio ), preservedAspectRatio(), opacity );
3311 if ( !img.isNull() )
3312 {
3313 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
3314 if ( useSelectedColor )
3315 {
3317 }
3318
3319 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
3320 }
3321}
3322
3323QImage QgsRasterMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
3324{
3325 bool cached = false;
3326 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking );
3327}
3328
3329double QgsRasterMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
3330{
3331 double scaledSize = mSize;
3333
3334 bool ok = true;
3335 if ( hasDataDefinedSize )
3336 {
3339 }
3340 else
3341 {
3343 if ( hasDataDefinedSize )
3344 {
3347 }
3348 }
3349
3350 if ( hasDataDefinedSize && ok )
3351 {
3352 switch ( mScaleMethod )
3353 {
3355 scaledSize = std::sqrt( scaledSize );
3356 break;
3358 break;
3359 }
3360 }
3361
3362 return scaledSize;
3363}
3364
3365double QgsRasterMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
3366{
3368 if ( !hasDataDefinedAspectRatio )
3369 return mFixedAspectRatio;
3370
3372 return 0.0;
3373
3374 double scaledAspectRatio = mDefaultAspectRatio;
3375 if ( mFixedAspectRatio > 0.0 )
3376 scaledAspectRatio = mFixedAspectRatio;
3377
3378 const double defaultHeight = mSize * scaledAspectRatio;
3379 scaledAspectRatio = defaultHeight / scaledSize;
3380
3381 bool ok = true;
3382 double scaledHeight = scaledSize * scaledAspectRatio;
3384 {
3385 context.setOriginalValueVariable( defaultHeight );
3387 }
3388
3389 if ( hasDataDefinedAspectRatio && ok )
3390 {
3391 switch ( mScaleMethod )
3392 {
3394 scaledHeight = sqrt( scaledHeight );
3395 break;
3397 break;
3398 }
3399 }
3400
3401 scaledAspectRatio = scaledHeight / scaledSize;
3402
3403 return scaledAspectRatio;
3404}
3405
3406void QgsRasterMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
3407{
3408 //offset
3409 double offsetX = 0;
3410 double offsetY = 0;
3411 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
3412 offset = QPointF( offsetX, offsetY );
3413
3416 {
3419 }
3420
3422 if ( hasDataDefinedRotation )
3423 {
3424 const QgsFeature *f = context.feature();
3425 if ( f )
3426 {
3427 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3428 {
3429 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3430 angle += m2p.mapRotation();
3431 }
3432 }
3433 }
3434
3435 if ( angle )
3437}
3438
3439
3441{
3442 QVariantMap map;
3443 map[QStringLiteral( "imageFile" )] = mPath;
3444 map[QStringLiteral( "size" )] = QString::number( mSize );
3445 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3446 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3447 map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
3448 map[QStringLiteral( "angle" )] = QString::number( mAngle );
3449 map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
3450 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3451 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3452 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3453 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
3454 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
3455 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
3456 return map;
3457}
3458
3460{
3461 auto m = std::make_unique< QgsRasterMarkerSymbolLayer >();
3462 m->mPath = mPath;
3463 m->mDefaultAspectRatio = mDefaultAspectRatio;
3464 m->mSize = mSize;
3465 m->mAngle = mAngle;
3466 // other members are copied by:
3467 copyCommonProperties( m.get() );
3468 return m.release();
3469}
3470
3471
3486
3492
3494{
3495 return QColor();
3496}
3497
3502
3507
3509{
3510 bool hasDataDefinedSize = false;
3511 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3512 const double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3513 bool hasDataDefinedAspectRatio = false;
3514 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3515 const double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3516
3517 //don't render symbols with size below one or above 10,000 pixels
3518 if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
3519 {
3520 return QRectF();
3521 }
3522
3523 QPointF outputOffset;
3524 double angle = 0.0;
3525 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3526
3527 QTransform transform;
3528
3529 // move to the desired position
3530 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3531
3532 if ( !qgsDoubleNear( angle, 0.0 ) )
3533 transform.rotate( angle );
3534
3535 QRectF symbolBounds = transform.mapRect( QRectF( -width / 2.0,
3536 -height / 2.0,
3537 width,
3538 height ) );
3539
3540 return symbolBounds;
3541}
3542
3543void QgsRasterMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
3544{
3545 QgsSldExportContext context;
3546 context.setExtraProperties( props );
3547 writeSldMarker( doc, element, context );
3548}
3549
3550bool QgsRasterMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
3551{
3552 Q_UNUSED( context )
3553
3554 // <Graphic>
3555 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
3556 element.appendChild( graphicElem );
3557
3558 // <ExternalGraphic>
3559 QDomElement extGraphElem = doc.createElement( QStringLiteral( "se:ExternalGraphic" ) );
3560 graphicElem.appendChild( extGraphElem );
3561
3562 QMimeDatabase mimeDB;
3563 QMimeType mimeType;
3564
3565 QString base64data;
3566 if ( mPath.startsWith( QStringLiteral( "base64:" ) ) )
3567 {
3568 base64data = mPath.mid( 7 );
3569 }
3570 else
3571 {
3572 QString mime;
3573 QString data;
3575 {
3576 base64data = data;
3577 }
3578 }
3579
3580 if ( !base64data.isEmpty() )
3581 {
3582 // <InlineContent>
3583 QDomElement inlineContEleme = doc.createElement( QStringLiteral( "se:InlineContent" ) );
3584
3585 inlineContEleme.setAttribute( QStringLiteral( "encoding" ), QStringLiteral( "base64" ) );
3586 inlineContEleme.appendChild( doc.createTextNode( base64data ) );
3587 extGraphElem.appendChild( inlineContEleme );
3588
3589 // determine mime type
3590 const QByteArray ba = QByteArray::fromBase64( base64data.toUtf8() );
3591 mimeType = mimeDB.mimeTypeForData( ba );
3592 }
3593 else
3594 {
3595 // <ExternalGraphic>
3596 QDomElement onlineResElem = doc.createElement( QStringLiteral( "se:OnlineResource" ) );
3597 QString url = mPath;
3598
3599 onlineResElem.setAttribute( QStringLiteral( "xlink:href" ), url );
3600 onlineResElem.setAttribute( QStringLiteral( "xlink:type" ), QStringLiteral( "simple" ) );
3601 extGraphElem.appendChild( onlineResElem );
3602
3603 // determine mime type
3604 if ( mPath.startsWith( QStringLiteral( "http://" ) ) || mPath.startsWith( QStringLiteral( "https://" ) ) )
3605 {
3606 // Qt can't guess mime type for remote URLs, and it seems geoserver can handle wrong image mime types
3607 // but not generic ones, so let's hardcode to png.
3608 mimeType = mimeDB.mimeTypeForName( "image/png" );
3609 }
3610 else
3611 {
3612 mimeType = mimeDB.mimeTypeForUrl( url );
3613 }
3614 }
3615
3616 QDomElement formatElem = doc.createElement( QStringLiteral( "se:Format" ) );
3617 formatElem.appendChild( doc.createTextNode( mimeType.name() ) );
3618 extGraphElem.appendChild( formatElem );
3619
3620 // TODO: write other attributes from the SLD spec (Opacity, Size, Rotation, AnchorPoint, Displacement)
3621 return true;
3622}
3623
3625
3626QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QString chr, double pointSize, const QColor &color, double angle )
3627{
3628 mFontFamily = fontFamily;
3629 mString = chr;
3630 mColor = color;
3631 mAngle = angle;
3632 mSize = pointSize;
3633 mOrigSize = pointSize;
3635 mOffset = QPointF( 0, 0 );
3637 mStrokeColor = DEFAULT_FONTMARKER_BORDERCOLOR;
3638 mStrokeWidth = 0.0;
3639 mStrokeWidthUnit = Qgis::RenderUnit::Millimeters;
3640 mPenJoinStyle = DEFAULT_FONTMARKER_JOINSTYLE;
3641}
3642
3644
3646{
3648 QString string = DEFAULT_FONTMARKER_CHR;
3649 double pointSize = DEFAULT_FONTMARKER_SIZE;
3652
3653 if ( props.contains( QStringLiteral( "font" ) ) )
3654 fontFamily = props[QStringLiteral( "font" )].toString();
3655 if ( props.contains( QStringLiteral( "chr" ) ) && props[QStringLiteral( "chr" )].toString().length() > 0 )
3656 {
3657 string = props["chr"].toString();
3658 const thread_local QRegularExpression charRegExp( QStringLiteral( "%1([0-9]+)%1" ).arg( FONTMARKER_CHR_FIX ) );
3659 QRegularExpressionMatch match = charRegExp.match( string );
3660 while ( match.hasMatch() )
3661 {
3662 QChar replacement = QChar( match.captured( 1 ).toUShort() );
3663 string = string.mid( 0, match.capturedStart( 0 ) ) + replacement + string.mid( match.capturedEnd( 0 ) );
3664 match = charRegExp.match( string );
3665 }
3666 }
3667
3668 if ( props.contains( QStringLiteral( "size" ) ) )
3669 pointSize = props[QStringLiteral( "size" )].toDouble();
3670 if ( props.contains( QStringLiteral( "color" ) ) )
3671 color = QgsColorUtils::colorFromString( props[QStringLiteral( "color" )].toString() );
3672 if ( props.contains( QStringLiteral( "angle" ) ) )
3673 angle = props[QStringLiteral( "angle" )].toDouble();
3674
3676
3677 if ( props.contains( QStringLiteral( "font_style" ) ) )
3678 m->setFontStyle( props[QStringLiteral( "font_style" )].toString() );
3679 if ( props.contains( QStringLiteral( "outline_color" ) ) )
3680 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "outline_color" )].toString() ) );
3681 if ( props.contains( QStringLiteral( "outline_width" ) ) )
3682 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
3683 if ( props.contains( QStringLiteral( "offset" ) ) )
3684 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
3685 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
3686 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
3687 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3688 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3689 if ( props.contains( QStringLiteral( "size_unit" ) ) )
3690 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
3691 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3692 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3693 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
3694 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
3695 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
3696 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
3697 if ( props.contains( QStringLiteral( "joinstyle" ) ) )
3698 m->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() ) );
3699 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3700 m->setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3701 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3702 m->setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3703
3705
3706 return m;
3707}
3708
3710{
3711 return QStringLiteral( "FontMarker" );
3712}
3713
3718
3720{
3721 QColor brushColor = mColor;
3722 QColor penColor = mStrokeColor;
3723
3724 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
3725 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
3726
3727 mBrush = QBrush( brushColor );
3728 mPen = QPen( penColor );
3729 mPen.setJoinStyle( mPenJoinStyle );
3730 mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
3731
3732 mFont = QgsFontUtils::createFont( QgsApplication::fontManager()->processFontFamilyName( mFontFamily ) );
3733 if ( !mFontStyle.isEmpty() )
3734 {
3735 mFont.setStyleName( QgsFontUtils::translateNamedStyle( mFontStyle ) );
3736 }
3737
3738 double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3739 mNonZeroFontSize = !qgsDoubleNear( sizePixels, 0.0 );
3740
3741 if ( mNonZeroFontSize && sizePixels > MAX_FONT_CHARACTER_SIZE_IN_PIXELS )
3742 {
3743 // if font is too large (e.g using map units and map is very zoomed in), then we limit
3744 // the font size and instead scale up the painter.
3745 // this avoids issues with massive font sizes (eg https://github.com/qgis/QGIS/issues/42270)
3746 mFontSizeScale = sizePixels / MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3747 sizePixels = MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3748 }
3749 else
3750 mFontSizeScale = 1.0;
3751
3752 // if a non zero, but small pixel size results, round up to 2 pixels so that a "dot" is at least visible
3753 // (if we set a <=1 pixel size here Qt will reset the font to a default size, leading to much too large symbols)
3754 mFont.setPixelSize( std::max( 2, static_cast< int >( std::round( sizePixels ) ) ) );
3755 mFontMetrics.reset( new QFontMetrics( mFont ) );
3756 mChrWidth = mFontMetrics->horizontalAdvance( mString );
3757 switch ( mVerticalAnchorPoint )
3758 {
3760 {
3761 mChrOffset = QPointF( mChrWidth / 2.0, -sizePixels / 2.0 );
3762 break;
3763 }
3767 {
3768 mChrOffset = QPointF( mChrWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3769 break;
3770 }
3771 }
3772 mOrigSize = mSize; // save in case the size would be data defined
3773
3774 // use caching only when not using a data defined character
3778 if ( mUseCachedPath )
3779 {
3780 QPointF chrOffset = mChrOffset;
3781 double chrWidth;
3782 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3783 mCachedPath = QPainterPath();
3784 mCachedPath.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3785 }
3786}
3787
3789{
3790 Q_UNUSED( context )
3791}
3792
3793QString QgsFontMarkerSymbolLayer::characterToRender( QgsSymbolRenderContext &context, QPointF &charOffset, double &charWidth )
3794{
3795 charOffset = mChrOffset;
3796 QString stringToRender = mString;
3798 {
3799 context.setOriginalValueVariable( mString );
3801 if ( stringToRender != mString )
3802 {
3803 charWidth = mFontMetrics->horizontalAdvance( stringToRender );
3804 switch ( mVerticalAnchorPoint )
3805 {
3807 {
3808 const double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3809 charOffset = QPointF( charWidth / 2.0, -sizePixels / 2.0 );
3810 break;
3811 }
3815 {
3816 charOffset = QPointF( charWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3817 break;
3818 }
3819 }
3820 }
3821 }
3822 return stringToRender;
3823}
3824
3825void QgsFontMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
3826 double scaledSize,
3827 bool &hasDataDefinedRotation,
3828 QPointF &offset,
3829 double &angle ) const
3830{
3831 //offset
3832 double offsetX = 0;
3833 double offsetY = 0;
3834 markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
3835 offset = QPointF( offsetX, offsetY );
3836 hasDataDefinedRotation = false;
3837
3838 //angle
3839 bool ok = true;
3842 {
3845
3846 // If the expression evaluation was not successful, fallback to static value
3847 if ( !ok )
3849
3850 hasDataDefinedRotation = true;
3851 }
3852
3853 hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || hasDataDefinedRotation;
3854 if ( hasDataDefinedRotation )
3855 {
3856 // For non-point markers, "dataDefinedRotation" means following the
3857 // shape (shape-data defined). For them, "field-data defined" does
3858 // not work at all. TODO: if "field-data defined" ever gets implemented
3859 // we'll need a way to distinguish here between the two, possibly
3860 // using another flag in renderHints()
3861 const QgsFeature *f = context.feature();
3862 if ( f )
3863 {
3864 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3865 {
3866 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3867 angle += m2p.mapRotation();
3868 }
3869 }
3870 }
3871
3872 if ( angle )
3874}
3875
3876double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context )
3877{
3878 double scaledSize = mSize;
3879 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
3880
3881 bool ok = true;
3882 if ( hasDataDefinedSize )
3883 {
3886 }
3887
3888 if ( hasDataDefinedSize && ok )
3889 {
3890 switch ( mScaleMethod )
3891 {
3893 scaledSize = std::sqrt( scaledSize );
3894 break;
3896 break;
3897 }
3898 }
3899 return scaledSize;
3900}
3901
3903{
3904 QPainter *p = context.renderContext().painter();
3905 if ( !p || !mNonZeroFontSize )
3906 return;
3907
3908 QTransform transform;
3909
3910 bool ok;
3911 QColor brushColor = mColor;
3913 {
3916 }
3917 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
3918 brushColor = useSelectedColor ? context.renderContext().selectionColor() : brushColor;
3919 if ( !useSelectedColor || !SELECTION_IS_OPAQUE )
3920 {
3921 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
3922 }
3923 mBrush.setColor( brushColor );
3924
3925 QColor penColor = mStrokeColor;
3927 {
3930 }
3931 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
3932
3933 double penWidth = context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3935 {
3936 context.setOriginalValueVariable( mStrokeWidth );
3938 if ( ok )
3939 {
3940 penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3941 }
3942 }
3943
3945 {
3948 if ( ok )
3949 {
3950 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
3951 }
3952 }
3953
3954 const QgsScopedQPainterState painterState( p );
3955 p->setBrush( mBrush );
3956 if ( !qgsDoubleNear( penWidth, 0.0 ) )
3957 {
3958 mPen.setColor( penColor );
3959 mPen.setWidthF( penWidth );
3960 p->setPen( mPen );
3961 }
3962 else
3963 {
3964 p->setPen( Qt::NoPen );
3965 }
3966
3968 {
3969 context.setOriginalValueVariable( mFontFamily );
3971 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( ok ? fontFamily : mFontFamily );
3972 QgsFontUtils::setFontFamily( mFont, processedFamily );
3973 }
3975 {
3976 context.setOriginalValueVariable( mFontStyle );
3979 }
3981 {
3982 mFontMetrics.reset( new QFontMetrics( mFont ) );
3983 }
3984
3985 QPointF chrOffset = mChrOffset;
3986 double chrWidth;
3987 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3988
3989 const double sizeToRender = calculateSize( context );
3990
3991 bool hasDataDefinedRotation = false;
3992 QPointF offset;
3993 double angle = 0;
3994 calculateOffsetAndRotation( context, sizeToRender, hasDataDefinedRotation, offset, angle );
3995
3996 p->translate( point.x() + offset.x(), point.y() + offset.y() );
3997
3998 if ( !qgsDoubleNear( angle, 0.0 ) )
3999 transform.rotate( angle );
4000
4001 if ( !qgsDoubleNear( sizeToRender, mOrigSize ) )
4002 {
4003 const double s = sizeToRender / mOrigSize;
4004 transform.scale( s, s );
4005 }
4006
4007 if ( !qgsDoubleNear( mFontSizeScale, 1.0 ) )
4008 transform.scale( mFontSizeScale, mFontSizeScale );
4009
4010 if ( mUseCachedPath )
4011 {
4012 p->drawPath( transform.map( mCachedPath ) );
4013 }
4014 else
4015 {
4016 QPainterPath path;
4017 path.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
4018 p->drawPath( transform.map( path ) );
4019 }
4020}
4021
4023{
4024 QVariantMap props;
4025 props[QStringLiteral( "font" )] = mFontFamily;
4026 props[QStringLiteral( "font_style" )] = mFontStyle;
4027 QString chr = mString;
4028 for ( int i = 0; i < 32; i++ )
4029 {
4030 if ( i == 9 || i == 10 || i == 13 )
4031 {
4032 continue;
4033 }
4034 chr.replace( QChar( i ), QStringLiteral( "%1%2%1" ).arg( FONTMARKER_CHR_FIX, QString::number( i ) ) );
4035 }
4036 props[QStringLiteral( "chr" )] = chr;
4037 props[QStringLiteral( "size" )] = QString::number( mSize );
4038 props[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
4039 props[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
4040 props[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mColor );
4041 props[QStringLiteral( "outline_color" )] = QgsColorUtils::colorToString( mStrokeColor );
4042 props[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
4043 props[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
4044 props[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
4045 props[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
4046 props[QStringLiteral( "angle" )] = QString::number( mAngle );
4047 props[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
4048 props[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
4049 props[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
4050 props[QStringLiteral( "horizontal_anchor_point" )] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
4051 props[QStringLiteral( "vertical_anchor_point" )] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
4052 return props;
4053}
4054
4056{
4057 QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( mFontFamily, mString, mSize, mColor, mAngle );
4058 m->setFontStyle( mFontStyle );
4059 m->setStrokeColor( mStrokeColor );
4060 m->setStrokeWidth( mStrokeWidth );
4061 m->setStrokeWidthUnit( mStrokeWidthUnit );
4062 m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
4063 m->setPenJoinStyle( mPenJoinStyle );
4064 m->setOffset( mOffset );
4067 m->setSizeUnit( mSizeUnit );
4072 copyPaintEffect( m );
4073 return m;
4074}
4075
4076void QgsFontMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
4077{
4078 QgsSldExportContext context;
4079 context.setExtraProperties( props );
4080 toSld( doc, element, context );
4081}
4082
4083bool QgsFontMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
4084{
4085 return QgsMarkerSymbolLayer::toSld( doc, element, context );
4086}
4087
4088void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
4089{
4090 QgsSldExportContext context;
4091 context.setExtraProperties( props );
4092 writeSldMarker( doc, element, context );
4093}
4094
4095bool QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
4096{
4097 // <Graphic>
4098 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
4099 element.appendChild( graphicElem );
4100
4101 const QVariantMap props = context.extraProperties();
4102 const QString fontPath = QStringLiteral( "ttf://%1" ).arg( mFontFamily );
4103 int markIndex = !mString.isEmpty() ? mString.at( 0 ).unicode() : 0;
4104 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
4105 QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, QStringLiteral( "ttf" ), context, &markIndex, mColor, size );
4106
4107 // <Rotation>
4108 QString angleFunc;
4109 bool ok;
4110 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
4111 if ( !ok )
4112 {
4113 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
4114 }
4115 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
4116 {
4117 angleFunc = QString::number( angle + mAngle );
4118 }
4119 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc, context );
4120
4121 // <Displacement>
4122 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
4124 return true;
4125}
4126
4133
4135{
4137 mStrokeWidthUnit = unit;
4138}
4139
4141{
4142 QPointF chrOffset = mChrOffset;
4143 double chrWidth = mChrWidth;
4144 //calculate width of rendered character
4145 ( void )characterToRender( context, chrOffset, chrWidth );
4146
4147 if ( !mFontMetrics )
4148 mFontMetrics.reset( new QFontMetrics( mFont ) );
4149
4150 double scaledSize = calculateSize( context );
4151 if ( !qgsDoubleNear( scaledSize, mOrigSize ) )
4152 {
4153 chrWidth *= scaledSize / mOrigSize;
4154 }
4155 chrWidth *= mFontSizeScale;
4156
4157 bool hasDataDefinedRotation = false;
4158 QPointF offset;
4159 double angle = 0;
4160 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
4161 scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
4162
4163 QTransform transform;
4164
4165 // move to the desired position
4166 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
4167
4168 if ( !qgsDoubleNear( angle, 0.0 ) )
4169 transform.rotate( angle );
4170
4171 QRectF symbolBounds = transform.mapRect( QRectF( -chrWidth / 2.0,
4172 -scaledSize / 2.0,
4173 chrWidth,
4174 scaledSize ) );
4175 return symbolBounds;
4176}
4177
4179{
4180 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
4181
4182 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
4183 if ( graphicElem.isNull() )
4184 return nullptr;
4185
4186 QString name, format;
4187 QColor color;
4188 double size;
4189 int chr;
4190
4191 if ( !QgsSymbolLayerUtils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
4192 return nullptr;
4193
4194 if ( !name.startsWith( QLatin1String( "ttf://" ) ) || format != QLatin1String( "ttf" ) )
4195 return nullptr;
4196
4197 const QString fontFamily = name.mid( 6 );
4198
4199 double angle = 0.0;
4200 QString angleFunc;
4201 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
4202 {
4203 bool ok;
4204 const double d = angleFunc.toDouble( &ok );
4205 if ( ok )
4206 angle = d;
4207 }
4208
4209 QPointF offset;
4211
4212 double scaleFactor = 1.0;
4213 const QString uom = element.attribute( QStringLiteral( "uom" ) );
4214 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
4215 offset.setX( offset.x() * scaleFactor );
4216 offset.setY( offset.y() * scaleFactor );
4217 size = size * scaleFactor;
4218
4220 m->setOutputUnit( sldUnitSize );
4221 m->setAngle( angle );
4222 m->setOffset( offset );
4223 return m;
4224}
4225
4226void QgsFontMarkerSymbolLayer::resolveFonts( const QVariantMap &properties, const QgsReadWriteContext &context )
4227{
4228 const QString fontFamily = properties.value( QStringLiteral( "font" ), DEFAULT_FONTMARKER_FONT ).toString();
4229 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( fontFamily );
4230 QString matched;
4231 if ( !QgsFontUtils::fontFamilyMatchOnSystem( processedFamily )
4232 && !QgsApplication::fontManager()->tryToDownloadFontFamily( processedFamily, matched ) )
4233 {
4234 context.pushMessage( QObject::tr( "Font “%1” not available on system" ).arg( processedFamily ) );
4235 }
4236}
4237
4239{
4240 QMap<QString, QgsProperty>::iterator it = mParameters.begin();
4241 for ( ; it != mParameters.end(); ++it )
4242 it.value().prepare( context.renderContext().expressionContext() );
4243
4245}
4246
4247
4249{
4250 QSet<QString> attrs = QgsMarkerSymbolLayer::usedAttributes( context );
4251
4252 QMap<QString, QgsProperty>::const_iterator it = mParameters.constBegin();
4253 for ( ; it != mParameters.constEnd(); ++it )
4254 {
4255 attrs.unite( it.value().referencedFields( context.expressionContext(), true ) );
4256 }
4257
4258 return attrs;
4259}
4260
4261//
4262// QgsAnimatedMarkerSymbolLayer
4263//
4264
4265QgsAnimatedMarkerSymbolLayer::QgsAnimatedMarkerSymbolLayer( const QString &path, double size, double angle )
4266 : QgsRasterMarkerSymbolLayer( path, size, angle )
4267{
4268
4269}
4270
4272
4273QgsSymbolLayer *QgsAnimatedMarkerSymbolLayer::create( const QVariantMap &properties ) // cppcheck-suppress duplInheritedMember
4274{
4275 QString path;
4278
4279 if ( properties.contains( QStringLiteral( "imageFile" ) ) )
4280 path = properties[QStringLiteral( "imageFile" )].toString();
4281 if ( properties.contains( QStringLiteral( "size" ) ) )
4282 size = properties[QStringLiteral( "size" )].toDouble();
4283 if ( properties.contains( QStringLiteral( "angle" ) ) )
4284 angle = properties[QStringLiteral( "angle" )].toDouble();
4285
4286 auto m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( path, size, angle );
4287 m->setFrameRate( properties.value( QStringLiteral( "frameRate" ), QStringLiteral( "10" ) ).toDouble() );
4288
4289 m->setCommonProperties( properties );
4290 return m.release();
4291}
4292
4294{
4295 return QStringLiteral( "AnimatedMarker" );
4296}
4297
4299{
4300 QVariantMap res = QgsRasterMarkerSymbolLayer::properties();
4301 res.insert( QStringLiteral( "frameRate" ), mFrameRateFps );
4302 return res;
4303}
4304
4306{
4307 auto m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( mPath, mSize, mAngle );
4308 m->setFrameRate( mFrameRateFps );
4309 copyCommonProperties( m.get() );
4310 return m.release();
4311}
4312
4314{
4316
4317 mPreparedPaths.clear();
4319 {
4321 mStaticPath = true;
4322 }
4323 else
4324 {
4325 mStaticPath = false;
4326 }
4327}
4328
4329QImage QgsAnimatedMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
4330{
4331 if ( !mStaticPath && !mPreparedPaths.contains( path ) )
4332 {
4334 mPreparedPaths.insert( path );
4335 }
4336
4337 const long long mapFrameNumber = context.currentFrame();
4339 const double markerAnimationDuration = totalFrameCount / mFrameRateFps;
4340
4341 double animationTimeSeconds = 0;
4342 if ( mapFrameNumber >= 0 && context.frameRate() > 0 )
4343 {
4344 // render is part of an animation, so we base the calculated frame on that
4345 animationTimeSeconds = mapFrameNumber / context.frameRate();
4346 }
4347 else
4348 {
4349 // render is outside of animation, so base the calculated frame on the current epoch
4350 animationTimeSeconds = QDateTime::currentMSecsSinceEpoch() / 1000.0;
4351 }
4352
4353 const double markerAnimationProgressSeconds = std::fmod( animationTimeSeconds, markerAnimationDuration );
4354 const int movieFrame = static_cast< int >( std::floor( markerAnimationProgressSeconds * mFrameRateFps ) );
4355
4356 bool cached = false;
4357 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking, 96, movieFrame );
4358}
4359
@ 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.
@ CanCalculateMaskGeometryPerFeature
If present, indicates that mask geometry can safely be calculated per feature for the symbol layer....
ScaleMethod
Scale methods.
Definition qgis.h:588
@ ScaleDiameter
Calculate scale by the diameter.
@ ScaleArea
Calculate scale by the area.
QFlags< SymbolLayerFlag > SymbolLayerFlags
Symbol layer flags.
Definition qgis.h:851
MarkerShape
Marker shapes.
Definition qgis.h:3009
@ 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.
@ AsteriskFill
A filled asterisk shape.
@ HalfArc
A line-only half arc.
@ Line
Vertical line.
@ QuarterSquare
Quarter square (top left quarter)
@ Triangle
Triangle.
@ Cross2
Rotated cross (lines only), 'x' shape.
@ Trapezoid
Trapezoid.
@ ArrowHeadFilled
Right facing filled arrow head.
@ Shield
A shape consisting of a triangle attached to a rectangle.
@ HalfSquare
Half square (left half)
@ CrossFill
Solid filled cross.
@ RoundedSquare
A square with rounded corners.
@ RightHalfTriangle
Right half of triangle.
@ ThirdCircle
One third circle (top left third)
@ ThirdArc
A line-only one third arc.
@ SquareWithCorners
A square with diagonal corners.
@ QuarterArc
A line-only one quarter arc.
@ DiamondStar
A 4-sided star.
@ Cross
Cross (lines only)
@ ParallelogramLeft
Parallelogram that slants left.
@ DiagonalHalfSquare
Diagonal half square (bottom left half)
VerticalAnchorPoint
Marker symbol vertical anchor points.
Definition qgis.h:778
@ Bottom
Align to bottom of symbol.
@ Center
Align to vertical center of symbol.
@ Baseline
Align to baseline of symbol, e.g. font baseline for font marker symbol layers. Treated as Bottom if n...
@ Top
Align to top of symbol.
RenderUnit
Rendering size units.
Definition qgis.h:5064
@ 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,...
@ RenderLayerTree
The render is for a layer tree display where map based properties are not available and where avoidan...
@ RenderBlocking
Render and load remote sources in the same thread to ensure rendering remote sources (svg and images)...
@ Fill
Fill symbol.
HorizontalAnchorPoint
Marker symbol horizontal anchor points.
Definition qgis.h:764
static bool parseBase64DataUrl(const QString &path, QString *mimeType=nullptr, QString *data=nullptr)
Parses a path to determine if it represents a base 64 encoded HTML data URL, and if so,...
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 std::unique_ptr< 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.
void stopFeatureRender(const QgsFeature &feature, QgsRenderContext &context) override
Called after the layer has been rendered for a particular feature.
void startFeatureRender(const QgsFeature &feature, QgsRenderContext &context) override
Called before the layer will be rendered for a particular feature.
~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.
A marker symbol layer which displays characters rendered using a font.
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.
Q_DECL_DEPRECATED void toSld(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Saves the symbol layer as SLD.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
Q_DECL_DEPRECATED 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.
void setVerticalAnchorPoint(Qgis::VerticalAnchorPoint v)
Sets the vertical anchor point for positioning the symbol.
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)
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.
bool toSld(QDomDocument &doc, QDomElement &element, QgsSldExportContext &context) const override
Saves the symbol layer as SLD.
Qgis::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
QPointF mOffset
Marker offset.
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.
Qgis::VerticalAnchorPoint mVerticalAnchorPoint
Vertical anchor point.
static QPointF _rotatedOffset(QPointF offset, double angle)
Adjusts a marker offset to account for rotation.
void setHorizontalAnchorPoint(Qgis::HorizontalAnchorPoint h)
Sets the horizontal anchor point for positioning the symbol.
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.
void markerOffset(QgsSymbolRenderContext &context, double &offsetX, double &offsetY) const
Calculates the required marker offset, including both the symbol offset and any displacement required...
Qgis::HorizontalAnchorPoint mHorizontalAnchorPoint
Horizontal 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
static void drawPicture(QPainter *painter, const QPointF &point, const QPicture &picture)
Draws a picture onto a painter, correctly applying workarounds to avoid issues with incorrect scaling...
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.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsRasterMarkerSymbolLayer from an SLD XML element.
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.
Q_DECL_DEPRECATED void writeSldMarker(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Writes the symbol layer definition as a SLD XML element.
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.
A container for the context for various read/write operations on 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.
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 a 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.
Q_DECL_DEPRECATED void toSld(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Saves the symbol layer as SLD.
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
Q_DECL_DEPRECATED 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.
Holds SLD export options and other information related to SLD export of a QGIS layer style.
void setExtraProperties(const QVariantMap &properties)
Sets the open ended set of properties that can drive/inform the SLD encoding.
QVariantMap extraProperties() const
Returns the open ended set of properties that can drive/inform the SLD encoding.
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.
A marker symbol layer which renders an SVG graphic.
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.
Q_DECL_DEPRECATED void toSld(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Saves the symbol layer as SLD.
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.
Q_DECL_DEPRECATED 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 Q_DECL_DEPRECATED 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 Q_DECL_DEPRECATED void externalMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &path, const QString &format, int *markIndex=nullptr, const QColor &color=QColor(), double size=-1)
Exports a marker to an SLD definition.
static bool wellKnownMarkerFromSld(QDomElement &element, QString &name, QColor &color, QColor &strokeColor, Qt::PenStyle &strokeStyle, double &strokeWidth, double &size)
Extracts properties from an SLD marker definition.
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 Q_DECL_DEPRECATED void wellKnownMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &name, const QColor &color, const QColor &strokeColor, Qt::PenStyle strokeStyle, double strokeWidth=-1, double size=-1)
Exports a marker to SLD.
static QString encodeScaleMethod(Qgis::ScaleMethod scaleMethod)
Encodes a symbol scale method to a string.
static Qt::PenStyle decodePenStyle(const QString &str)
static Q_DECL_DEPRECATED void createRotationElement(QDomDocument &doc, QDomElement &element, const QString &rotationFunc)
Creates SLD rotation element.
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.
Abstract base class for symbol layers.
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.
bool installMasks(QgsRenderContext &context, bool recursive, const QRectF &rect=QRectF())
When rendering, install masks on context painter.
void removeMasks(QgsRenderContext &context, bool recursive)
When rendering, remove previously installed masks from context painter if recursive is true masks are...
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 Q_DECL_DEPRECATED void toSld(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const
Saves the symbol layer as SLD.
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.
Encapsulates the context in which a symbol is being rendered.
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.
bool forceVectorRendering() const
Returns true if symbol must be rendered using vector methods, and optimisations like pre-rendered ima...
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:231
Qgis::SymbolType type() const
Returns the symbol's type.
Definition qgssymbol.h:294
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:6392
QMap< QString, QString > QgsStringMap
Definition qgis.h:6906
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
#define DEG2RAD(x)
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:41
#define QgsDebugError(str)
Definition qgslogger.h:40
#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