QGIS API Documentation 3.41.0-Master (af5edcb665c)
Loading...
Searching...
No Matches
qgsmarkersymbollayer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmarkersymbollayer.cpp
3 ---------------------
4 begin : November 2009
5 copyright : (C) 2009 by Martin Dobias
6 email : wonder dot sk at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17#include "qgssymbollayerutils.h"
18
19#include "qgsdxfexport.h"
20#include "qgsdxfpaintdevice.h"
21#include "qgsfontutils.h"
22#include "qgsimagecache.h"
23#include "qgsimageoperation.h"
24#include "qgsrendercontext.h"
25#include "qgslogger.h"
26#include "qgssvgcache.h"
27#include "qgsunittypes.h"
28#include "qgssymbol.h"
29#include "qgsfillsymbol.h"
30#include "qgsfontmanager.h"
31#include "qgscolorutils.h"
32#include "qgspainting.h"
33
34#include <QPainter>
35#include <QSvgRenderer>
36#include <QFileInfo>
37#include <QDir>
38#include <QDomDocument>
39#include <QDomElement>
40#include <QUrlQuery>
41
42#include <cmath>
43
44static constexpr int MAX_FONT_CHARACTER_SIZE_IN_PIXELS = 500;
45
47
48
49//
50// QgsSimpleMarkerSymbolLayerBase
51//
52
96
107
109
111{
112 switch ( shape )
113 {
144 return true;
145
153 return false;
154 }
155 return true;
156}
157
159{
160 const bool hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation
162 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
163
164 // use either QPolygonF or QPainterPath for drawing
165 if ( !prepareMarkerShape( mShape ) ) // drawing as a polygon
166 {
167 prepareMarkerPath( mShape ); // drawing as a painter path
168 }
169
170 QTransform transform;
171
172 // scale the shape (if the size is not going to be modified)
173 if ( !hasDataDefinedSize )
174 {
175 double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
177 {
178 // rendering for symbol previews -- an size in meters in map units can't be calculated, so treat the size as millimeters
179 // and clamp it to a reasonable range. It's the best we can do in this situation!
180 scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
181 }
182
183 const double half = scaledSize / 2.0;
184 transform.scale( half, half );
185 }
186
187 // rotate if the rotation is not going to be changed during the rendering
188 if ( !hasDataDefinedRotation && !qgsDoubleNear( mAngle, 0.0 ) )
189 {
190 transform.rotate( mAngle );
191 }
192
193 if ( !mPolygon.isEmpty() )
194 mPolygon = transform.map( mPolygon );
195 else
196 mPath = transform.map( mPath );
197
199}
200
202{
203 Q_UNUSED( context )
204}
205
207{
208 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
209 //of the rendered point!
210
211 QPainter *p = context.renderContext().painter();
212 if ( !p )
213 {
214 return;
215 }
216
217 bool hasDataDefinedSize = false;
218 const double scaledSize = calculateSize( context, hasDataDefinedSize );
219
220 bool hasDataDefinedRotation = false;
221 QPointF offset;
222 double angle = 0;
223 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
224
225 //data defined shape?
226 bool createdNewPath = false;
227 bool ok = true;
228 Qgis::MarkerShape symbol = mShape;
230 {
231 context.setOriginalValueVariable( encodeShape( symbol ) );
233 if ( !QgsVariantUtils::isNull( exprVal ) )
234 {
235 const Qgis::MarkerShape decoded = decodeShape( exprVal.toString(), &ok );
236 if ( ok )
237 {
238 symbol = decoded;
239
240 if ( !prepareMarkerShape( symbol ) ) // drawing as a polygon
241 {
242 prepareMarkerPath( symbol ); // drawing as a painter path
243 }
244 createdNewPath = true;
245 }
246 }
247 else
248 {
249 symbol = mShape;
250 }
251 }
252
253 QTransform transform;
254
255 // move to the desired position
256 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
257
258 // resize if necessary
259 if ( hasDataDefinedSize || createdNewPath )
260 {
261 double s = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
263 {
264 // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
265 // and clamp it to a reasonable range. It's the best we can do in this situation!
266 s = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
267 }
268 const double half = s / 2.0;
269 transform.scale( half, half );
270 }
271
272 if ( !qgsDoubleNear( angle, 0.0 ) && ( hasDataDefinedRotation || createdNewPath ) )
273 {
274 transform.rotate( angle );
275 }
276
277 //need to pass: symbol, polygon, path
278
279 QPolygonF polygon;
280 QPainterPath path;
281 if ( !mPolygon.isEmpty() )
282 {
283 polygon = transform.map( mPolygon );
284 }
285 else
286 {
287 path = transform.map( mPath );
288 }
289 draw( context, symbol, polygon, path );
290}
291
293{
294 bool hasDataDefinedSize = false;
295 double scaledSize = calculateSize( context, hasDataDefinedSize );
296
297 bool hasDataDefinedRotation = false;
298 QPointF offset;
299 double angle = 0;
300 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
301
302 scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
303
304 QTransform transform;
305
306 // move to the desired position
307 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
308
309 if ( !qgsDoubleNear( angle, 0.0 ) )
310 transform.rotate( angle );
311
312 return transform.mapRect( QRectF( -scaledSize / 2.0,
313 -scaledSize / 2.0,
314 scaledSize,
315 scaledSize ) );
316}
317
319{
320 if ( ok )
321 *ok = true;
322 const QString cleaned = name.toLower().trimmed();
323
324 if ( cleaned == QLatin1String( "square" ) || cleaned == QLatin1String( "rectangle" ) )
326 else if ( cleaned == QLatin1String( "trapezoid" ) )
328 else if ( cleaned == QLatin1String( "parallelogram_right" ) )
330 else if ( cleaned == QLatin1String( "parallelogram_left" ) )
332 else if ( cleaned == QLatin1String( "square_with_corners" ) )
334 else if ( cleaned == QLatin1String( "rounded_square" ) )
336 else if ( cleaned == QLatin1String( "diamond" ) )
338 else if ( cleaned == QLatin1String( "shield" ) )
340 else if ( cleaned == QLatin1String( "pentagon" ) )
342 else if ( cleaned == QLatin1String( "hexagon" ) )
344 else if ( cleaned == QLatin1String( "octagon" ) )
346 else if ( cleaned == QLatin1String( "decagon" ) )
348 else if ( cleaned == QLatin1String( "triangle" ) )
350 else if ( cleaned == QLatin1String( "equilateral_triangle" ) )
352 else if ( cleaned == QLatin1String( "star_diamond" ) )
354 else if ( cleaned == QLatin1String( "star" ) || cleaned == QLatin1String( "regular_star" ) )
356 else if ( cleaned == QLatin1String( "heart" ) )
358 else if ( cleaned == QLatin1String( "arrow" ) )
360 else if ( cleaned == QLatin1String( "circle" ) )
362 else if ( cleaned == QLatin1String( "cross" ) )
364 else if ( cleaned == QLatin1String( "cross_fill" ) )
366 else if ( cleaned == QLatin1String( "cross2" ) || cleaned == QLatin1String( "x" ) )
368 else if ( cleaned == QLatin1String( "line" ) )
370 else if ( cleaned == QLatin1String( "arrowhead" ) )
372 else if ( cleaned == QLatin1String( "filled_arrowhead" ) )
374 else if ( cleaned == QLatin1String( "semi_circle" ) )
376 else if ( cleaned == QLatin1String( "third_circle" ) )
378 else if ( cleaned == QLatin1String( "quarter_circle" ) )
380 else if ( cleaned == QLatin1String( "quarter_square" ) )
382 else if ( cleaned == QLatin1String( "half_square" ) )
384 else if ( cleaned == QLatin1String( "diagonal_half_square" ) )
386 else if ( cleaned == QLatin1String( "right_half_triangle" ) )
388 else if ( cleaned == QLatin1String( "left_half_triangle" ) )
390 else if ( cleaned == QLatin1String( "asterisk_fill" ) )
392 else if ( cleaned == QLatin1String( "half_arc" ) )
394 else if ( cleaned == QLatin1String( "third_arc" ) )
396 else if ( cleaned == QLatin1String( "quarter_arc" ) )
398
399 if ( ok )
400 *ok = false;
402}
403
405{
406 switch ( shape )
407 {
409 return QStringLiteral( "square" );
411 return QStringLiteral( "quarter_square" );
413 return QStringLiteral( "half_square" );
415 return QStringLiteral( "diagonal_half_square" );
417 return QStringLiteral( "parallelogram_right" );
419 return QStringLiteral( "parallelogram_left" );
421 return QStringLiteral( "trapezoid" );
423 return QStringLiteral( "shield" );
425 return QStringLiteral( "diamond" );
427 return QStringLiteral( "pentagon" );
429 return QStringLiteral( "hexagon" );
431 return QStringLiteral( "octagon" );
433 return QStringLiteral( "decagon" );
435 return QStringLiteral( "square_with_corners" );
437 return QStringLiteral( "rounded_square" );
439 return QStringLiteral( "triangle" );
441 return QStringLiteral( "equilateral_triangle" );
443 return QStringLiteral( "left_half_triangle" );
445 return QStringLiteral( "right_half_triangle" );
447 return QStringLiteral( "star_diamond" );
449 return QStringLiteral( "star" );
451 return QStringLiteral( "heart" );
453 return QStringLiteral( "arrow" );
455 return QStringLiteral( "filled_arrowhead" );
457 return QStringLiteral( "cross_fill" );
459 return QStringLiteral( "circle" );
461 return QStringLiteral( "cross" );
463 return QStringLiteral( "cross2" );
465 return QStringLiteral( "line" );
467 return QStringLiteral( "arrowhead" );
469 return QStringLiteral( "semi_circle" );
471 return QStringLiteral( "third_circle" );
473 return QStringLiteral( "quarter_circle" );
475 return QStringLiteral( "asterisk_fill" );
477 return QStringLiteral( "half_arc" );
479 return QStringLiteral( "third_arc" );
481 return QStringLiteral( "quarter_arc" );
482 }
483 return QString();
484}
485
490
492{
493 polygon.clear();
494
495 switch ( shape )
496 {
498 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 1, 1 ) ) );
499 return true;
500
502 {
503 static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 0.6072;
504
505 polygon << QPointF( - VERTEX_OFFSET_FROM_ORIGIN, 1 )
506 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, 1 )
507 << QPointF( 1, VERTEX_OFFSET_FROM_ORIGIN )
508 << QPointF( 1, -VERTEX_OFFSET_FROM_ORIGIN )
509 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, -1 )
510 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, -1 )
511 << QPointF( -1, -VERTEX_OFFSET_FROM_ORIGIN )
512 << QPointF( -1, VERTEX_OFFSET_FROM_ORIGIN )
513 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 );
514 return true;
515 }
516
518 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 0 ) ) );
519 return true;
520
522 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 1 ) ) );
523 return true;
524
526 polygon << QPointF( -1, -1 ) << QPointF( 1, 1 ) << QPointF( -1, 1 ) << QPointF( -1, -1 );
527 return true;
528
530 polygon << QPointF( 0.5, -0.5 )
531 << QPointF( 1, 0.5 )
532 << QPointF( -1, 0.5 )
533 << QPointF( -0.5, -0.5 )
534 << QPointF( 0.5, -0.5 );
535 return true;
536
538 polygon << QPointF( 0.5, 0.5 )
539 << QPointF( 1, -0.5 )
540 << QPointF( -0.5, -0.5 )
541 << QPointF( -1, 0.5 )
542 << QPointF( 0.5, 0.5 );
543 return true;
544
546 polygon << QPointF( 1, 0.5 )
547 << QPointF( 0.5, -0.5 )
548 << QPointF( -1, -0.5 )
549 << QPointF( -0.5, 0.5 )
550 << QPointF( 1, 0.5 );
551 return true;
552
554 polygon << QPointF( -1, 0 ) << QPointF( 0, 1 )
555 << QPointF( 1, 0 ) << QPointF( 0, -1 ) << QPointF( -1, 0 );
556 return true;
557
559 polygon << QPointF( 1, 0.5 )
560 << QPointF( 1, -1 )
561 << QPointF( -1, -1 )
562 << QPointF( -1, 0.5 )
563 << QPointF( 0, 1 )
564 << QPointF( 1, 0.5 );
565 return true;
566
568 /* angular-representation of hardcoded values used
569 polygon << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288.0 ) ) )
570 << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) )
571 << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) )
572 << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) )
573 << QPointF( 0, -1 ); */
574 polygon << QPointF( -0.9511, -0.3090 )
575 << QPointF( -0.5878, 0.8090 )
576 << QPointF( 0.5878, 0.8090 )
577 << QPointF( 0.9511, -0.3090 )
578 << QPointF( 0, -1 )
579 << QPointF( -0.9511, -0.3090 );
580 return true;
581
583 /* angular-representation of hardcoded values used
584 polygon << QPointF( std::sin( DEG2RAD( 300.0 ) ), - std::cos( DEG2RAD( 300.0 ) ) )
585 << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
586 << QPointF( std::sin( DEG2RAD( 180.0 ) ), - std::cos( DEG2RAD( 180.0 ) ) )
587 << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
588 << QPointF( std::sin( DEG2RAD( 60.0 ) ), - std::cos( DEG2RAD( 60.0 ) ) )
589 << QPointF( 0, -1 ); */
590 polygon << QPointF( -0.8660, -0.5 )
591 << QPointF( -0.8660, 0.5 )
592 << QPointF( 0, 1 )
593 << QPointF( 0.8660, 0.5 )
594 << QPointF( 0.8660, -0.5 )
595 << QPointF( 0, -1 )
596 << QPointF( -0.8660, -0.5 );
597 return true;
598
600 {
601 static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 1.0 / ( 1 + M_SQRT2 );
602
603 polygon << QPointF( - VERTEX_OFFSET_FROM_ORIGIN, 1 )
604 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, 1 )
605 << QPointF( 1, VERTEX_OFFSET_FROM_ORIGIN )
606 << QPointF( 1, -VERTEX_OFFSET_FROM_ORIGIN )
607 << QPointF( VERTEX_OFFSET_FROM_ORIGIN, -1 )
608 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, -1 )
609 << QPointF( -1, -VERTEX_OFFSET_FROM_ORIGIN )
610 << QPointF( -1, VERTEX_OFFSET_FROM_ORIGIN )
611 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 );
612 return true;
613 }
614
616 {
617
618 polygon << QPointF( 0.587785252, 0.809016994 )
619 << QPointF( 0.951056516, 0.309016994 )
620 << QPointF( 0.951056516, -0.309016994 )
621 << QPointF( 0.587785252, -0.809016994 )
622 << QPointF( 0, -1 )
623 << QPointF( -0.587785252, -0.809016994 )
624 << QPointF( -0.951056516, -0.309016994 )
625 << QPointF( -0.951056516, 0.309016994 )
626 << QPointF( -0.587785252, 0.809016994 )
627 << QPointF( 0, 1 )
628 << QPointF( 0.587785252, 0.809016994 );
629 return true;
630 }
631
633 polygon << QPointF( -1, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
634 return true;
635
637 /* angular-representation of hardcoded values used
638 polygon << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
639 << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
640 << QPointF( 0, -1 ); */
641 polygon << QPointF( -0.8660, 0.5 )
642 << QPointF( 0.8660, 0.5 )
643 << QPointF( 0, -1 )
644 << QPointF( -0.8660, 0.5 );
645 return true;
646
648 polygon << QPointF( 0, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( 0, 1 );
649 return true;
650
652 polygon << QPointF( -1, 1 ) << QPointF( 0, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
653 return true;
654
656 {
657 const double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
658
659 polygon << QPointF( inner_r * std::sin( DEG2RAD( 315.0 ) ), - inner_r * std::cos( DEG2RAD( 315.0 ) ) )
660 << QPointF( std::sin( DEG2RAD( 270 ) ), - std::cos( DEG2RAD( 270 ) ) )
661 << QPointF( inner_r * std::sin( DEG2RAD( 225.0 ) ), - inner_r * std::cos( DEG2RAD( 225.0 ) ) )
662 << QPointF( std::sin( DEG2RAD( 180 ) ), - std::cos( DEG2RAD( 180 ) ) )
663 << QPointF( inner_r * std::sin( DEG2RAD( 135.0 ) ), - inner_r * std::cos( DEG2RAD( 135.0 ) ) )
664 << QPointF( std::sin( DEG2RAD( 90 ) ), - std::cos( DEG2RAD( 90 ) ) )
665 << QPointF( inner_r * std::sin( DEG2RAD( 45.0 ) ), - inner_r * std::cos( DEG2RAD( 45.0 ) ) )
666 << QPointF( std::sin( DEG2RAD( 0 ) ), - std::cos( DEG2RAD( 0 ) ) );
667 return true;
668 }
669
671 {
672 const double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
673
674 polygon << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ) // 324
675 << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288 ) ) ) // 288
676 << QPointF( inner_r * std::sin( DEG2RAD( 252.0 ) ), - inner_r * std::cos( DEG2RAD( 252.0 ) ) ) // 252
677 << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) ) // 216
678 << QPointF( 0, inner_r ) // 180
679 << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) ) // 144
680 << QPointF( inner_r * std::sin( DEG2RAD( 108.0 ) ), - inner_r * std::cos( DEG2RAD( 108.0 ) ) ) // 108
681 << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) ) // 72
682 << QPointF( inner_r * std::sin( DEG2RAD( 36.0 ) ), - inner_r * std::cos( DEG2RAD( 36.0 ) ) ) // 36
683 << QPointF( 0, -1 )
684 << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ); // 324; // 0
685 return true;
686 }
687
689 polygon << QPointF( 0, -1 )
690 << QPointF( 0.5, -0.5 )
691 << QPointF( 0.25, -0.5 )
692 << QPointF( 0.25, 1 )
693 << QPointF( -0.25, 1 )
694 << QPointF( -0.25, -0.5 )
695 << QPointF( -0.5, -0.5 )
696 << QPointF( 0, -1 );
697 return true;
698
700 polygon << QPointF( 0, 0 ) << QPointF( -1, 1 ) << QPointF( -1, -1 ) << QPointF( 0, 0 );
701 return true;
702
704 polygon << QPointF( -1, -0.2 )
705 << QPointF( -1, -0.2 )
706 << QPointF( -1, 0.2 )
707 << QPointF( -0.2, 0.2 )
708 << QPointF( -0.2, 1 )
709 << QPointF( 0.2, 1 )
710 << QPointF( 0.2, 0.2 )
711 << QPointF( 1, 0.2 )
712 << QPointF( 1, -0.2 )
713 << QPointF( 0.2, -0.2 )
714 << QPointF( 0.2, -1 )
715 << QPointF( -0.2, -1 )
716 << QPointF( -0.2, -0.2 )
717 << QPointF( -1, -0.2 );
718 return true;
719
721 {
722 static constexpr double THICKNESS = 0.3;
723 static constexpr double HALF_THICKNESS = THICKNESS / 2.0;
724 static constexpr double INTERSECTION_POINT = THICKNESS / M_SQRT2;
725 static constexpr double DIAGONAL1 = M_SQRT1_2 - INTERSECTION_POINT * 0.5;
726 static constexpr double DIAGONAL2 = M_SQRT1_2 + INTERSECTION_POINT * 0.5;
727
728 polygon << QPointF( -HALF_THICKNESS, -1 )
729 << QPointF( HALF_THICKNESS, -1 )
730 << QPointF( HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
731 << QPointF( DIAGONAL1, -DIAGONAL2 )
732 << QPointF( DIAGONAL2, -DIAGONAL1 )
733 << QPointF( HALF_THICKNESS + INTERSECTION_POINT, -HALF_THICKNESS )
734 << QPointF( 1, -HALF_THICKNESS )
735 << QPointF( 1, HALF_THICKNESS )
736 << QPointF( HALF_THICKNESS + INTERSECTION_POINT, HALF_THICKNESS )
737 << QPointF( DIAGONAL2, DIAGONAL1 )
738 << QPointF( DIAGONAL1, DIAGONAL2 )
739 << QPointF( HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
740 << QPointF( HALF_THICKNESS, 1 )
741 << QPointF( -HALF_THICKNESS, 1 )
742 << QPointF( -HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
743 << QPointF( -DIAGONAL1, DIAGONAL2 )
744 << QPointF( -DIAGONAL2, DIAGONAL1 )
745 << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, HALF_THICKNESS )
746 << QPointF( -1, HALF_THICKNESS )
747 << QPointF( -1, -HALF_THICKNESS )
748 << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, -HALF_THICKNESS )
749 << QPointF( -DIAGONAL2, -DIAGONAL1 )
750 << QPointF( -DIAGONAL1, -DIAGONAL2 )
751 << QPointF( -HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
752 << QPointF( -HALF_THICKNESS, -1 );
753 return true;
754 }
755
769 return false;
770 }
771
772 return false;
773}
774
776{
777 mPath = QPainterPath();
778
779 switch ( symbol )
780 {
782
783 mPath.addEllipse( QRectF( -1, -1, 2, 2 ) ); // x,y,w,h
784 return true;
785
787 mPath.moveTo( -1, -1 );
788 mPath.addRoundedRect( -1, -1, 2, 2, 0.25, 0.25 );
789 return true;
790
792 mPath.arcTo( -1, -1, 2, 2, 0, 180 );
793 mPath.lineTo( 0, 0 );
794 return true;
795
797 mPath.arcTo( -1, -1, 2, 2, 90, 120 );
798 mPath.lineTo( 0, 0 );
799 return true;
800
802 mPath.arcTo( -1, -1, 2, 2, 90, 90 );
803 mPath.lineTo( 0, 0 );
804 return true;
805
807 mPath.moveTo( 1, 0 );
808 mPath.arcTo( -1, -1, 2, 2, 0, 180 );
809 return true;
810
812 mPath.moveTo( 0, -1 );
813 mPath.arcTo( -1, -1, 2, 2, 90, 120 );
814 return true;
815
817 mPath.moveTo( 0, -1 );
818 mPath.arcTo( -1, -1, 2, 2, 90, 90 );
819 return true;
820
822 mPath.moveTo( -1, 0 );
823 mPath.lineTo( 1, 0 ); // horizontal
824 mPath.moveTo( 0, -1 );
825 mPath.lineTo( 0, 1 ); // vertical
826 return true;
827
829 mPath.moveTo( -1, -1 );
830 mPath.lineTo( 1, 1 );
831 mPath.moveTo( 1, -1 );
832 mPath.lineTo( -1, 1 );
833 return true;
834
836 mPath.moveTo( 0, -1 );
837 mPath.lineTo( 0, 1 ); // vertical line
838 return true;
839
841 mPath.moveTo( -1, -1 );
842 mPath.lineTo( 0, 0 );
843 mPath.lineTo( -1, 1 );
844 return true;
845
847 mPath.moveTo( 0, 0.75 );
848 mPath.arcTo( 0, -1, 1, 1, -45, 210 );
849 mPath.arcTo( -1, -1, 1, 1, 15, 210 );
850 mPath.lineTo( 0, 0.75 );
851 return true;
852
877 return false;
878 }
879 return false;
880}
881
882double QgsSimpleMarkerSymbolLayerBase::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
883{
884 double scaledSize = mSize;
885
887 bool ok = true;
888 if ( hasDataDefinedSize )
889 {
892 mSize, &ok );
893 }
894
895 if ( hasDataDefinedSize && ok )
896 {
897 switch ( mScaleMethod )
898 {
900 scaledSize = std::sqrt( scaledSize );
901 break;
903 break;
904 }
905 }
906
907 return scaledSize;
908}
909
910void QgsSimpleMarkerSymbolLayerBase::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle ) const
911{
912 //offset
913 double offsetX = 0;
914 double offsetY = 0;
915 markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
916 offset = QPointF( offsetX, offsetY );
917
918 hasDataDefinedRotation = false;
919 //angle
920 bool ok = true;
923 {
926
927 // If the expression evaluation was not successful, fallback to static value
928 if ( !ok )
930
931 hasDataDefinedRotation = true;
932 }
933
934 hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || hasDataDefinedRotation;
935
936 if ( hasDataDefinedRotation )
937 {
938 // For non-point markers, "dataDefinedRotation" means following the
939 // shape (shape-data defined). For them, "field-data defined" does
940 // not work at all. TODO: if "field-data defined" ever gets implemented
941 // we'll need a way to distinguish here between the two, possibly
942 // using another flag in renderHints()
943 const QgsFeature *f = context.feature();
944 if ( f )
945 {
946 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
947 {
948 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
949 angle += m2p.mapRotation();
950 }
951 }
952 }
953
954 if ( angle )
956}
957
958
959//
960// QgsSimpleMarkerSymbolLayer
961//
962
963QgsSimpleMarkerSymbolLayer::QgsSimpleMarkerSymbolLayer( Qgis::MarkerShape shape, double size, double angle, Qgis::ScaleMethod scaleMethod, const QColor &color, const QColor &strokeColor, Qt::PenJoinStyle penJoinStyle )
964 : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
965 , mStrokeColor( strokeColor )
966 , mPenJoinStyle( penJoinStyle )
967{
968 mColor = color;
969}
970
972
974{
982
983 if ( props.contains( QStringLiteral( "name" ) ) )
984 {
985 shape = decodeShape( props[QStringLiteral( "name" )].toString() );
986 }
987 if ( props.contains( QStringLiteral( "color" ) ) )
988 color = QgsColorUtils::colorFromString( props[QStringLiteral( "color" )].toString() );
989 if ( props.contains( QStringLiteral( "color_border" ) ) )
990 {
991 //pre 2.5 projects use "color_border"
992 strokeColor = QgsColorUtils::colorFromString( props[QStringLiteral( "color_border" )].toString() );
993 }
994 else if ( props.contains( QStringLiteral( "outline_color" ) ) )
995 {
996 strokeColor = QgsColorUtils::colorFromString( props[QStringLiteral( "outline_color" )].toString() );
997 }
998 else if ( props.contains( QStringLiteral( "line_color" ) ) )
999 {
1000 strokeColor = QgsColorUtils::colorFromString( props[QStringLiteral( "line_color" )].toString() );
1001 }
1002 if ( props.contains( QStringLiteral( "joinstyle" ) ) )
1003 {
1004 penJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() );
1005 }
1006 if ( props.contains( QStringLiteral( "size" ) ) )
1007 size = props[QStringLiteral( "size" )].toDouble();
1008 if ( props.contains( QStringLiteral( "angle" ) ) )
1009 angle = props[QStringLiteral( "angle" )].toDouble();
1010 if ( props.contains( QStringLiteral( "scale_method" ) ) )
1011 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1012
1014 if ( props.contains( QStringLiteral( "offset" ) ) )
1015 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1016 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1017 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1018 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1019 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1020 if ( props.contains( QStringLiteral( "size_unit" ) ) )
1021 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1022 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1023 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1024
1025 if ( props.contains( QStringLiteral( "outline_style" ) ) )
1026 {
1027 m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "outline_style" )].toString() ) );
1028 }
1029 else if ( props.contains( QStringLiteral( "line_style" ) ) )
1030 {
1031 m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "line_style" )].toString() ) );
1032 }
1033 if ( props.contains( QStringLiteral( "outline_width" ) ) )
1034 {
1035 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
1036 }
1037 else if ( props.contains( QStringLiteral( "line_width" ) ) )
1038 {
1039 m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
1040 }
1041 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
1042 {
1043 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
1044 }
1045 if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
1046 {
1047 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
1048 }
1049 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
1050 {
1051 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
1052 }
1053
1054 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1055 {
1056 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1057 }
1058 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1059 {
1060 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1061 }
1062
1063 if ( props.contains( QStringLiteral( "cap_style" ) ) )
1064 {
1065 m->setPenCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( props[QStringLiteral( "cap_style" )].toString() ) );
1066 }
1067
1069
1070 return m;
1071}
1072
1073
1075{
1076 return QStringLiteral( "SimpleMarker" );
1077}
1078
1083
1085{
1087
1088 QColor brushColor = mColor;
1089 QColor penColor = mStrokeColor;
1090
1091 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
1092 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
1093
1094 mBrush = QBrush( brushColor );
1095 mPen = QPen( penColor );
1096 mPen.setStyle( mStrokeStyle );
1097 mPen.setCapStyle( mPenCapStyle );
1098 mPen.setJoinStyle( mPenJoinStyle );
1100
1101 QColor selBrushColor = context.renderContext().selectionColor();
1102 QColor selPenColor = selBrushColor == mColor ? selBrushColor : mStrokeColor;
1103 if ( context.opacity() < 1 && !SELECTION_IS_OPAQUE )
1104 {
1105 selBrushColor.setAlphaF( context.opacity() );
1106 selPenColor.setAlphaF( context.opacity() );
1107 }
1108 mSelBrush = QBrush( selBrushColor );
1109 mSelPen = QPen( selPenColor );
1110 mSelPen.setStyle( mStrokeStyle );
1112
1114 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
1115
1116 // use caching only when:
1117 // - size, rotation, shape, color, stroke color is not data-defined
1118 // - drawing to screen (not printer)
1119 mUsingCache = !hasDataDefinedRotation && !hasDataDefinedSize && !context.forceVectorRendering()
1123
1124 if ( mUsingCache )
1125 mCachedOpacity = context.opacity();
1126
1127 if ( !shapeIsFilled( mShape ) )
1128 {
1129 // some markers can't be drawn as a polygon (circle, cross)
1130 // For these set the selected stroke color to the selected color
1131 mSelPen.setColor( selBrushColor );
1132 }
1133
1134
1135 if ( mUsingCache )
1136 {
1137 if ( !prepareCache( context ) )
1138 {
1139 mUsingCache = false;
1140 }
1141 }
1142 else
1143 {
1144 mCache = QImage();
1145 mSelCache = QImage();
1146 }
1147}
1148
1149
1151{
1152 double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
1153 const double deviceRatio = context.renderContext().devicePixelRatio();
1155 {
1156 // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
1157 // and clamp it to a reasonable range. It's the best we can do in this situation!
1158 scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
1159 }
1160
1161 // take into account angle (which is not data-defined otherwise cache wouldn't be used)
1162 if ( !qgsDoubleNear( mAngle, 0.0 ) )
1163 {
1164 scaledSize = ( std::abs( std::sin( mAngle * M_PI / 180 ) ) + std::abs( std::cos( mAngle * M_PI / 180 ) ) ) * scaledSize;
1165 }
1166 // calculate necessary image size for the cache
1167 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
1168 const int imageSize = ( static_cast< int >( scaledSize ) + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width
1169 const double center = imageSize / 2.0;
1170 if ( imageSize * deviceRatio > MAXIMUM_CACHE_WIDTH )
1171 {
1172 return false;
1173 }
1174
1175 mCache = QImage( QSize( imageSize * deviceRatio,
1176 imageSize * deviceRatio ), QImage::Format_ARGB32_Premultiplied );
1177 mCache.setDevicePixelRatio( context.renderContext().devicePixelRatio() );
1178 mCache.setDotsPerMeterX( std::round( context.renderContext().scaleFactor() * 1000 ) );
1179 mCache.setDotsPerMeterY( std::round( context.renderContext().scaleFactor() * 1000 ) );
1180 mCache.fill( 0 );
1181
1182 const bool needsBrush = shapeIsFilled( mShape );
1183
1184 QPainter p;
1185 p.begin( &mCache );
1186 p.setRenderHint( QPainter::Antialiasing );
1187 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1188 p.setPen( mPen );
1189 p.translate( QPointF( center, center ) );
1190 drawMarker( &p, context );
1191 p.end();
1192
1193 // Construct the selected version of the Cache
1194
1195 const QColor selColor = context.renderContext().selectionColor();
1196
1197 mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
1198 mSelCache.fill( 0 );
1199
1200 p.begin( &mSelCache );
1201 p.setRenderHint( QPainter::Antialiasing );
1202 p.setBrush( needsBrush ? mSelBrush : Qt::NoBrush );
1203 p.setPen( mSelPen );
1204 p.translate( QPointF( center, center ) );
1205 drawMarker( &p, context );
1206 p.end();
1207
1208 // Check that the selected version is different. If not, then re-render,
1209 // filling the background with the selection color and using the normal
1210 // colors for the symbol .. could be ugly!
1211
1212 if ( mSelCache == mCache )
1213 {
1214 p.begin( &mSelCache );
1215 p.setRenderHint( QPainter::Antialiasing );
1216 p.fillRect( 0, 0, imageSize, imageSize, selColor );
1217 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1218 p.setPen( mPen );
1219 p.translate( QPointF( center, center ) );
1220 drawMarker( &p, context );
1221 p.end();
1222 }
1223
1224 return true;
1225}
1226
1227void QgsSimpleMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
1228{
1229 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1230 //of the rendered point!
1231
1232 QPainter *p = context.renderContext().painter();
1233 if ( !p )
1234 {
1235 return;
1236 }
1237
1238 QColor brushColor = mColor;
1239 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
1240 mBrush.setColor( brushColor );
1241
1242 QColor penColor = mStrokeColor;
1243 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
1244 mPen.setColor( penColor );
1245
1246 bool ok = true;
1248 {
1251 if ( ok )
1252 {
1253 c.setAlphaF( c.alphaF() * context.opacity() );
1254 mBrush.setColor( c );
1255 }
1256 }
1258 {
1261 if ( ok )
1262 {
1263 c.setAlphaF( c.alphaF() * context.opacity() );
1264 mPen.setColor( c );
1265 mSelPen.setColor( c );
1266 }
1267 }
1269 {
1272 if ( ok )
1273 {
1276 }
1277 }
1279 {
1282 if ( ok )
1283 {
1286 }
1287 }
1289 {
1292 if ( ok )
1293 {
1294 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1295 mSelPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1296 }
1297 }
1299 {
1302 if ( ok )
1303 {
1304 mPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1305 mSelPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1306 }
1307 }
1308
1309 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
1310 if ( shapeIsFilled( shape ) )
1311 {
1312 p->setBrush( useSelectedColor ? mSelBrush : mBrush );
1313 }
1314 else
1315 {
1316 p->setBrush( Qt::NoBrush );
1317 }
1318 p->setPen( useSelectedColor ? mSelPen : mPen );
1319
1320 if ( !polygon.isEmpty() )
1321 p->drawPolygon( polygon );
1322 else
1323 p->drawPath( path );
1324}
1325
1327{
1328 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1329 //of the rendered point!
1330
1331 QPainter *p = context.renderContext().painter();
1332 if ( !p )
1333 {
1334 return;
1335 }
1336
1337 if ( mUsingCache && qgsDoubleNear( mCachedOpacity, context.opacity() ) )
1338 {
1339 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
1340 const QImage &img = useSelectedColor ? mSelCache : mCache;
1341 const double s = img.width() / img.devicePixelRatioF();
1342
1343 bool hasDataDefinedSize = false;
1344 const double scaledSize = calculateSize( context, hasDataDefinedSize );
1345
1346 bool hasDataDefinedRotation = false;
1347 QPointF offset;
1348 double angle = 0;
1349 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
1350
1351 p->drawImage( QRectF( point.x() - s / 2.0 + offset.x(),
1352 point.y() - s / 2.0 + offset.y(),
1353 s, s ), img );
1354 }
1355 else
1356 {
1358 }
1359}
1360
1362{
1363 QVariantMap map;
1364 map[QStringLiteral( "name" )] = encodeShape( mShape );
1365 map[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mColor );
1366 map[QStringLiteral( "outline_color" )] = QgsColorUtils::colorToString( mStrokeColor );
1367 map[QStringLiteral( "size" )] = QString::number( mSize );
1368 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1369 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1370 map[QStringLiteral( "angle" )] = QString::number( mAngle );
1371 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1372 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1373 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1374 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1375 map[QStringLiteral( "outline_style" )] = QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle );
1376 map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
1377 map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
1378 map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
1379 map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
1380 map[QStringLiteral( "cap_style" )] = QgsSymbolLayerUtils::encodePenCapStyle( mPenCapStyle );
1381 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1382 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1383 return map;
1384}
1385
1405
1406void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1407{
1408 // <Graphic>
1409 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
1410 element.appendChild( graphicElem );
1411
1413 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
1415
1416 // <Rotation>
1417 QString angleFunc;
1418
1420 {
1422 }
1423 else
1424 {
1425 bool ok;
1426 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
1427 if ( !ok )
1428 {
1429 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
1430 }
1431 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
1432 {
1433 angleFunc = QString::number( angle + mAngle );
1434 }
1435 }
1436
1437 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
1438
1439 // <Displacement>
1440 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
1442}
1443
1444QString QgsSimpleMarkerSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
1445{
1446 Q_UNUSED( mmScaleFactor )
1447 Q_UNUSED( mapUnitScaleFactor )
1448#if 0
1449 QString ogrType = "3"; //default is circle
1450 if ( mName == "square" )
1451 {
1452 ogrType = "5";
1453 }
1454 else if ( mName == "triangle" )
1455 {
1456 ogrType = "7";
1457 }
1458 else if ( mName == "star" )
1459 {
1460 ogrType = "9";
1461 }
1462 else if ( mName == "circle" )
1463 {
1464 ogrType = "3";
1465 }
1466 else if ( mName == "cross" )
1467 {
1468 ogrType = "0";
1469 }
1470 else if ( mName == "x" || mName == "cross2" )
1471 {
1472 ogrType = "1";
1473 }
1474 else if ( mName == "line" )
1475 {
1476 ogrType = "10";
1477 }
1478
1479 QString ogrString;
1480 ogrString.append( "SYMBOL(" );
1481 ogrString.append( "id:" );
1482 ogrString.append( '\"' );
1483 ogrString.append( "ogr-sym-" );
1484 ogrString.append( ogrType );
1485 ogrString.append( '\"' );
1486 ogrString.append( ",c:" );
1487 ogrString.append( mColor.name() );
1488 ogrString.append( ",o:" );
1489 ogrString.append( mStrokeColor.name() );
1490 ogrString.append( QString( ",s:%1mm" ).arg( mSize ) );
1491 ogrString.append( ')' );
1492 return ogrString;
1493#endif //0
1494
1495 QString ogrString;
1496 ogrString.append( "PEN(" );
1497 ogrString.append( "c:" );
1498 ogrString.append( mColor.name() );
1499 ogrString.append( ",w:" );
1500 ogrString.append( QString::number( mSize ) );
1501 ogrString.append( "mm" );
1502 ogrString.append( ")" );
1503 return ogrString;
1504}
1505
1507{
1508 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
1509
1510 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
1511 if ( graphicElem.isNull() )
1512 return nullptr;
1513
1514 QString name = QStringLiteral( "square" );
1515 QColor color, strokeColor;
1516 double strokeWidth, size;
1517 Qt::PenStyle strokeStyle;
1518
1520 return nullptr;
1521
1522 double angle = 0.0;
1523 QString angleFunc;
1524 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
1525 {
1526 bool ok;
1527 const double d = angleFunc.toDouble( &ok );
1528 if ( ok )
1529 angle = d;
1530 }
1531
1532 QPointF offset;
1534
1535 const Qgis::MarkerShape shape = decodeShape( name );
1536
1537 double scaleFactor = 1.0;
1538 const QString uom = element.attribute( QStringLiteral( "uom" ) );
1539 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
1540 size = size * scaleFactor;
1541 offset.setX( offset.x() * scaleFactor );
1542 offset.setY( offset.y() * scaleFactor );
1543
1545 m->setOutputUnit( sldUnitSize );
1546 m->setColor( color );
1548 m->setAngle( angle );
1549 m->setOffset( offset );
1552 return m;
1553}
1554
1556{
1557 Q_UNUSED( context )
1558
1559 if ( mPolygon.count() != 0 )
1560 {
1561 p->drawPolygon( mPolygon );
1562 }
1563 else
1564 {
1565 p->drawPath( mPath );
1566 }
1567}
1568
1569bool QgsSimpleMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
1570{
1571 //data defined size?
1572 double size = mSize;
1573
1574 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
1575
1576 //data defined size
1577 bool ok = true;
1578 if ( hasDataDefinedSize )
1579 {
1581
1582 if ( ok )
1583 {
1584 switch ( mScaleMethod )
1585 {
1587 size = std::sqrt( size );
1588 break;
1590 break;
1591 }
1592 }
1593 }
1594
1596 {
1597 size *= mmMapUnitScaleFactor;
1598 }
1599
1601 {
1603 }
1604 const double halfSize = size / 2.0;
1605
1606 //strokeWidth
1607 double strokeWidth = mStrokeWidth;
1608
1610 {
1613 }
1616 {
1618 }
1619
1620 //color
1621 QColor pc = mPen.color();
1622 QColor bc = mBrush.color();
1624 {
1627 }
1629 {
1632 }
1633
1634 //offset
1635 double offsetX = 0;
1636 double offsetY = 0;
1637 markerOffset( context, offsetX, offsetY );
1638 offsetX *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1639 offsetY *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1640
1641
1642 QPointF off( offsetX, offsetY );
1643
1644 //angle
1645 double angle = mAngle + mLineAngle;
1647 {
1650 }
1651
1654 {
1656 const QString shapeName = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::Name, context.renderContext().expressionContext(), QString(), &ok );
1657 if ( ok )
1658 {
1659 shape = decodeShape( shapeName, &ok );
1660 if ( !ok )
1661 shape = mShape;
1662 }
1663 }
1664
1665 if ( angle )
1666 off = _rotatedOffset( off, angle );
1667
1669
1670 QTransform t;
1671 t.translate( shift.x() + off.x(), shift.y() - off.y() );
1672
1673 if ( !qgsDoubleNear( angle, 0.0 ) )
1674 t.rotate( -angle );
1675
1676 QPolygonF polygon;
1677 if ( shapeToPolygon( shape, polygon ) )
1678 {
1679 t.scale( halfSize, -halfSize );
1680
1681 polygon = t.map( polygon );
1682
1684 p.reserve( polygon.size() );
1685 for ( int i = 0; i < polygon.size(); i++ )
1686 {
1687 p << QgsPoint( polygon[i] );
1688 }
1689
1690 if ( mBrush.style() != Qt::NoBrush )
1691 e.writePolygon( QgsRingSequence() << p, layerName, QStringLiteral( "SOLID" ), bc );
1692 if ( mPen.style() != Qt::NoPen )
1693 e.writePolyline( p, layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1694 }
1695 else if ( shape == Qgis::MarkerShape::Circle )
1696 {
1697 shift += QPointF( off.x(), -off.y() );
1698 if ( mBrush.style() != Qt::NoBrush )
1699 e.writeFilledCircle( layerName, bc, QgsPoint( shift ), halfSize );
1700 if ( mPen.style() != Qt::NoPen )
1701 e.writeCircle( layerName, pc, QgsPoint( shift ), halfSize, QStringLiteral( "CONTINUOUS" ), strokeWidth );
1702 }
1703 else if ( shape == Qgis::MarkerShape::Line )
1704 {
1705 const QPointF pt1 = t.map( QPointF( 0, -halfSize ) );
1706 const QPointF pt2 = t.map( QPointF( 0, halfSize ) );
1707
1708 if ( mPen.style() != Qt::NoPen )
1709 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1710 }
1711 else if ( shape == Qgis::MarkerShape::Cross )
1712 {
1713 if ( mPen.style() != Qt::NoPen )
1714 {
1715 const QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
1716 const QPointF pt2 = t.map( QPointF( halfSize, 0 ) );
1717 const QPointF pt3 = t.map( QPointF( 0, -halfSize ) );
1718 const QPointF pt4 = t.map( QPointF( 0, halfSize ) );
1719
1720 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1721 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1722 }
1723 }
1724 else if ( shape == Qgis::MarkerShape::Cross2 )
1725 {
1726 if ( mPen.style() != Qt::NoPen )
1727 {
1728 const QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
1729 const QPointF pt2 = t.map( QPointF( halfSize, halfSize ) );
1730 const QPointF pt3 = t.map( QPointF( halfSize, -halfSize ) );
1731 const QPointF pt4 = t.map( QPointF( -halfSize, halfSize ) );
1732
1733 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1734 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1735 }
1736 }
1737 else if ( shape == Qgis::MarkerShape::ArrowHead )
1738 {
1739 if ( mPen.style() != Qt::NoPen )
1740 {
1741 const QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
1742 const QPointF pt2 = t.map( QPointF( 0, 0 ) );
1743 const QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
1744
1745 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1746 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1747 }
1748 }
1749 else
1750 {
1751 QgsDebugError( QStringLiteral( "Unsupported dxf marker name %1" ).arg( encodeShape( shape ) ) );
1752 return false;
1753 }
1754
1755 return true;
1756}
1757
1758
1764
1773
1779
1788
1795
1797{
1798 QRectF symbolBounds = QgsSimpleMarkerSymbolLayerBase::bounds( point, context );
1799
1800 // need to account for stroke width
1801 double penWidth = mStrokeWidth;
1802 bool ok = true;
1804 {
1807 if ( ok )
1808 {
1809 penWidth = strokeWidth;
1810 }
1811 }
1814 {
1817 if ( ok && strokeStyle == QLatin1String( "no" ) )
1818 {
1819 penWidth = 0.0;
1820 }
1821 }
1822 else if ( mStrokeStyle == Qt::NoPen )
1823 penWidth = 0;
1824
1825 //antialiasing, add 1 pixel
1826 penWidth += 1;
1827
1828 //extend bounds by pen width / 2.0
1829 symbolBounds.adjust( -penWidth / 2.0, -penWidth / 2.0,
1830 penWidth / 2.0, penWidth / 2.0 );
1831
1832 return symbolBounds;
1833}
1834
1835void QgsSimpleMarkerSymbolLayer::setColor( const QColor &color )
1836{
1837 if ( shapeIsFilled( mShape ) )
1838 {
1840 }
1841 else
1842 {
1844 }
1845}
1846
1848{
1849 if ( shapeIsFilled( mShape ) )
1850 {
1851 return fillColor();
1852 }
1853 else
1854 {
1855 return strokeColor();
1856 }
1857}
1858
1859
1860
1861
1862//
1863// QgsFilledMarkerSymbolLayer
1864//
1865
1867 : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
1868{
1869 mFill.reset( static_cast<QgsFillSymbol *>( QgsFillSymbol::createSimple( QVariantMap() ) ) );
1870}
1871
1873
1875{
1876 QString name = DEFAULT_SIMPLEMARKER_NAME;
1880
1881 if ( props.contains( QStringLiteral( "name" ) ) )
1882 name = props[QStringLiteral( "name" )].toString();
1883 if ( props.contains( QStringLiteral( "size" ) ) )
1884 size = props[QStringLiteral( "size" )].toDouble();
1885 if ( props.contains( QStringLiteral( "angle" ) ) )
1886 angle = props[QStringLiteral( "angle" )].toDouble();
1887 if ( props.contains( QStringLiteral( "scale_method" ) ) )
1888 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1889
1891 if ( props.contains( QStringLiteral( "offset" ) ) )
1892 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1893 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1894 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1895 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1896 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1897 if ( props.contains( QStringLiteral( "size_unit" ) ) )
1898 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1899 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1900 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1901 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1902 {
1903 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1904 }
1905 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1906 {
1907 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1908 }
1909
1911
1913
1914 return m;
1915}
1916
1918{
1919 return QStringLiteral( "FilledMarker" );
1920}
1921
1923{
1924 if ( mFill )
1925 {
1926 mFill->setRenderHints( mFill->renderHints() | Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol );
1927 mFill->startRender( context.renderContext(), context.fields() );
1928 }
1929
1931}
1932
1934{
1935 if ( mFill )
1936 {
1937 mFill->stopRender( context.renderContext() );
1938 }
1939}
1940
1942{
1943 QVariantMap map;
1944 map[QStringLiteral( "name" )] = encodeShape( mShape );
1945 map[QStringLiteral( "size" )] = QString::number( mSize );
1946 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1947 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1948 map[QStringLiteral( "angle" )] = QString::number( mAngle );
1949 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1950 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1951 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1952 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1953 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1954 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1955
1956 if ( mFill )
1957 {
1958 map[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mFill->color() );
1959 }
1960 return map;
1961}
1962
1971
1973{
1974 return mFill.get();
1975}
1976
1978{
1979 if ( symbol && symbol->type() == Qgis::SymbolType::Fill )
1980 {
1981 mFill.reset( static_cast<QgsFillSymbol *>( symbol ) );
1982 return true;
1983 }
1984 else
1985 {
1986 delete symbol;
1987 return false;
1988 }
1989}
1990
1992{
1993 if ( mFill )
1994 {
1995 return QgsSymbolLayerUtils::estimateMaxSymbolBleed( mFill.get(), context );
1996 }
1997 return 0;
1998}
1999
2001{
2002 QSet<QString> attr = QgsSimpleMarkerSymbolLayerBase::usedAttributes( context );
2003 if ( mFill )
2004 attr.unite( mFill->usedAttributes( context ) );
2005 return attr;
2006}
2007
2009{
2011 return true;
2012 if ( mFill && mFill->hasDataDefinedProperties() )
2013 return true;
2014 return false;
2015}
2016
2018{
2019 mColor = c;
2020 if ( mFill )
2021 mFill->setColor( c );
2022}
2023
2025{
2026 return mFill ? mFill->color() : mColor;
2027}
2028
2035
2037{
2039 if ( mFill )
2040 mFill->setOutputUnit( unit );
2041}
2042
2043void QgsFilledMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
2044{
2045 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
2046 //of the rendered point!
2047
2048 QPainter *p = context.renderContext().painter();
2049 if ( !p )
2050 {
2051 return;
2052 }
2053
2054 const double prevOpacity = mFill->opacity();
2055 mFill->setOpacity( mFill->opacity() * context.opacity() );
2056
2057 if ( shapeIsFilled( shape ) )
2058 {
2059 p->setBrush( Qt::red );
2060 }
2061 else
2062 {
2063 p->setBrush( Qt::NoBrush );
2064 }
2065 p->setPen( Qt::black );
2066
2067 const bool prevIsSubsymbol = context.renderContext().flags() & Qgis::RenderContextFlag::RenderingSubSymbol;
2069
2070 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
2071 if ( !polygon.isEmpty() )
2072 {
2073 mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
2074 }
2075 else
2076 {
2077 const QPolygonF poly = path.toFillPolygon();
2078 mFill->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
2079 }
2080
2082
2083 mFill->setOpacity( prevOpacity );
2084}
2085
2086
2088
2089
2090QgsSvgMarkerSymbolLayer::QgsSvgMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
2091{
2092 mSize = size;
2093 mAngle = angle;
2094 mOffset = QPointF( 0, 0 );
2096 mStrokeWidth = 0.2;
2098 mColor = QColor( 35, 35, 35 );
2099 mStrokeColor = QColor( 35, 35, 35 );
2100 setPath( path );
2101}
2102
2104 : QgsMarkerSymbolLayer( other )
2105 , mPath( other.mPath )
2106 , mDefaultAspectRatio( other.mDefaultAspectRatio )
2107 , mFixedAspectRatio( other.mFixedAspectRatio )
2108 , mHasFillParam( other.mHasFillParam )
2109 , mStrokeColor( other.mStrokeColor )
2110 , mStrokeWidth( other.mStrokeWidth )
2111 , mParameters( other.mParameters )
2112 , mStrokeWidthUnit( other.mStrokeWidthUnit )
2113 , mStrokeWidthMapUnitScale( other.mStrokeWidthMapUnitScale )
2114{
2115}
2116
2118
2120{
2121 QString name;
2125
2126 if ( props.contains( QStringLiteral( "name" ) ) )
2127 name = props[QStringLiteral( "name" )].toString();
2128 if ( props.contains( QStringLiteral( "size" ) ) )
2129 size = props[QStringLiteral( "size" )].toDouble();
2130 if ( props.contains( QStringLiteral( "angle" ) ) )
2131 angle = props[QStringLiteral( "angle" )].toDouble();
2132 if ( props.contains( QStringLiteral( "scale_method" ) ) )
2133 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
2134
2136
2137 if ( props.contains( QStringLiteral( "size_unit" ) ) )
2138 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
2139 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
2140 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
2141 if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
2142 m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
2143 if ( props.contains( QStringLiteral( "offset" ) ) )
2144 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
2145 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
2146 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
2147 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2148 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
2149 if ( props.contains( QStringLiteral( "fill" ) ) )
2150 {
2151 //pre 2.5 projects used "fill"
2152 m->setFillColor( QgsColorUtils::colorFromString( props[QStringLiteral( "fill" )].toString() ) );
2153 }
2154 else if ( props.contains( QStringLiteral( "color" ) ) )
2155 {
2156 m->setFillColor( QgsColorUtils::colorFromString( props[QStringLiteral( "color" )].toString() ) );
2157 }
2158 if ( props.contains( QStringLiteral( "outline" ) ) )
2159 {
2160 //pre 2.5 projects used "outline"
2161 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "outline" )].toString() ) );
2162 }
2163 else if ( props.contains( QStringLiteral( "outline_color" ) ) )
2164 {
2165 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "outline_color" )].toString() ) );
2166 }
2167 else if ( props.contains( QStringLiteral( "line_color" ) ) )
2168 {
2169 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "line_color" )].toString() ) );
2170 }
2171
2172 if ( props.contains( QStringLiteral( "outline-width" ) ) )
2173 {
2174 //pre 2.5 projects used "outline-width"
2175 m->setStrokeWidth( props[QStringLiteral( "outline-width" )].toDouble() );
2176 }
2177 else if ( props.contains( QStringLiteral( "outline_width" ) ) )
2178 {
2179 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
2180 }
2181 else if ( props.contains( QStringLiteral( "line_width" ) ) )
2182 {
2183 m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
2184 }
2185
2186 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
2187 {
2188 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
2189 }
2190 else if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
2191 {
2192 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
2193 }
2194 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
2195 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
2196
2197 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2198 {
2199 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2200 }
2201 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2202 {
2203 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2204 }
2205
2207
2209
2210 if ( props.contains( QStringLiteral( "parameters" ) ) )
2211 {
2212 const QVariantMap parameters = props[QStringLiteral( "parameters" )].toMap();
2214 }
2215
2216 return m;
2217}
2218
2219void QgsSvgMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
2220{
2221 const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
2222 if ( it != properties.end() )
2223 {
2224 if ( saving )
2225 {
2226 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
2227 }
2228 else
2229 {
2230 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
2231 }
2232 }
2233}
2234
2235void QgsSvgMarkerSymbolLayer::setPath( const QString &path )
2236{
2238 mHasFillParam = false;
2239 mPath = path;
2240 QColor defaultFillColor, defaultStrokeColor;
2241 double strokeWidth, fillOpacity, strokeOpacity;
2242 bool hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
2243 bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
2244 QgsApplication::svgCache()->containsParams( path, mHasFillParam, hasDefaultFillColor, defaultFillColor,
2245 hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
2246 hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2247 hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
2248 hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
2249
2250 const double newFillOpacity = hasFillOpacityParam ? fillColor().alphaF() : 1.0;
2251 const double newStrokeOpacity = hasStrokeOpacityParam ? strokeColor().alphaF() : 1.0;
2252
2253 if ( hasDefaultFillColor )
2254 {
2255 defaultFillColor.setAlphaF( newFillOpacity );
2256 setFillColor( defaultFillColor );
2257 }
2258 if ( hasDefaultFillOpacity )
2259 {
2260 QColor c = fillColor();
2261 c.setAlphaF( fillOpacity );
2262 setFillColor( c );
2263 }
2264 if ( hasDefaultStrokeColor )
2265 {
2266 defaultStrokeColor.setAlphaF( newStrokeOpacity );
2267 setStrokeColor( defaultStrokeColor );
2268 }
2269 if ( hasDefaultStrokeWidth )
2270 {
2272 }
2273 if ( hasDefaultStrokeOpacity )
2274 {
2275 QColor c = strokeColor();
2276 c.setAlphaF( strokeOpacity );
2277 setStrokeColor( c );
2278 }
2279
2281}
2282
2284{
2285 if ( mDefaultAspectRatio == 0.0 )
2286 {
2287 //size
2288 const double size = mSize;
2289 //assume 88 dpi as standard value
2290 const double widthScaleFactor = 3.465;
2291 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( mPath, size, mColor, mStrokeColor, mStrokeWidth, widthScaleFactor );
2292 // set default aspect ratio
2293 mDefaultAspectRatio = svgViewbox.isValid() ? svgViewbox.height() / svgViewbox.width() : 0.0;
2294 }
2295 return mDefaultAspectRatio;
2296}
2297
2299{
2300 const bool aPreservedAspectRatio = preservedAspectRatio();
2301 if ( aPreservedAspectRatio && !par )
2302 {
2304 }
2305 else if ( !aPreservedAspectRatio && par )
2306 {
2307 mFixedAspectRatio = 0.0;
2308 }
2309 return preservedAspectRatio();
2310}
2311
2312void QgsSvgMarkerSymbolLayer::setParameters( const QMap<QString, QgsProperty> &parameters )
2313{
2315}
2316
2317
2319{
2320 return QStringLiteral( "SvgMarker" );
2321}
2322
2327
2329{
2330 QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
2331 Q_UNUSED( context )
2332}
2333
2335{
2336 Q_UNUSED( context )
2337}
2338
2340{
2341 QPainter *p = context.renderContext().painter();
2342 if ( !p )
2343 return;
2344
2345 bool hasDataDefinedSize = false;
2346 const double scaledWidth = calculateSize( context, hasDataDefinedSize );
2347 const double devicePixelRatio = context.renderContext().devicePixelRatio();
2348 const double width = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2349
2350 //don't render symbols with a width below one or above 10,000 pixels
2351 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2352 {
2353 return;
2354 }
2355
2356 const QgsScopedQPainterState painterState( p );
2357
2358 bool hasDataDefinedAspectRatio = false;
2359 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2360 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2361
2363
2364 double strokeWidth = mStrokeWidth;
2366 {
2369 }
2371
2372 QColor fillColor = mColor;
2373 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
2374 if ( useSelectedColor && mHasFillParam )
2375 {
2377 }
2379 {
2382 }
2383
2384 QColor strokeColor = mStrokeColor;
2386 {
2389 }
2390
2391 QString path = mPath;
2393 {
2396 context.renderContext().pathResolver() );
2398 {
2399 // adjust height of data defined path
2400 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2401 context.renderContext().scaleFactor(), aspectRatio,
2402 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2403 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2404 }
2405 }
2406
2407 QPointF outputOffset;
2408 double angle = 0.0;
2409 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2410
2411 p->translate( point + outputOffset );
2412
2413 const bool rotated = !qgsDoubleNear( angle, 0 );
2414 if ( rotated )
2415 p->rotate( angle );
2416
2417 bool fitsInCache = true;
2418 bool usePict = true;
2420 if ( ( !context.forceVectorRendering() && !rotated ) || ( useSelectedColor && rasterizeSelected ) )
2421 {
2422 QImage img = QgsApplication::svgCache()->svgAsImage( path, width * devicePixelRatio, fillColor, strokeColor, strokeWidth,
2423 context.renderContext().scaleFactor(), fitsInCache, aspectRatio,
2424 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2425 if ( fitsInCache && img.width() > 1 )
2426 {
2427 usePict = false;
2428
2429 if ( useSelectedColor )
2430 {
2432 }
2433
2434 //consider transparency
2435 if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2436 {
2437 QImage transparentImage = img.copy();
2438 QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2439 if ( devicePixelRatio == 1 )
2440 {
2441 p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
2442 }
2443 else
2444 {
2445 p->drawImage( QRectF( -transparentImage.width() / 2.0 / devicePixelRatio, -transparentImage.height() / 2.0 / devicePixelRatio,
2446 transparentImage.width() / devicePixelRatio, transparentImage.height() / devicePixelRatio
2447 ), transparentImage );
2448 }
2449 }
2450 else
2451 {
2452 if ( devicePixelRatio == 1 )
2453 {
2454 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2455 }
2456 else
2457 {
2458 p->drawImage( QRectF( -img.width() / 2.0 / devicePixelRatio, -img.height() / 2.0 / devicePixelRatio,
2459 img.width() / devicePixelRatio, img.height() / devicePixelRatio ), img );
2460 }
2461 }
2462 }
2463 }
2464
2465 if ( usePict || !fitsInCache )
2466 {
2467 p->setOpacity( context.opacity() );
2469 context.renderContext().scaleFactor(), context.forceVectorRendering(), aspectRatio,
2470 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2471 if ( pct.width() > 1 )
2472 {
2473 QgsPainting::drawPicture( p, QPointF( 0, 0 ), pct );
2474 }
2475 }
2476
2477 // workaround issue with nested QPictures forgetting antialiasing flag - see https://github.com/qgis/QGIS/issues/22909
2479}
2480
2481double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2482{
2483 double scaledSize = mSize;
2485
2486 bool ok = true;
2487 if ( hasDataDefinedSize )
2488 {
2491 }
2492 else
2493 {
2495 if ( hasDataDefinedSize )
2496 {
2499 }
2500 }
2501
2502 if ( hasDataDefinedSize && ok )
2503 {
2504 switch ( mScaleMethod )
2505 {
2507 scaledSize = std::sqrt( scaledSize );
2508 break;
2510 break;
2511 }
2512 }
2513
2514 return scaledSize;
2515}
2516
2517double QgsSvgMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2518{
2520 if ( !hasDataDefinedAspectRatio )
2521 return mFixedAspectRatio;
2522
2524 return 0.0;
2525
2526 double scaledAspectRatio = mDefaultAspectRatio;
2527 if ( mFixedAspectRatio > 0.0 )
2528 scaledAspectRatio = mFixedAspectRatio;
2529
2530 const double defaultHeight = mSize * scaledAspectRatio;
2531 scaledAspectRatio = defaultHeight / scaledSize;
2532
2533 bool ok = true;
2534 double scaledHeight = scaledSize * scaledAspectRatio;
2536 {
2537 context.setOriginalValueVariable( defaultHeight );
2539 }
2540
2541 if ( hasDataDefinedAspectRatio && ok )
2542 {
2543 switch ( mScaleMethod )
2544 {
2546 scaledHeight = sqrt( scaledHeight );
2547 break;
2549 break;
2550 }
2551 }
2552
2553 scaledAspectRatio = scaledHeight / scaledSize;
2554
2555 return scaledAspectRatio;
2556}
2557
2558void QgsSvgMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
2559{
2560 //offset
2561 double offsetX = 0;
2562 double offsetY = 0;
2563 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
2564 offset = QPointF( offsetX, offsetY );
2565
2568 {
2571 }
2572
2574 if ( hasDataDefinedRotation )
2575 {
2576 // For non-point markers, "dataDefinedRotation" means following the
2577 // shape (shape-data defined). For them, "field-data defined" does
2578 // not work at all. TODO: if "field-data defined" ever gets implemented
2579 // we'll need a way to distinguish here between the two, possibly
2580 // using another flag in renderHints()
2581 const QgsFeature *f = context.feature();
2582 if ( f )
2583 {
2584 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
2585 {
2586 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2587 angle += m2p.mapRotation();
2588 }
2589 }
2590 }
2591
2592 if ( angle )
2594}
2595
2596
2598{
2599 QVariantMap map;
2600 map[QStringLiteral( "name" )] = mPath;
2601 map[QStringLiteral( "size" )] = QString::number( mSize );
2602 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2603 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2604 map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2605 map[QStringLiteral( "angle" )] = QString::number( mAngle );
2606 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2607 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2608 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2609 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2610 map[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mColor );
2611 map[QStringLiteral( "outline_color" )] = QgsColorUtils::colorToString( mStrokeColor );
2612 map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
2613 map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2614 map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2615 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
2616 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
2617
2618 map[QStringLiteral( "parameters" )] = QgsProperty::propertyMapToVariantMap( mParameters );
2619
2620 return map;
2621}
2622
2629
2634
2640
2642{
2644 if ( unit != mStrokeWidthUnit )
2645 {
2647 }
2648 return unit;
2649}
2650
2656
2665
2666void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
2667{
2668 // <Graphic>
2669 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2670 element.appendChild( graphicElem );
2671
2672 // encode a parametric SVG reference
2673 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
2676
2677 // <Rotation>
2678 QString angleFunc;
2679 bool ok;
2680 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2681 if ( !ok )
2682 {
2683 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
2684 }
2685 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2686 {
2687 angleFunc = QString::number( angle + mAngle );
2688 }
2689
2690 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2691
2692 // <Displacement>
2693 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2695}
2696
2698{
2699 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
2700
2701 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
2702 if ( graphicElem.isNull() )
2703 return nullptr;
2704
2705 QString path, mimeType;
2706 // Unused and to be DEPRECATED in externalGraphicFromSld
2707 QColor fillColor_;
2708 double size;
2709
2710 if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor_, size ) )
2711 return nullptr;
2712
2713 double scaleFactor = 1.0;
2714 const QString uom = element.attribute( QStringLiteral( "uom" ) );
2715 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
2716 size = size * scaleFactor;
2717
2718 if ( mimeType != QLatin1String( "image/svg+xml" ) )
2719 return nullptr;
2720
2721 double angle = 0.0;
2722 QString angleFunc;
2723 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2724 {
2725 bool ok;
2726 const double d = angleFunc.toDouble( &ok );
2727 if ( ok )
2728 angle = d;
2729 }
2730
2731 QPointF offset;
2733
2734 // Extract parameters from URL
2735 QString realPath { path };
2736 QUrl svgUrl { path };
2737
2738 // Because color definition can start with '#', the url parsing won't recognize the query string entirely
2739 QUrlQuery queryString;
2740
2741 if ( svgUrl.hasQuery() && svgUrl.hasFragment() )
2742 {
2743 const QString queryPart { path.mid( path.indexOf( '?' ) + 1 ) };
2744 queryString.setQuery( queryPart );
2745 }
2746
2747 // Remove query for simple file paths
2748 if ( svgUrl.scheme().isEmpty() || svgUrl.isLocalFile() )
2749 {
2750 svgUrl.setQuery( QString() );
2751 realPath = svgUrl.path();
2752 }
2753
2755
2756 QMap<QString, QgsProperty> params;
2757
2758 bool ok;
2759
2760 if ( queryString.hasQueryItem( QStringLiteral( "fill" ) ) )
2761 {
2762 const QColor fillColor { queryString.queryItemValue( QStringLiteral( "fill" ) ) };
2763 m->setFillColor( fillColor );
2764 }
2765
2766 if ( queryString.hasQueryItem( QStringLiteral( "fill-opacity" ) ) )
2767 {
2768 const double alpha { queryString.queryItemValue( QStringLiteral( "fill-opacity" ) ).toDouble( &ok ) };
2769 if ( ok )
2770 {
2771 params.insert( QStringLiteral( "fill-opacity" ), QgsProperty::fromValue( alpha ) );
2772 }
2773 }
2774
2775 if ( queryString.hasQueryItem( QStringLiteral( "outline" ) ) )
2776 {
2777 const QColor strokeColor { queryString.queryItemValue( QStringLiteral( "outline" ) ) };
2779 }
2780
2781 if ( queryString.hasQueryItem( QStringLiteral( "outline-opacity" ) ) )
2782 {
2783 const double alpha { queryString.queryItemValue( QStringLiteral( "outline-opacity" ) ).toDouble( &ok ) };
2784 if ( ok )
2785 {
2786 params.insert( QStringLiteral( "outline-opacity" ), QgsProperty::fromValue( alpha ) );
2787 }
2788 }
2789
2790 if ( queryString.hasQueryItem( QStringLiteral( "outline-width" ) ) )
2791 {
2792 const int width { queryString.queryItemValue( QStringLiteral( "outline-width" ) ).toInt( &ok )};
2793 if ( ok )
2794 {
2795 m->setStrokeWidth( width );
2796 }
2797 }
2798
2799 if ( ! params.isEmpty() )
2800 {
2801 m->setParameters( params );
2802 }
2803
2804 m->setOutputUnit( sldUnitSize );
2805 m->setAngle( angle );
2806 m->setOffset( offset );
2807 return m;
2808}
2809
2810bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
2811{
2812 //size
2813 double size = mSize;
2814
2815 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
2816
2817 bool ok = true;
2818 if ( hasDataDefinedSize )
2819 {
2822 }
2823
2824 if ( hasDataDefinedSize && ok )
2825 {
2826 switch ( mScaleMethod )
2827 {
2829 size = std::sqrt( size );
2830 break;
2832 break;
2833 }
2834 }
2835
2837 {
2838 size *= mmMapUnitScaleFactor;
2839 }
2840
2841//offset, angle
2842 QPointF offset = mOffset;
2843
2845 {
2848 const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
2849 if ( ok )
2850 offset = res;
2851 }
2852 const double offsetX = offset.x();
2853 const double offsetY = offset.y();
2854
2855 QPointF outputOffset( offsetX, offsetY );
2856
2857 double angle = mAngle + mLineAngle;
2859 {
2862 }
2863
2864 if ( angle )
2865 outputOffset = _rotatedOffset( outputOffset, angle );
2866
2868
2869 QString path = mPath;
2871 {
2874 context.renderContext().pathResolver() );
2875 }
2876
2877 double strokeWidth = mStrokeWidth;
2879 {
2882 }
2884
2885 QColor fillColor = mColor;
2887 {
2890 }
2891
2892 QColor strokeColor = mStrokeColor;
2894 {
2897 }
2898
2900
2901 const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, size, fillColor, strokeColor, strokeWidth,
2903 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2904
2905 QSvgRenderer r( svgContent );
2906 if ( !r.isValid() )
2907 return false;
2908
2909 QgsDxfPaintDevice pd( &e );
2910 pd.setDrawingSize( QSizeF( r.defaultSize() ) );
2911
2912 QSizeF outSize( r.defaultSize() );
2913 outSize.scale( size, size, Qt::KeepAspectRatio );
2914
2915 QPainter p;
2916 p.begin( &pd );
2917 if ( !qgsDoubleNear( angle, 0.0 ) )
2918 {
2919 p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
2920 p.rotate( angle );
2921 p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
2922 }
2923 pd.setShift( shift + QPointF( outputOffset.x(), -outputOffset.y() ) );
2924 pd.setOutputSize( QRectF( -outSize.width() / 2.0, -outSize.height() / 2.0, outSize.width(), outSize.height() ) );
2925 pd.setLayer( layerName );
2926 r.render( &p );
2927 p.end();
2928 return true;
2929}
2930
2932{
2933 bool hasDataDefinedSize = false;
2934 double scaledWidth = calculateSize( context, hasDataDefinedSize );
2935
2936 bool hasDataDefinedAspectRatio = false;
2937 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2938 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2939
2940 scaledWidth = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2941 scaledHeight = context.renderContext().convertToPainterUnits( scaledHeight, mSizeUnit, mSizeMapUnitScale );
2942
2943 //don't render symbols with size below one or above 10,000 pixels
2944 if ( static_cast< int >( scaledWidth ) < 1 || 10000.0 < scaledWidth )
2945 {
2946 return QRectF();
2947 }
2948
2949 QPointF outputOffset;
2950 double angle = 0.0;
2951 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2952
2953 double strokeWidth = mStrokeWidth;
2955 {
2958 }
2960
2961 QString path = mPath;
2963 {
2966 context.renderContext().pathResolver() );
2968 {
2969 // need to get colors to take advantage of cached SVGs
2970 QColor fillColor = mColor;
2972 {
2975 }
2976
2977 const QColor strokeColor = mStrokeColor;
2979 {
2982 }
2983
2985
2986 // adjust height of data defined path
2987 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2988 context.renderContext().scaleFactor(), aspectRatio,
2989 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2990 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2991 }
2992 }
2993
2994 QTransform transform;
2995 // move to the desired position
2996 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
2997
2998 if ( !qgsDoubleNear( angle, 0.0 ) )
2999 transform.rotate( angle );
3000
3001 //antialiasing
3002 strokeWidth += 1.0 / 2.0;
3003
3004 QRectF symbolBounds = transform.mapRect( QRectF( -scaledWidth / 2.0,
3005 -scaledHeight / 2.0,
3006 scaledWidth,
3007 scaledHeight ) );
3008
3009 //extend bounds by pen width / 2.0
3010 symbolBounds.adjust( -strokeWidth / 2.0, -strokeWidth / 2.0,
3011 strokeWidth / 2.0, strokeWidth / 2.0 );
3012
3013 return symbolBounds;
3014}
3015
3017
3018QgsRasterMarkerSymbolLayer::QgsRasterMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
3019 : mPath( path )
3020{
3021 mSize = size;
3022 mAngle = angle;
3023 mOffset = QPointF( 0, 0 );
3026}
3027
3029
3031{
3032 QString path;
3036
3037 if ( props.contains( QStringLiteral( "imageFile" ) ) )
3038 path = props[QStringLiteral( "imageFile" )].toString();
3039 if ( props.contains( QStringLiteral( "size" ) ) )
3040 size = props[QStringLiteral( "size" )].toDouble();
3041 if ( props.contains( QStringLiteral( "angle" ) ) )
3042 angle = props[QStringLiteral( "angle" )].toDouble();
3043 if ( props.contains( QStringLiteral( "scale_method" ) ) )
3044 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
3045
3046 std::unique_ptr< QgsRasterMarkerSymbolLayer > m = std::make_unique< QgsRasterMarkerSymbolLayer >( path, size, angle, scaleMethod );
3047 m->setCommonProperties( props );
3048 return m.release();
3049}
3050
3051void QgsRasterMarkerSymbolLayer::setCommonProperties( const QVariantMap &properties )
3052{
3053 if ( properties.contains( QStringLiteral( "alpha" ) ) )
3054 {
3055 setOpacity( properties[QStringLiteral( "alpha" )].toDouble() );
3056 }
3057
3058 if ( properties.contains( QStringLiteral( "size_unit" ) ) )
3059 setSizeUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "size_unit" )].toString() ) );
3060 if ( properties.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3061 setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3062 if ( properties.contains( QStringLiteral( "fixedAspectRatio" ) ) )
3063 setFixedAspectRatio( properties[QStringLiteral( "fixedAspectRatio" )].toDouble() );
3064
3065 if ( properties.contains( QStringLiteral( "offset" ) ) )
3066 setOffset( QgsSymbolLayerUtils::decodePoint( properties[QStringLiteral( "offset" )].toString() ) );
3067 if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
3068 setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )].toString() ) );
3069 if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3070 setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3071
3072 if ( properties.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3073 {
3074 setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( properties[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3075 }
3076 if ( properties.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3077 {
3078 setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( properties[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3079 }
3080
3083}
3084
3085void QgsRasterMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
3086{
3087 const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
3088 if ( it != properties.end() && it.value().userType() == QMetaType::Type::QString )
3089 {
3090 if ( saving )
3091 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
3092 else
3093 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
3094 }
3095}
3096
3097void QgsRasterMarkerSymbolLayer::setPath( const QString &path )
3098{
3099 mPath = path;
3101}
3102
3104{
3105 const bool aPreservedAspectRatio = preservedAspectRatio();
3106 if ( aPreservedAspectRatio && !par )
3107 {
3109 }
3110 else if ( !aPreservedAspectRatio && par )
3111 {
3112 mFixedAspectRatio = 0.0;
3113 }
3114 return preservedAspectRatio();
3115}
3116
3118{
3119 if ( mDefaultAspectRatio == 0.0 )
3120 {
3122 mDefaultAspectRatio = ( !size.isNull() && size.isValid() && size.width() > 0 ) ? static_cast< double >( size.height() ) / static_cast< double >( size.width() ) : 0.0;
3123 }
3124 return mDefaultAspectRatio;
3125}
3126
3128{
3129 return QStringLiteral( "RasterMarker" );
3130}
3131
3136
3138{
3139 QPainter *p = context.renderContext().painter();
3140 if ( !p )
3141 return;
3142
3143 QString path = mPath;
3145 {
3148 }
3149
3150 if ( path.isEmpty() )
3151 return;
3152
3153 double width = 0.0;
3154 double height = 0.0;
3155
3156 bool hasDataDefinedSize = false;
3157 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3158
3159 bool hasDataDefinedAspectRatio = false;
3160 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3161
3162 QPointF outputOffset;
3163 double angle = 0.0;
3164
3165 // RenderPercentage Unit Type takes original image size
3167 {
3169 if ( size.isEmpty() )
3170 return;
3171
3172 width = ( scaledSize * static_cast< double >( size.width() ) ) / 100.0;
3173 height = ( scaledSize * static_cast< double >( size.height() ) ) / 100.0;
3174
3175 // don't render symbols with size below one or above 10,000 pixels
3176 if ( static_cast< int >( width ) < 1 || 10000.0 < width || static_cast< int >( height ) < 1 || 10000.0 < height )
3177 return;
3178
3179 calculateOffsetAndRotation( context, width, height, outputOffset, angle );
3180 }
3181 else
3182 {
3183 width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3184 height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3185
3186 if ( preservedAspectRatio() && path != mPath )
3187 {
3189 if ( !size.isNull() && size.isValid() && size.width() > 0 )
3190 {
3191 height = width * ( static_cast< double >( size.height() ) / static_cast< double >( size.width() ) );
3192 }
3193 }
3194
3195 // don't render symbols with size below one or above 10,000 pixels
3196 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
3197 return;
3198
3199 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3200 }
3201
3202 const QgsScopedQPainterState painterState( p );
3203 p->translate( point + outputOffset );
3204
3205 const bool rotated = !qgsDoubleNear( angle, 0 );
3206 if ( rotated )
3207 p->rotate( angle );
3208
3209 double opacity = mOpacity;
3211 {
3214 }
3215 opacity *= context.opacity();
3216
3217 QImage img = fetchImage( context.renderContext(), path, QSize( width, preservedAspectRatio() ? 0 : width * aspectRatio ), preservedAspectRatio(), opacity );
3218 if ( !img.isNull() )
3219 {
3220 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
3221 if ( useSelectedColor )
3222 {
3224 }
3225
3226 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
3227 }
3228}
3229
3230QImage QgsRasterMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
3231{
3232 bool cached = false;
3233 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking );
3234}
3235
3236double QgsRasterMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
3237{
3238 double scaledSize = mSize;
3240
3241 bool ok = true;
3242 if ( hasDataDefinedSize )
3243 {
3246 }
3247 else
3248 {
3250 if ( hasDataDefinedSize )
3251 {
3254 }
3255 }
3256
3257 if ( hasDataDefinedSize && ok )
3258 {
3259 switch ( mScaleMethod )
3260 {
3262 scaledSize = std::sqrt( scaledSize );
3263 break;
3265 break;
3266 }
3267 }
3268
3269 return scaledSize;
3270}
3271
3272double QgsRasterMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
3273{
3275 if ( !hasDataDefinedAspectRatio )
3276 return mFixedAspectRatio;
3277
3279 return 0.0;
3280
3281 double scaledAspectRatio = mDefaultAspectRatio;
3282 if ( mFixedAspectRatio > 0.0 )
3283 scaledAspectRatio = mFixedAspectRatio;
3284
3285 const double defaultHeight = mSize * scaledAspectRatio;
3286 scaledAspectRatio = defaultHeight / scaledSize;
3287
3288 bool ok = true;
3289 double scaledHeight = scaledSize * scaledAspectRatio;
3291 {
3292 context.setOriginalValueVariable( defaultHeight );
3294 }
3295
3296 if ( hasDataDefinedAspectRatio && ok )
3297 {
3298 switch ( mScaleMethod )
3299 {
3301 scaledHeight = sqrt( scaledHeight );
3302 break;
3304 break;
3305 }
3306 }
3307
3308 scaledAspectRatio = scaledHeight / scaledSize;
3309
3310 return scaledAspectRatio;
3311}
3312
3313void QgsRasterMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
3314{
3315 //offset
3316 double offsetX = 0;
3317 double offsetY = 0;
3318 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
3319 offset = QPointF( offsetX, offsetY );
3320
3323 {
3326 }
3327
3329 if ( hasDataDefinedRotation )
3330 {
3331 const QgsFeature *f = context.feature();
3332 if ( f )
3333 {
3334 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3335 {
3336 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3337 angle += m2p.mapRotation();
3338 }
3339 }
3340 }
3341
3342 if ( angle )
3344}
3345
3346
3348{
3349 QVariantMap map;
3350 map[QStringLiteral( "imageFile" )] = mPath;
3351 map[QStringLiteral( "size" )] = QString::number( mSize );
3352 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3353 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3354 map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
3355 map[QStringLiteral( "angle" )] = QString::number( mAngle );
3356 map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
3357 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3358 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3359 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3360 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
3361 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3362 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3363 return map;
3364}
3365
3367{
3368 std::unique_ptr< QgsRasterMarkerSymbolLayer > m = std::make_unique< QgsRasterMarkerSymbolLayer >( mPath, mSize, mAngle );
3369 copyCommonProperties( m.get() );
3370 return m.release();
3371}
3372
3373
3388
3394
3396{
3397 return QColor();
3398}
3399
3404
3409
3411{
3412 bool hasDataDefinedSize = false;
3413 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3414 const double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3415 bool hasDataDefinedAspectRatio = false;
3416 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3417 const double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3418
3419 //don't render symbols with size below one or above 10,000 pixels
3420 if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
3421 {
3422 return QRectF();
3423 }
3424
3425 QPointF outputOffset;
3426 double angle = 0.0;
3427 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3428
3429 QTransform transform;
3430
3431 // move to the desired position
3432 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3433
3434 if ( !qgsDoubleNear( angle, 0.0 ) )
3435 transform.rotate( angle );
3436
3437 QRectF symbolBounds = transform.mapRect( QRectF( -width / 2.0,
3438 -height / 2.0,
3439 width,
3440 height ) );
3441
3442 return symbolBounds;
3443}
3444
3446
3447QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QString chr, double pointSize, const QColor &color, double angle )
3448{
3449 mFontFamily = fontFamily;
3450 mString = chr;
3451 mColor = color;
3452 mAngle = angle;
3453 mSize = pointSize;
3454 mOrigSize = pointSize;
3456 mOffset = QPointF( 0, 0 );
3458 mStrokeColor = DEFAULT_FONTMARKER_BORDERCOLOR;
3459 mStrokeWidth = 0.0;
3460 mStrokeWidthUnit = Qgis::RenderUnit::Millimeters;
3461 mPenJoinStyle = DEFAULT_FONTMARKER_JOINSTYLE;
3462}
3463
3465
3467{
3469 QString string = DEFAULT_FONTMARKER_CHR;
3470 double pointSize = DEFAULT_FONTMARKER_SIZE;
3473
3474 if ( props.contains( QStringLiteral( "font" ) ) )
3475 fontFamily = props[QStringLiteral( "font" )].toString();
3476 if ( props.contains( QStringLiteral( "chr" ) ) && props[QStringLiteral( "chr" )].toString().length() > 0 )
3477 {
3478 string = props["chr"].toString();
3479 const thread_local QRegularExpression charRegExp( QStringLiteral( "%1([0-9]+)%1" ).arg( FONTMARKER_CHR_FIX ) );
3480 QRegularExpressionMatch match = charRegExp.match( string );
3481 while ( match.hasMatch() )
3482 {
3483 QChar replacement = QChar( match.captured( 1 ).toUShort() );
3484 string = string.mid( 0, match.capturedStart( 0 ) ) + replacement + string.mid( match.capturedEnd( 0 ) );
3485 match = charRegExp.match( string );
3486 }
3487 }
3488
3489 if ( props.contains( QStringLiteral( "size" ) ) )
3490 pointSize = props[QStringLiteral( "size" )].toDouble();
3491 if ( props.contains( QStringLiteral( "color" ) ) )
3492 color = QgsColorUtils::colorFromString( props[QStringLiteral( "color" )].toString() );
3493 if ( props.contains( QStringLiteral( "angle" ) ) )
3494 angle = props[QStringLiteral( "angle" )].toDouble();
3495
3497
3498 if ( props.contains( QStringLiteral( "font_style" ) ) )
3499 m->setFontStyle( props[QStringLiteral( "font_style" )].toString() );
3500 if ( props.contains( QStringLiteral( "outline_color" ) ) )
3501 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "outline_color" )].toString() ) );
3502 if ( props.contains( QStringLiteral( "outline_width" ) ) )
3503 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
3504 if ( props.contains( QStringLiteral( "offset" ) ) )
3505 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
3506 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
3507 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
3508 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3509 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3510 if ( props.contains( QStringLiteral( "size_unit" ) ) )
3511 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
3512 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3513 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3514 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
3515 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
3516 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
3517 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
3518 if ( props.contains( QStringLiteral( "joinstyle" ) ) )
3519 m->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() ) );
3520 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3521 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3522 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3523 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3524
3526
3527 return m;
3528}
3529
3531{
3532 return QStringLiteral( "FontMarker" );
3533}
3534
3539
3541{
3542 QColor brushColor = mColor;
3543 QColor penColor = mStrokeColor;
3544
3545 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
3546 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
3547
3548 mBrush = QBrush( brushColor );
3549 mPen = QPen( penColor );
3550 mPen.setJoinStyle( mPenJoinStyle );
3551 mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
3552
3553 mFont = QgsFontUtils::createFont( QgsApplication::fontManager()->processFontFamilyName( mFontFamily ) );
3554 if ( !mFontStyle.isEmpty() )
3555 {
3556 mFont.setStyleName( QgsFontUtils::translateNamedStyle( mFontStyle ) );
3557 }
3558
3559 double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3560 mNonZeroFontSize = !qgsDoubleNear( sizePixels, 0.0 );
3561
3562 if ( mNonZeroFontSize && sizePixels > MAX_FONT_CHARACTER_SIZE_IN_PIXELS )
3563 {
3564 // if font is too large (e.g using map units and map is very zoomed in), then we limit
3565 // the font size and instead scale up the painter.
3566 // this avoids issues with massive font sizes (eg https://github.com/qgis/QGIS/issues/42270)
3567 mFontSizeScale = sizePixels / MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3568 sizePixels = MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3569 }
3570 else
3571 mFontSizeScale = 1.0;
3572
3573 // if a non zero, but small pixel size results, round up to 2 pixels so that a "dot" is at least visible
3574 // (if we set a <=1 pixel size here Qt will reset the font to a default size, leading to much too large symbols)
3575 mFont.setPixelSize( std::max( 2, static_cast< int >( std::round( sizePixels ) ) ) );
3576 mFontMetrics.reset( new QFontMetrics( mFont ) );
3577 mChrWidth = mFontMetrics->horizontalAdvance( mString );
3578 mChrOffset = QPointF( mChrWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3579 mOrigSize = mSize; // save in case the size would be data defined
3580
3581 // use caching only when not using a data defined character
3585 if ( mUseCachedPath )
3586 {
3587 QPointF chrOffset = mChrOffset;
3588 double chrWidth;
3589 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3590 mCachedPath = QPainterPath();
3591 mCachedPath.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3592 }
3593}
3594
3596{
3597 Q_UNUSED( context )
3598}
3599
3600QString QgsFontMarkerSymbolLayer::characterToRender( QgsSymbolRenderContext &context, QPointF &charOffset, double &charWidth )
3601{
3602 charOffset = mChrOffset;
3603 QString stringToRender = mString;
3605 {
3606 context.setOriginalValueVariable( mString );
3608 if ( stringToRender != mString )
3609 {
3610 charWidth = mFontMetrics->horizontalAdvance( stringToRender );
3611 charOffset = QPointF( charWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3612 }
3613 }
3614 return stringToRender;
3615}
3616
3617void QgsFontMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
3618 double scaledSize,
3619 bool &hasDataDefinedRotation,
3620 QPointF &offset,
3621 double &angle ) const
3622{
3623 //offset
3624 double offsetX = 0;
3625 double offsetY = 0;
3626 markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
3627 offset = QPointF( offsetX, offsetY );
3628
3629 //angle
3630 bool ok = true;
3633 {
3636
3637 // If the expression evaluation was not successful, fallback to static value
3638 if ( !ok )
3640 }
3641
3642 hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation;
3643 if ( hasDataDefinedRotation )
3644 {
3645 // For non-point markers, "dataDefinedRotation" means following the
3646 // shape (shape-data defined). For them, "field-data defined" does
3647 // not work at all. TODO: if "field-data defined" ever gets implemented
3648 // we'll need a way to distinguish here between the two, possibly
3649 // using another flag in renderHints()
3650 const QgsFeature *f = context.feature();
3651 if ( f )
3652 {
3653 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3654 {
3655 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3656 angle += m2p.mapRotation();
3657 }
3658 }
3659 }
3660
3661 if ( angle )
3663}
3664
3665double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context )
3666{
3667 double scaledSize = mSize;
3668 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
3669
3670 bool ok = true;
3671 if ( hasDataDefinedSize )
3672 {
3675 }
3676
3677 if ( hasDataDefinedSize && ok )
3678 {
3679 switch ( mScaleMethod )
3680 {
3682 scaledSize = std::sqrt( scaledSize );
3683 break;
3685 break;
3686 }
3687 }
3688 return scaledSize;
3689}
3690
3692{
3693 QPainter *p = context.renderContext().painter();
3694 if ( !p || !mNonZeroFontSize )
3695 return;
3696
3697 QTransform transform;
3698
3699 bool ok;
3700 QColor brushColor = mColor;
3702 {
3705 }
3706 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
3707 brushColor = useSelectedColor ? context.renderContext().selectionColor() : brushColor;
3708 if ( !useSelectedColor || !SELECTION_IS_OPAQUE )
3709 {
3710 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
3711 }
3712 mBrush.setColor( brushColor );
3713
3714 QColor penColor = mStrokeColor;
3716 {
3719 }
3720 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
3721
3722 double penWidth = context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3724 {
3725 context.setOriginalValueVariable( mStrokeWidth );
3727 if ( ok )
3728 {
3729 penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3730 }
3731 }
3732
3734 {
3737 if ( ok )
3738 {
3739 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
3740 }
3741 }
3742
3743 const QgsScopedQPainterState painterState( p );
3744 p->setBrush( mBrush );
3745 if ( !qgsDoubleNear( penWidth, 0.0 ) )
3746 {
3747 mPen.setColor( penColor );
3748 mPen.setWidthF( penWidth );
3749 p->setPen( mPen );
3750 }
3751 else
3752 {
3753 p->setPen( Qt::NoPen );
3754 }
3755
3757 {
3758 context.setOriginalValueVariable( mFontFamily );
3760 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( ok ? fontFamily : mFontFamily );
3761 QgsFontUtils::setFontFamily( mFont, processedFamily );
3762 }
3764 {
3765 context.setOriginalValueVariable( mFontStyle );
3768 }
3770 {
3771 mFontMetrics.reset( new QFontMetrics( mFont ) );
3772 }
3773
3774 QPointF chrOffset = mChrOffset;
3775 double chrWidth;
3776 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3777
3778 const double sizeToRender = calculateSize( context );
3779
3780 bool hasDataDefinedRotation = false;
3781 QPointF offset;
3782 double angle = 0;
3783 calculateOffsetAndRotation( context, sizeToRender, hasDataDefinedRotation, offset, angle );
3784
3785 p->translate( point.x() + offset.x(), point.y() + offset.y() );
3786
3787 if ( !qgsDoubleNear( angle, 0.0 ) )
3788 transform.rotate( angle );
3789
3790 if ( !qgsDoubleNear( sizeToRender, mOrigSize ) )
3791 {
3792 const double s = sizeToRender / mOrigSize;
3793 transform.scale( s, s );
3794 }
3795
3796 if ( !qgsDoubleNear( mFontSizeScale, 1.0 ) )
3797 transform.scale( mFontSizeScale, mFontSizeScale );
3798
3799 if ( mUseCachedPath )
3800 {
3801 p->drawPath( transform.map( mCachedPath ) );
3802 }
3803 else
3804 {
3805 QPainterPath path;
3806 path.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3807 p->drawPath( transform.map( path ) );
3808 }
3809}
3810
3812{
3813 QVariantMap props;
3814 props[QStringLiteral( "font" )] = mFontFamily;
3815 props[QStringLiteral( "font_style" )] = mFontStyle;
3816 QString chr = mString;
3817 for ( int i = 0; i < 32; i++ )
3818 {
3819 if ( i == 9 || i == 10 || i == 13 )
3820 {
3821 continue;
3822 }
3823 chr.replace( QChar( i ), QStringLiteral( "%1%2%1" ).arg( FONTMARKER_CHR_FIX, QString::number( i ) ) );
3824 }
3825 props[QStringLiteral( "chr" )] = chr;
3826 props[QStringLiteral( "size" )] = QString::number( mSize );
3827 props[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3828 props[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3829 props[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mColor );
3830 props[QStringLiteral( "outline_color" )] = QgsColorUtils::colorToString( mStrokeColor );
3831 props[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
3832 props[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
3833 props[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
3834 props[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
3835 props[QStringLiteral( "angle" )] = QString::number( mAngle );
3836 props[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3837 props[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3838 props[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3839 props[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3840 props[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3841 return props;
3842}
3843
3845{
3846 QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( mFontFamily, mString, mSize, mColor, mAngle );
3847 m->setFontStyle( mFontStyle );
3848 m->setStrokeColor( mStrokeColor );
3849 m->setStrokeWidth( mStrokeWidth );
3850 m->setStrokeWidthUnit( mStrokeWidthUnit );
3851 m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
3852 m->setPenJoinStyle( mPenJoinStyle );
3853 m->setOffset( mOffset );
3856 m->setSizeUnit( mSizeUnit );
3861 copyPaintEffect( m );
3862 return m;
3863}
3864
3865void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
3866{
3867 // <Graphic>
3868 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
3869 element.appendChild( graphicElem );
3870
3871 const QString fontPath = QStringLiteral( "ttf://%1" ).arg( mFontFamily );
3872 int markIndex = !mString.isEmpty() ? mString.at( 0 ).unicode() : 0;
3873 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
3874 QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, QStringLiteral( "ttf" ), &markIndex, mColor, size );
3875
3876 // <Rotation>
3877 QString angleFunc;
3878 bool ok;
3879 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
3880 if ( !ok )
3881 {
3882 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
3883 }
3884 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
3885 {
3886 angleFunc = QString::number( angle + mAngle );
3887 }
3888 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
3889
3890 // <Displacement>
3891 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
3893}
3894
3901
3903{
3905 mStrokeWidthUnit = unit;
3906}
3907
3909{
3910 QPointF chrOffset = mChrOffset;
3911 double chrWidth = mChrWidth;
3912 //calculate width of rendered character
3913 ( void )characterToRender( context, chrOffset, chrWidth );
3914
3915 if ( !mFontMetrics )
3916 mFontMetrics.reset( new QFontMetrics( mFont ) );
3917
3918 double scaledSize = calculateSize( context );
3919 if ( !qgsDoubleNear( scaledSize, mOrigSize ) )
3920 {
3921 chrWidth *= scaledSize / mOrigSize;
3922 }
3923 chrWidth *= mFontSizeScale;
3924
3925 bool hasDataDefinedRotation = false;
3926 QPointF offset;
3927 double angle = 0;
3928 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
3929 scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3930
3931 QTransform transform;
3932
3933 // move to the desired position
3934 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
3935
3936 if ( !qgsDoubleNear( angle, 0.0 ) )
3937 transform.rotate( angle );
3938
3939 QRectF symbolBounds = transform.mapRect( QRectF( -chrWidth / 2.0,
3940 -scaledSize / 2.0,
3941 chrWidth,
3942 scaledSize ) );
3943 return symbolBounds;
3944}
3945
3947{
3948 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
3949
3950 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
3951 if ( graphicElem.isNull() )
3952 return nullptr;
3953
3954 QString name, format;
3955 QColor color;
3956 double size;
3957 int chr;
3958
3959 if ( !QgsSymbolLayerUtils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
3960 return nullptr;
3961
3962 if ( !name.startsWith( QLatin1String( "ttf://" ) ) || format != QLatin1String( "ttf" ) )
3963 return nullptr;
3964
3965 const QString fontFamily = name.mid( 6 );
3966
3967 double angle = 0.0;
3968 QString angleFunc;
3969 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
3970 {
3971 bool ok;
3972 const double d = angleFunc.toDouble( &ok );
3973 if ( ok )
3974 angle = d;
3975 }
3976
3977 QPointF offset;
3979
3980 double scaleFactor = 1.0;
3981 const QString uom = element.attribute( QStringLiteral( "uom" ) );
3982 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
3983 offset.setX( offset.x() * scaleFactor );
3984 offset.setY( offset.y() * scaleFactor );
3985 size = size * scaleFactor;
3986
3988 m->setOutputUnit( sldUnitSize );
3989 m->setAngle( angle );
3990 m->setOffset( offset );
3991 return m;
3992}
3993
3994void QgsFontMarkerSymbolLayer::resolveFonts( const QVariantMap &properties, const QgsReadWriteContext &context )
3995{
3996 const QString fontFamily = properties.value( QStringLiteral( "font" ), DEFAULT_FONTMARKER_FONT ).toString();
3997 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( fontFamily );
3998 QString matched;
3999 if ( !QgsFontUtils::fontFamilyMatchOnSystem( processedFamily )
4000 && !QgsApplication::fontManager()->tryToDownloadFontFamily( processedFamily, matched ) )
4001 {
4002 context.pushMessage( QObject::tr( "Font “%1” not available on system" ).arg( processedFamily ) );
4003 }
4004}
4005
4007{
4008 QMap<QString, QgsProperty>::iterator it = mParameters.begin();
4009 for ( ; it != mParameters.end(); ++it )
4010 it.value().prepare( context.renderContext().expressionContext() );
4011
4013}
4014
4015
4017{
4018 QSet<QString> attrs = QgsMarkerSymbolLayer::usedAttributes( context );
4019
4020 QMap<QString, QgsProperty>::const_iterator it = mParameters.constBegin();
4021 for ( ; it != mParameters.constEnd(); ++it )
4022 {
4023 attrs.unite( it.value().referencedFields( context.expressionContext(), true ) );
4024 }
4025
4026 return attrs;
4027}
4028
4029//
4030// QgsAnimatedMarkerSymbolLayer
4031//
4032
4033QgsAnimatedMarkerSymbolLayer::QgsAnimatedMarkerSymbolLayer( const QString &path, double size, double angle )
4034 : QgsRasterMarkerSymbolLayer( path, size, angle )
4035{
4036
4037}
4038
4040
4041QgsSymbolLayer *QgsAnimatedMarkerSymbolLayer::create( const QVariantMap &properties ) // cppcheck-suppress duplInheritedMember
4042{
4043 QString path;
4046
4047 if ( properties.contains( QStringLiteral( "imageFile" ) ) )
4048 path = properties[QStringLiteral( "imageFile" )].toString();
4049 if ( properties.contains( QStringLiteral( "size" ) ) )
4050 size = properties[QStringLiteral( "size" )].toDouble();
4051 if ( properties.contains( QStringLiteral( "angle" ) ) )
4052 angle = properties[QStringLiteral( "angle" )].toDouble();
4053
4054 std::unique_ptr< QgsAnimatedMarkerSymbolLayer > m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( path, size, angle );
4055 m->setFrameRate( properties.value( QStringLiteral( "frameRate" ), QStringLiteral( "10" ) ).toDouble() );
4056
4057 m->setCommonProperties( properties );
4058 return m.release();
4059}
4060
4062{
4063 return QStringLiteral( "AnimatedMarker" );
4064}
4065
4067{
4068 QVariantMap res = QgsRasterMarkerSymbolLayer::properties();
4069 res.insert( QStringLiteral( "frameRate" ), mFrameRateFps );
4070 return res;
4071}
4072
4074{
4075 std::unique_ptr< QgsAnimatedMarkerSymbolLayer > m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( mPath, mSize, mAngle );
4076 m->setFrameRate( mFrameRateFps );
4077 copyCommonProperties( m.get() );
4078 return m.release();
4079}
4080
4082{
4084
4085 mPreparedPaths.clear();
4087 {
4089 mStaticPath = true;
4090 }
4091 else
4092 {
4093 mStaticPath = false;
4094 }
4095}
4096
4097QImage QgsAnimatedMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
4098{
4099 if ( !mStaticPath && !mPreparedPaths.contains( path ) )
4100 {
4102 mPreparedPaths.insert( path );
4103 }
4104
4105 const long long mapFrameNumber = context.currentFrame();
4107 const double markerAnimationDuration = totalFrameCount / mFrameRateFps;
4108
4109 double animationTimeSeconds = 0;
4110 if ( mapFrameNumber >= 0 && context.frameRate() > 0 )
4111 {
4112 // render is part of an animation, so we base the calculated frame on that
4113 animationTimeSeconds = mapFrameNumber / context.frameRate();
4114 }
4115 else
4116 {
4117 // render is outside of animation, so base the calculated frame on the current epoch
4118 animationTimeSeconds = QDateTime::currentMSecsSinceEpoch() / 1000.0;
4119 }
4120
4121 const double markerAnimationProgressSeconds = std::fmod( animationTimeSeconds, markerAnimationDuration );
4122 const int movieFrame = static_cast< int >( std::floor( markerAnimationProgressSeconds * mFrameRateFps ) );
4123
4124 bool cached = false;
4125 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking, 96, movieFrame );
4126}
4127
@ 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:821
MarkerShape
Marker shapes.
Definition qgis.h:2920
@ 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)
RenderUnit
Rendering size units.
Definition qgis.h:4910
@ Percentage
Percentage of another measurement (e.g., canvas size, feature size)
@ Millimeters
Millimeters.
@ Unknown
Mixed or unknown units.
@ MapUnits
Map units.
@ MetersInMapUnits
Meters value as Map units.
@ RenderingSubSymbol
Set whenever a sub-symbol of a parent symbol is currently being rendered. Can be used during symbol a...
@ RenderSymbolPreview
The render is for a symbol preview only and map based properties may not be available,...
@ RenderBlocking
Render and load remote sources in the same thread to ensure rendering remote sources (svg and images)...
@ Fill
Fill symbol.
QColor valueAsColor(int key, const QgsExpressionContext &context, const QColor &defaultColor=QColor(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a color.
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double.
QString valueAsString(int key, const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a string.
Animated marker symbol layer class.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsAnimatedMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
~QgsAnimatedMarkerSymbolLayer() override
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
QImage fetchImage(QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity) const override
Fetches the image to render.
QgsAnimatedMarkerSymbolLayer(const QString &path=QString(), double size=DEFAULT_RASTERMARKER_SIZE, double angle=DEFAULT_RASTERMARKER_ANGLE)
Constructor for animated marker symbol layer using the specified source image path.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates an animated marker symbol layer from a string map of properties.
QString layerType() const override
Returns a string that represents this layer type.
static QgsImageCache * imageCache()
Returns the application's image cache, used for caching resampled versions of raster images.
static QgsSvgCache * svgCache()
Returns the application's SVG cache, used for caching SVG images and handling parameter replacement w...
static QgsFontManager * fontManager()
Returns the application font manager, which manages available fonts and font installation for the QGI...
static QColor colorFromString(const QString &string)
Decodes a string into a color value.
static QString colorToString(const QColor &color)
Encodes a color into a string value.
Exports QGIS layers to the DXF format.
void writeFilledCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius)
Write filled circle (as hatch)
void writeCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius, const QString &lineStyleName, double width)
Write circle (as polyline)
static double mapUnitScaleFactor(double scale, Qgis::RenderUnit symbolUnits, Qgis::DistanceUnit mapUnits, double mapUnitsPerPixel=1.0)
Returns scale factor for conversion to map units.
void writeLine(const QgsPoint &pt1, const QgsPoint &pt2, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Write line (as a polyline)
void writePolygon(const QgsRingSequence &polygon, const QString &layer, const QString &hatchPattern, const QColor &color)
Draw dxf filled polygon (HATCH)
Qgis::DistanceUnit mapUnits() const
Retrieve map units.
double symbologyScale() const
Returns the reference scale for output.
void clipValueToMapUnitScale(double &value, const QgsMapUnitScale &scale, double pixelToMMFactor) const
Clips value to scale minimum/maximum.
void writePolyline(const QgsPointSequence &line, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Draw dxf primitives (LWPOLYLINE)
A paint device for drawing into dxf files.
void setShift(QPointF shift)
void setLayer(const QString &layer)
void setOutputSize(const QRectF &r)
void setDrawingSize(QSizeF size)
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
QgsGeometry geometry
Definition qgsfeature.h:69
bool hasGeometry() const
Returns true if the feature has an associated geometry.
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
static QgsFillSymbol * createSimple(const QVariantMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties.
Filled marker symbol layer, consisting of a shape which is rendered using a QgsFillSymbol.
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QColor color() const override
Returns the "representative" color of the symbol layer.
QgsFilledMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
bool hasDataDefinedProperties() const override
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QgsSymbol * subSymbol() override
Returns the symbol's sub symbol, if present.
~QgsFilledMarkerSymbolLayer() override
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer's subsymbol. takes ownership of the passed symbol.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
void setColor(const QColor &c) override
Sets the "representative" color for the symbol layer.
QString layerType() const override
Returns a string that represents this layer type.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsFilledMarkerSymbolLayer.
QgsFilledMarkerSymbolLayer(Qgis::MarkerShape shape=Qgis::MarkerShape::Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructor for QgsFilledMarkerSymbolLayer.
QString processFontFamilyName(const QString &name) const
Processes a font family name, applying any matching fontFamilyReplacements() to the name.
void setStrokeWidthUnit(Qgis::RenderUnit unit)
Sets the stroke width unit.
~QgsFontMarkerSymbolLayer() override
void setStrokeColor(const QColor &color) override
Sets the stroke color for the symbol layer.
QgsFontMarkerSymbolLayer(const QString &fontFamily=DEFAULT_FONTMARKER_FONT, QString chr=DEFAULT_FONTMARKER_CHR, double pointSize=DEFAULT_FONTMARKER_SIZE, const QColor &color=DEFAULT_FONTMARKER_COLOR, double angle=DEFAULT_FONTMARKER_ANGLE)
Constructs a font marker symbol layer.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
Sets the stroke width map unit scale.
double strokeWidth() const
Returns the marker's stroke width.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
void setFontStyle(const QString &style)
Sets the font style for the font which will be used to render the point.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QString fontStyle() const
Returns the font style for the associated font which will be used to render the point.
QString fontFamily() const
Returns the font family name for the associated font which will be used to render the point.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
void writeSldMarker(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Writes the symbol layer definition as a SLD XML element.
void setStrokeWidth(double width)
Set's the marker's stroke width.
static void resolveFonts(const QVariantMap &properties, const QgsReadWriteContext &context)
Resolves fonts from a properties map, raising warnings in the specified context if the required fonts...
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
void setPenJoinStyle(Qt::PenJoinStyle style)
Sets the stroke join style.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsFontMarkerSymbolLayer from an SLD XML element.
QgsFontMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
QString layerType() const override
Returns a string that represents this layer type.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsFontMarkerSymbolLayer from a property map (see properties())
Qgis::SymbolLayerFlags flags() const override
Returns flags which control the symbol layer's behavior.
static QString translateNamedStyle(const QString &namedStyle)
Returns the localized named style of a font, if such a translation is available.
static QFont createFont(const QString &family, int pointSize=-1, int weight=-1, bool italic=false)
Creates a font with the specified family.
static bool fontFamilyMatchOnSystem(const QString &family, QString *chosen=nullptr, bool *match=nullptr)
Check whether font family is on system.
static bool updateFontViaStyle(QFont &f, const QString &fontstyle, bool fallback=false)
Updates font with named style and retain all font properties.
static void setFontFamily(QFont &font, const QString &family)
Sets the family for a font object.
Qgis::GeometryType type
QSize originalSize(const QString &path, bool blocking=false) const
Returns the original size (in pixels) of the image at the specified path.
int totalFrameCount(const QString &path, bool blocking=false)
Returns the total frame count of the image at the specified path.
QImage pathAsImage(const QString &path, const QSize size, const bool keepAspectRatio, const double opacity, bool &fitsInCache, bool blocking=false, double targetDpi=96, int frameNumber=-1, bool *isMissing=nullptr)
Returns the specified path rendered as an image.
void prepareAnimation(const QString &path)
Prepares for optimized retrieval of frames for the animation at the given path.
static void overlayColor(QImage &image, const QColor &color)
Overlays a color onto an image.
Perform transforms between map coordinates and device coordinates.
double mapUnitsPerPixel() const
Returns the current map units per pixel.
double mapRotation() const
Returns the current map rotation in degrees (clockwise).
Struct for storing maximum and minimum scales for measurements in map units.
Abstract base class for marker symbol layers.
double mSize
Marker size.
Qgis::RenderUnit mOffsetUnit
Offset units.
QPointF offset() const
Returns the marker's offset, which is the horizontal and vertical displacement which the rendered mar...
double mLineAngle
Line rotation angle (see setLineAngle() for details)
HorizontalAnchorPoint
Symbol horizontal anchor points.
void setOffsetUnit(Qgis::RenderUnit unit)
Sets the units for the symbol's offset.
void setAngle(double angle)
Sets the rotation angle for the marker.
Qgis::ScaleMethod scaleMethod() const
Returns the method to use for scaling the marker's size.
Qgis::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
void setVerticalAnchorPoint(VerticalAnchorPoint v)
Sets the vertical anchor point for positioning the symbol.
QPointF mOffset
Marker offset.
void setHorizontalAnchorPoint(HorizontalAnchorPoint h)
Sets the horizontal anchor point for positioning the symbol.
QgsMapUnitScale mapUnitScale() const override
void setOffset(QPointF offset)
Sets the marker's offset, which is the horizontal and vertical displacement which the rendered marker...
void setSizeMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol's size.
double size() const
Returns the symbol size.
QgsMapUnitScale mOffsetMapUnitScale
Offset map unit scale.
HorizontalAnchorPoint mHorizontalAnchorPoint
Horizontal anchor point.
static QPointF _rotatedOffset(QPointF offset, double angle)
Adjusts a marker offset to account for rotation.
Qgis::ScaleMethod mScaleMethod
Marker size scaling method.
QgsMapUnitScale mSizeMapUnitScale
Marker size map unit scale.
Qgis::RenderUnit mSizeUnit
Marker size unit.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units for the symbol's size.
VerticalAnchorPoint
Symbol vertical anchor points.
void markerOffset(QgsSymbolRenderContext &context, double &offsetX, double &offsetY) const
Calculates the required marker offset, including both the symbol offset and any displacement required...
VerticalAnchorPoint mVerticalAnchorPoint
Vertical anchor point.
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol's offset.
double mAngle
Marker rotation angle, in degrees clockwise from north.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
double angle() const
Returns the rotation angle for the marker, in degrees clockwise from north.
void setMapUnitScale(const QgsMapUnitScale &scale) override
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.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsMapUnitScale mapUnitScale() const override
void copyCommonProperties(QgsRasterMarkerSymbolLayer *other) const
Copies common properties to another layer.
void setOpacity(double opacity)
Set the marker opacity.
QString path() const
Returns the marker raster image path.
double calculateAspectRatio(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio) const
Calculates the marker aspect ratio between width and height.
QgsRasterMarkerSymbolLayer(const QString &path=QString(), double size=DEFAULT_SVGMARKER_SIZE, double angle=DEFAULT_SVGMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructs raster marker symbol layer with picture from given absolute path to a raster image file.
void setPath(const QString &path)
Set the marker raster image path.
virtual QImage fetchImage(QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity) const
Fetches the image to render.
double defaultAspectRatio() const
Returns the default marker aspect ratio between width and height, 0 if not yet calculated.
Qgis::SymbolLayerFlags flags() const override
Returns flags which control the symbol layer's behavior.
void setFixedAspectRatio(double ratio)
Set the marker aspect ratio between width and height to be used in rendering, if the value set is low...
void setMapUnitScale(const QgsMapUnitScale &scale) override
void setCommonProperties(const QVariantMap &properties)
Sets common class properties from a properties map.
QgsRasterMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
bool preservedAspectRatio() const
Returns the preserved aspect ratio value, true if fixed aspect ratio has been lower or equal to 0.
static void resolvePaths(QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving)
Turns relative paths in properties map to absolute when reading and vice versa when writing.
~QgsRasterMarkerSymbolLayer() override
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a raster marker symbol layer from a string map of properties.
double mOpacity
The marker default opacity.
double updateDefaultAspectRatio()
Calculates the default marker aspect ratio between width and height.
double mDefaultAspectRatio
The marker default aspect ratio.
bool setPreservedAspectRatio(bool par)
Set preserved the marker aspect ratio between width and height.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QString layerType() const override
Returns a string that represents this layer type.
double opacity() const
Returns the marker opacity.
The class is used as a container of context for various read/write operations on other objects.
void pushMessage(const QString &message, Qgis::MessageLevel level=Qgis::MessageLevel::Warning) const
Append a message to the context.
Contains information about the context of a rendering operation.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
double convertToPainterUnits(double size, Qgis::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QPainter * painter()
Returns the destination QPainter for the render operation.
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
QgsExpressionContext & expressionContext()
Gets the expression context.
long long currentFrame() const
Returns the current frame number of the map (in frames per second), for maps which are part of an ani...
float devicePixelRatio() const
Returns the device pixel ratio.
void setFlag(Qgis::RenderContextFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
double frameRate() const
Returns the frame rate of the map, for maps which are part of an animation.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
QColor selectionColor() const
Returns the color to use when rendering selected features.
Qgis::RenderContextFlags flags() const
Returns combination of flags used for rendering.
const QgsPathResolver & pathResolver() const
Returns the path resolver for conversion between relative and absolute paths during rendering operati...
Scoped object for saving and restoring a QPainter object's state.
Abstract base class for simple marker symbol layers.
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
void calculateOffsetAndRotation(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle) const
Calculates the marker offset and rotation.
Qgis::MarkerShape mShape
Symbol shape.
QPainterPath mPath
Painter path representing shape. If mPolygon is empty then the shape is stored in mPath.
bool shapeToPolygon(Qgis::MarkerShape shape, QPolygonF &polygon) const
Creates a polygon representing the specified shape.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
static QList< Qgis::MarkerShape > availableShapes()
Returns a list of all available shape types.
~QgsSimpleMarkerSymbolLayerBase() override
static bool shapeIsFilled(Qgis::MarkerShape shape)
Returns true if a symbol shape has a fill.
QPolygonF mPolygon
Polygon of points in shape. If polygon is empty then shape is using mPath.
Qgis::MarkerShape shape() const
Returns the shape for the rendered marker symbol.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
QgsSimpleMarkerSymbolLayerBase(Qgis::MarkerShape shape=Qgis::MarkerShape::Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructor for QgsSimpleMarkerSymbolLayerBase.
static QString encodeShape(Qgis::MarkerShape shape)
Encodes a shape to its string representation.
double calculateSize(QgsSymbolRenderContext &context, bool &hasDataDefinedSize) const
Calculates the desired size of the marker, considering data defined size overrides.
static Qgis::MarkerShape decodeShape(const QString &name, bool *ok=nullptr)
Attempts to decode a string representation of a shape name to the corresponding shape.
bool prepareMarkerPath(Qgis::MarkerShape symbol)
Prepares the layer for drawing the specified shape (QPainterPath version)
bool prepareMarkerShape(Qgis::MarkerShape shape)
Prepares the layer for drawing the specified shape (QPolygonF version)
Simple marker symbol layer, consisting of a rendered shape with solid fill color and an stroke.
QPen mSelPen
QPen to use as stroke of selected symbols.
void setColor(const QColor &color) override
Sets the "representative" color for the symbol layer.
QImage mSelCache
Cached image of selected marker, if using cached version.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QImage mCache
Cached image of marker, if using cached version.
QBrush mSelBrush
QBrush to use as fill of selected symbols.
void setFillColor(const QColor &color) override
Sets the fill color for the symbol layer.
Qt::PenJoinStyle penJoinStyle() const
Returns the marker's stroke join style (e.g., miter, bevel, etc).
void drawMarker(QPainter *p, QgsSymbolRenderContext &context)
Draws the marker shape in the specified painter.
QPen mPen
QPen corresponding to marker's stroke style.
Qgis::RenderUnit mStrokeWidthUnit
Stroke width units.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsSimpleMarkerSymbolLayer.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
void setMapUnitScale(const QgsMapUnitScale &scale) override
Qgis::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
void setStrokeWidthUnit(Qgis::RenderUnit u)
Sets the unit for the width of the marker's stroke.
QColor color() const override
Returns the "representative" color of the symbol layer.
QgsMapUnitScale mapUnitScale() const override
Qt::PenStyle mStrokeStyle
Stroke style.
QgsSimpleMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsSimpleMarkerSymbolLayer from an SLD XML element.
~QgsSimpleMarkerSymbolLayer() override
Qt::PenCapStyle mPenCapStyle
Stroke pen cap style.
QString layerType() const override
Returns a string that represents this layer type.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
Sets the map scale for the width of the marker's stroke.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
void setStrokeStyle(Qt::PenStyle strokeStyle)
Sets the marker's stroke style (e.g., solid, dashed, etc)
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QColor fillColor() const override
Returns the fill color for the symbol layer.
QColor strokeColor() const override
Returns the marker's stroke color.
QBrush mBrush
QBrush corresponding to marker's fill style.
void setStrokeWidth(double w)
Sets the width of the marker's stroke.
void setStrokeColor(const QColor &color) override
Sets the marker's stroke color.
Qt::PenStyle strokeStyle() const
Returns the marker's stroke style (e.g., solid, dashed, etc)
bool mUsingCache
true if using cached images of markers for drawing.
Qgis::SymbolLayerFlags flags() const override
Returns flags which control the symbol layer's behavior.
void setPenCapStyle(Qt::PenCapStyle style)
Sets the marker's stroke cap style (e.g., flat, round, etc).
bool writeDxf(QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift=QPointF(0.0, 0.0)) const override
write as DXF
void writeSldMarker(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Writes the symbol layer definition as a SLD XML element.
static const int MAXIMUM_CACHE_WIDTH
Maximum width/height of cache image.
QString ogrFeatureStyle(double mmScaleFactor, double mapUnitScaleFactor) const override
bool prepareCache(QgsSymbolRenderContext &context)
Prepares cache image.
QgsMapUnitScale mStrokeWidthMapUnitScale
Stroke width map unit scale.
QgsSimpleMarkerSymbolLayer(Qgis::MarkerShape shape=Qgis::MarkerShape::Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD, const QColor &color=DEFAULT_SIMPLEMARKER_COLOR, const QColor &strokeColor=DEFAULT_SIMPLEMARKER_BORDERCOLOR, Qt::PenJoinStyle penJoinStyle=DEFAULT_SIMPLEMARKER_JOINSTYLE)
Constructor for QgsSimpleMarkerSymbolLayer.
double strokeWidth() const
Returns the width of the marker's stroke.
Qt::PenJoinStyle mPenJoinStyle
Stroke pen join style.
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
QSizeF svgViewboxSize(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >())
Calculates the viewbox size of a (possibly cached) SVG file.
QPicture svgAsPicture(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool forceVectorOutput=false, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >())
Returns an SVG drawing as a QPicture.
void containsParams(const QString &path, bool &hasFillParam, QColor &defaultFillColor, bool &hasStrokeParam, QColor &defaultStrokeColor, bool &hasStrokeWidthParam, double &defaultStrokeWidth, bool blocking=false) const
Tests if an SVG file contains parameters for fill, stroke color, stroke width.
QImage svgAsImage(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool &fitsInCache, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >())
Returns an SVG drawing as a QImage.
QByteArray svgContent(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >(), bool *isMissingImage=nullptr)
Gets the SVG content corresponding to the given path.
QgsSvgMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
QColor fillColor() const override
Returns the fill color for the symbol layer.
QgsMapUnitScale mapUnitScale() const override
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates the symbol.
double mDefaultAspectRatio
The marker default aspect ratio.
QString layerType() const override
Returns a string that represents this layer type.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
QString path() const
Returns the marker SVG path.
Qgis::SymbolLayerFlags flags() const override
Returns flags which control the symbol layer's behavior.
bool preservedAspectRatio() const
Returns the preserved aspect ratio value, true if fixed aspect ratio has been lower or equal to 0.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void prepareExpressions(const QgsSymbolRenderContext &context) override
Prepares all data defined property expressions for evaluation.
QMap< QString, QgsProperty > mParameters
bool setPreservedAspectRatio(bool par)
Set preserved the marker aspect ratio between width and height.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
Qgis::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
double calculateAspectRatio(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio) const
Calculates the marker aspect ratio between width and height.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
static QgsSymbolLayer * createFromSld(QDomElement &element)
void setStrokeWidthUnit(Qgis::RenderUnit unit)
Sets the units for the stroke width.
void setStrokeColor(const QColor &c) override
Sets the stroke color for the symbol layer.
void setMapUnitScale(const QgsMapUnitScale &scale) override
double updateDefaultAspectRatio()
Calculates the default marker aspect ratio between width and height.
QColor strokeColor() const override
Returns the stroke color for the symbol layer.
void setFillColor(const QColor &color) override
Sets the fill color for the symbol layer.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
QgsSvgMarkerSymbolLayer(const QString &path, double size=DEFAULT_SVGMARKER_SIZE, double angle=DEFAULT_SVGMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructs SVG marker symbol layer with picture from given absolute path to a SVG file.
void writeSldMarker(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Writes the symbol layer definition as a SLD XML element.
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
~QgsSvgMarkerSymbolLayer() override
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
static void resolvePaths(QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving)
Turns relative paths in properties map to absolute when reading and vice versa when writing.
QMap< QString, QgsProperty > parameters() const
Returns the dynamic SVG parameters.
QgsMapUnitScale mStrokeWidthMapUnitScale
void setParameters(const QMap< QString, QgsProperty > &parameters)
Sets the dynamic SVG parameters.
double mFixedAspectRatio
The marker fixed aspect ratio.
bool writeDxf(QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift=QPointF(0.0, 0.0)) const override
write as DXF
void setFixedAspectRatio(double ratio)
Set the marker aspect ratio between width and height to be used in rendering, if the value set is low...
void setPath(const QString &path)
Set the marker SVG path.
static bool externalMarkerFromSld(QDomElement &element, QString &path, QString &format, int &markIndex, QColor &color, double &size)
static bool rotationFromSldElement(QDomElement &element, QString &rotationFunc)
static QString encodePenStyle(Qt::PenStyle style)
static Qt::PenJoinStyle decodePenJoinStyle(const QString &str)
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
static QgsStringMap evaluatePropertiesMap(const QMap< QString, QgsProperty > &propertiesMap, const QgsExpressionContext &context)
Evaluates a map of properties using the given context and returns a variant map with evaluated expres...
static bool displacementFromSldElement(QDomElement &element, QPointF &offset)
static QString svgSymbolPathToName(const QString &path, const QgsPathResolver &pathResolver)
Determines an SVG symbol's name from its path.
static QPointF toPoint(const QVariant &value, bool *ok=nullptr)
Converts a value to a point.
static void multiplyImageOpacity(QImage *image, qreal opacity)
Multiplies opacity of image pixel values with a (global) transparency value.
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
static double rescaleUom(double size, Qgis::RenderUnit unit, const QVariantMap &props)
Rescales the given size based on the uomScale found in the props, if any is found,...
static Qt::PenCapStyle decodePenCapStyle(const QString &str)
static bool externalGraphicFromSld(QDomElement &element, QString &path, QString &mime, QColor &color, double &size)
static void parametricSvgToSld(QDomDocument &doc, QDomElement &graphicElem, const QString &path, const QColor &fillColor, double size, const QColor &strokeColor, double strokeWidth)
Encodes a reference to a parametric SVG into SLD, as a succession of parametric SVG using URL paramet...
static Qgis::ScaleMethod decodeScaleMethod(const QString &str)
Decodes a symbol scale method from a string.
static QString encodePenCapStyle(Qt::PenCapStyle style)
static void externalMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &path, const QString &format, int *markIndex=nullptr, const QColor &color=QColor(), double size=-1)
static bool wellKnownMarkerFromSld(QDomElement &element, QString &name, QColor &color, QColor &strokeColor, Qt::PenStyle &strokeStyle, double &strokeWidth, double &size)
static void createDisplacementElement(QDomDocument &doc, QDomElement &element, QPointF offset)
static QString svgSymbolNameToPath(const QString &name, const QgsPathResolver &pathResolver)
Determines an SVG symbol's path from its name.
static QString encodeColor(const QColor &color)
static Qgis::RenderUnit decodeSldUom(const QString &str, double *scaleFactor=nullptr)
Decodes a SLD unit of measure string to a render unit.
static double estimateMaxSymbolBleed(QgsSymbol *symbol, const QgsRenderContext &context)
Returns the maximum estimated bleed for the symbol.
static void wellKnownMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &name, const QColor &color, const QColor &strokeColor, Qt::PenStyle strokeStyle, double strokeWidth=-1, double size=-1)
static QString encodeScaleMethod(Qgis::ScaleMethod scaleMethod)
Encodes a symbol scale method to a string.
static Qt::PenStyle decodePenStyle(const QString &str)
static void createRotationElement(QDomDocument &doc, QDomElement &element, const QString &rotationFunc)
static QString encodePoint(QPointF point)
Encodes a QPointF to a string.
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
static QPointF decodePoint(const QString &string)
Decodes a QSizeF from a string.
bool shouldRenderUsingSelectionColor(const QgsSymbolRenderContext &context) const
Returns true if the symbol layer should be rendered using the selection color from the render context...
static const bool SELECTION_IS_OPAQUE
Whether styles for selected features ignore symbol alpha.
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns the set of attributes referenced by the layer.
void copyDataDefinedProperties(QgsSymbolLayer *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
@ StrokeStyle
Stroke style (eg solid, dashed)
@ Name
Name, eg shape name for simple markers.
@ Character
Character, eg for font marker symbol layers.
@ StrokeColor
Stroke color.
@ CapStyle
Line cap style.
@ JoinStyle
Line join style.
@ StrokeWidth
Stroke width.
@ FontFamily
Font family.
@ Offset
Symbol offset.
@ Height
Symbol height.
void restoreOldDataDefinedProperties(const QVariantMap &stringMap)
Restores older data defined properties from string map.
virtual void startRender(QgsSymbolRenderContext &context)=0
Called before a set of rendering operations commences on the supplied render context.
virtual void prepareExpressions(const QgsSymbolRenderContext &context)
Prepares all data defined property expressions for evaluation.
virtual QColor color() const
Returns the "representative" color of the symbol layer.
virtual void setOutputUnit(Qgis::RenderUnit unit)
Sets the units to use for sizes and widths within the symbol layer.
void copyPaintEffect(QgsSymbolLayer *destLayer) const
Copies paint effect of this layer to another symbol layer.
virtual Qgis::SymbolLayerFlags flags() const
Returns flags which control the symbol layer's behavior.
QgsPropertyCollection mDataDefinedProperties
virtual bool hasDataDefinedProperties() const
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
const QgsFeature * feature() const
Returns the current feature being rendered.
QgsFields fields() const
Fields of the layer.
Qgis::SymbolRenderHints renderHints() const
Returns the rendering hint flags for the symbol.
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for data defined symbology.
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:6066
QMap< QString, QString > QgsStringMap
Definition qgis.h:6604
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
#define DEG2RAD(x)
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
#define QgsDebugError(str)
Definition qgslogger.h:38
#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