QGIS API Documentation 3.41.0-Master (88383c3d16f)
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 << QPointF( inner_r * std::sin( DEG2RAD( 315.0 ) ), - inner_r * std::cos( DEG2RAD( 315.0 ) ) );
668 return true;
669 }
670
672 {
673 const double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
674
675 polygon << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ) // 324
676 << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288 ) ) ) // 288
677 << QPointF( inner_r * std::sin( DEG2RAD( 252.0 ) ), - inner_r * std::cos( DEG2RAD( 252.0 ) ) ) // 252
678 << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) ) // 216
679 << QPointF( 0, inner_r ) // 180
680 << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) ) // 144
681 << QPointF( inner_r * std::sin( DEG2RAD( 108.0 ) ), - inner_r * std::cos( DEG2RAD( 108.0 ) ) ) // 108
682 << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) ) // 72
683 << QPointF( inner_r * std::sin( DEG2RAD( 36.0 ) ), - inner_r * std::cos( DEG2RAD( 36.0 ) ) ) // 36
684 << QPointF( 0, -1 )
685 << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ); // 324; // 0
686 return true;
687 }
688
690 polygon << QPointF( 0, -1 )
691 << QPointF( 0.5, -0.5 )
692 << QPointF( 0.25, -0.5 )
693 << QPointF( 0.25, 1 )
694 << QPointF( -0.25, 1 )
695 << QPointF( -0.25, -0.5 )
696 << QPointF( -0.5, -0.5 )
697 << QPointF( 0, -1 );
698 return true;
699
701 polygon << QPointF( 0, 0 ) << QPointF( -1, 1 ) << QPointF( -1, -1 ) << QPointF( 0, 0 );
702 return true;
703
705 polygon << QPointF( -1, -0.2 )
706 << QPointF( -1, -0.2 )
707 << QPointF( -1, 0.2 )
708 << QPointF( -0.2, 0.2 )
709 << QPointF( -0.2, 1 )
710 << QPointF( 0.2, 1 )
711 << QPointF( 0.2, 0.2 )
712 << QPointF( 1, 0.2 )
713 << QPointF( 1, -0.2 )
714 << QPointF( 0.2, -0.2 )
715 << QPointF( 0.2, -1 )
716 << QPointF( -0.2, -1 )
717 << QPointF( -0.2, -0.2 )
718 << QPointF( -1, -0.2 );
719 return true;
720
722 {
723 static constexpr double THICKNESS = 0.3;
724 static constexpr double HALF_THICKNESS = THICKNESS / 2.0;
725 static constexpr double INTERSECTION_POINT = THICKNESS / M_SQRT2;
726 static constexpr double DIAGONAL1 = M_SQRT1_2 - INTERSECTION_POINT * 0.5;
727 static constexpr double DIAGONAL2 = M_SQRT1_2 + INTERSECTION_POINT * 0.5;
728
729 polygon << QPointF( -HALF_THICKNESS, -1 )
730 << QPointF( HALF_THICKNESS, -1 )
731 << QPointF( HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
732 << QPointF( DIAGONAL1, -DIAGONAL2 )
733 << QPointF( DIAGONAL2, -DIAGONAL1 )
734 << QPointF( HALF_THICKNESS + INTERSECTION_POINT, -HALF_THICKNESS )
735 << QPointF( 1, -HALF_THICKNESS )
736 << QPointF( 1, HALF_THICKNESS )
737 << QPointF( HALF_THICKNESS + INTERSECTION_POINT, HALF_THICKNESS )
738 << QPointF( DIAGONAL2, DIAGONAL1 )
739 << QPointF( DIAGONAL1, DIAGONAL2 )
740 << QPointF( HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
741 << QPointF( HALF_THICKNESS, 1 )
742 << QPointF( -HALF_THICKNESS, 1 )
743 << QPointF( -HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
744 << QPointF( -DIAGONAL1, DIAGONAL2 )
745 << QPointF( -DIAGONAL2, DIAGONAL1 )
746 << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, HALF_THICKNESS )
747 << QPointF( -1, HALF_THICKNESS )
748 << QPointF( -1, -HALF_THICKNESS )
749 << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, -HALF_THICKNESS )
750 << QPointF( -DIAGONAL2, -DIAGONAL1 )
751 << QPointF( -DIAGONAL1, -DIAGONAL2 )
752 << QPointF( -HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
753 << QPointF( -HALF_THICKNESS, -1 );
754 return true;
755 }
756
770 return false;
771 }
772
773 return false;
774}
775
777{
778 mPath = QPainterPath();
779
780 switch ( symbol )
781 {
783
784 mPath.addEllipse( QRectF( -1, -1, 2, 2 ) ); // x,y,w,h
785 return true;
786
788 mPath.moveTo( -1, -1 );
789 mPath.addRoundedRect( -1, -1, 2, 2, 0.25, 0.25 );
790 return true;
791
793 mPath.arcTo( -1, -1, 2, 2, 0, 180 );
794 mPath.lineTo( 0, 0 );
795 return true;
796
798 mPath.arcTo( -1, -1, 2, 2, 90, 120 );
799 mPath.lineTo( 0, 0 );
800 return true;
801
803 mPath.arcTo( -1, -1, 2, 2, 90, 90 );
804 mPath.lineTo( 0, 0 );
805 return true;
806
808 mPath.moveTo( 1, 0 );
809 mPath.arcTo( -1, -1, 2, 2, 0, 180 );
810 return true;
811
813 mPath.moveTo( 0, -1 );
814 mPath.arcTo( -1, -1, 2, 2, 90, 120 );
815 return true;
816
818 mPath.moveTo( 0, -1 );
819 mPath.arcTo( -1, -1, 2, 2, 90, 90 );
820 return true;
821
823 mPath.moveTo( -1, 0 );
824 mPath.lineTo( 1, 0 ); // horizontal
825 mPath.moveTo( 0, -1 );
826 mPath.lineTo( 0, 1 ); // vertical
827 return true;
828
830 mPath.moveTo( -1, -1 );
831 mPath.lineTo( 1, 1 );
832 mPath.moveTo( 1, -1 );
833 mPath.lineTo( -1, 1 );
834 return true;
835
837 mPath.moveTo( 0, -1 );
838 mPath.lineTo( 0, 1 ); // vertical line
839 return true;
840
842 mPath.moveTo( -1, -1 );
843 mPath.lineTo( 0, 0 );
844 mPath.lineTo( -1, 1 );
845 return true;
846
848 mPath.moveTo( 0, 0.75 );
849 mPath.arcTo( 0, -1, 1, 1, -45, 210 );
850 mPath.arcTo( -1, -1, 1, 1, 15, 210 );
851 mPath.lineTo( 0, 0.75 );
852 return true;
853
878 return false;
879 }
880 return false;
881}
882
883double QgsSimpleMarkerSymbolLayerBase::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
884{
885 double scaledSize = mSize;
886
888 bool ok = true;
889 if ( hasDataDefinedSize )
890 {
893 mSize, &ok );
894 }
895
896 if ( hasDataDefinedSize && ok )
897 {
898 switch ( mScaleMethod )
899 {
901 scaledSize = std::sqrt( scaledSize );
902 break;
904 break;
905 }
906 }
907
908 return scaledSize;
909}
910
911void QgsSimpleMarkerSymbolLayerBase::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle ) const
912{
913 //offset
914 double offsetX = 0;
915 double offsetY = 0;
916 markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
917 offset = QPointF( offsetX, offsetY );
918
919 hasDataDefinedRotation = false;
920 //angle
921 bool ok = true;
924 {
927
928 // If the expression evaluation was not successful, fallback to static value
929 if ( !ok )
931
932 hasDataDefinedRotation = true;
933 }
934
935 hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || hasDataDefinedRotation;
936
937 if ( hasDataDefinedRotation )
938 {
939 // For non-point markers, "dataDefinedRotation" means following the
940 // shape (shape-data defined). For them, "field-data defined" does
941 // not work at all. TODO: if "field-data defined" ever gets implemented
942 // we'll need a way to distinguish here between the two, possibly
943 // using another flag in renderHints()
944 const QgsFeature *f = context.feature();
945 if ( f )
946 {
947 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
948 {
949 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
950 angle += m2p.mapRotation();
951 }
952 }
953 }
954
955 if ( angle )
957}
958
959
960//
961// QgsSimpleMarkerSymbolLayer
962//
963
964QgsSimpleMarkerSymbolLayer::QgsSimpleMarkerSymbolLayer( Qgis::MarkerShape shape, double size, double angle, Qgis::ScaleMethod scaleMethod, const QColor &color, const QColor &strokeColor, Qt::PenJoinStyle penJoinStyle )
965 : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
966 , mStrokeColor( strokeColor )
967 , mPenJoinStyle( penJoinStyle )
968{
969 mColor = color;
970}
971
973
975{
983
984 if ( props.contains( QStringLiteral( "name" ) ) )
985 {
986 shape = decodeShape( props[QStringLiteral( "name" )].toString() );
987 }
988 if ( props.contains( QStringLiteral( "color" ) ) )
989 color = QgsColorUtils::colorFromString( props[QStringLiteral( "color" )].toString() );
990 if ( props.contains( QStringLiteral( "color_border" ) ) )
991 {
992 //pre 2.5 projects use "color_border"
993 strokeColor = QgsColorUtils::colorFromString( props[QStringLiteral( "color_border" )].toString() );
994 }
995 else if ( props.contains( QStringLiteral( "outline_color" ) ) )
996 {
997 strokeColor = QgsColorUtils::colorFromString( props[QStringLiteral( "outline_color" )].toString() );
998 }
999 else if ( props.contains( QStringLiteral( "line_color" ) ) )
1000 {
1001 strokeColor = QgsColorUtils::colorFromString( props[QStringLiteral( "line_color" )].toString() );
1002 }
1003 if ( props.contains( QStringLiteral( "joinstyle" ) ) )
1004 {
1005 penJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() );
1006 }
1007 if ( props.contains( QStringLiteral( "size" ) ) )
1008 size = props[QStringLiteral( "size" )].toDouble();
1009 if ( props.contains( QStringLiteral( "angle" ) ) )
1010 angle = props[QStringLiteral( "angle" )].toDouble();
1011 if ( props.contains( QStringLiteral( "scale_method" ) ) )
1012 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1013
1015 if ( props.contains( QStringLiteral( "offset" ) ) )
1016 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1017 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1018 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1019 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1020 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1021 if ( props.contains( QStringLiteral( "size_unit" ) ) )
1022 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1023 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1024 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1025
1026 if ( props.contains( QStringLiteral( "outline_style" ) ) )
1027 {
1028 m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "outline_style" )].toString() ) );
1029 }
1030 else if ( props.contains( QStringLiteral( "line_style" ) ) )
1031 {
1032 m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "line_style" )].toString() ) );
1033 }
1034 if ( props.contains( QStringLiteral( "outline_width" ) ) )
1035 {
1036 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
1037 }
1038 else if ( props.contains( QStringLiteral( "line_width" ) ) )
1039 {
1040 m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
1041 }
1042 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
1043 {
1044 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
1045 }
1046 if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
1047 {
1048 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
1049 }
1050 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
1051 {
1052 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
1053 }
1054
1055 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1056 {
1057 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1058 }
1059 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1060 {
1061 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1062 }
1063
1064 if ( props.contains( QStringLiteral( "cap_style" ) ) )
1065 {
1066 m->setPenCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( props[QStringLiteral( "cap_style" )].toString() ) );
1067 }
1068
1070
1071 return m;
1072}
1073
1074
1076{
1077 return QStringLiteral( "SimpleMarker" );
1078}
1079
1084
1086{
1088
1089 QColor brushColor = mColor;
1090 QColor penColor = mStrokeColor;
1091
1092 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
1093 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
1094
1095 mBrush = QBrush( brushColor );
1096 mPen = QPen( penColor );
1097 mPen.setStyle( mStrokeStyle );
1098 mPen.setCapStyle( mPenCapStyle );
1099 mPen.setJoinStyle( mPenJoinStyle );
1101
1102 QColor selBrushColor = context.renderContext().selectionColor();
1103 QColor selPenColor = selBrushColor == mColor ? selBrushColor : mStrokeColor;
1104 if ( context.opacity() < 1 && !SELECTION_IS_OPAQUE )
1105 {
1106 selBrushColor.setAlphaF( context.opacity() );
1107 selPenColor.setAlphaF( context.opacity() );
1108 }
1109 mSelBrush = QBrush( selBrushColor );
1110 mSelPen = QPen( selPenColor );
1111 mSelPen.setStyle( mStrokeStyle );
1113
1115 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
1116
1117 // use caching only when:
1118 // - size, rotation, shape, color, stroke color is not data-defined
1119 // - drawing to screen (not printer)
1120 mUsingCache = !hasDataDefinedRotation && !hasDataDefinedSize && !context.forceVectorRendering()
1124
1125 if ( mUsingCache )
1126 mCachedOpacity = context.opacity();
1127
1128 if ( !shapeIsFilled( mShape ) )
1129 {
1130 // some markers can't be drawn as a polygon (circle, cross)
1131 // For these set the selected stroke color to the selected color
1132 mSelPen.setColor( selBrushColor );
1133 }
1134
1135
1136 if ( mUsingCache )
1137 {
1138 if ( !prepareCache( context ) )
1139 {
1140 mUsingCache = false;
1141 }
1142 }
1143 else
1144 {
1145 mCache = QImage();
1146 mSelCache = QImage();
1147 }
1148}
1149
1150
1152{
1153 double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
1154 const double deviceRatio = context.renderContext().devicePixelRatio();
1156 {
1157 // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
1158 // and clamp it to a reasonable range. It's the best we can do in this situation!
1159 scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
1160 }
1161
1162 // take into account angle (which is not data-defined otherwise cache wouldn't be used)
1163 if ( !qgsDoubleNear( mAngle, 0.0 ) )
1164 {
1165 scaledSize = ( std::abs( std::sin( mAngle * M_PI / 180 ) ) + std::abs( std::cos( mAngle * M_PI / 180 ) ) ) * scaledSize;
1166 }
1167 // calculate necessary image size for the cache
1168 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
1169 const int imageSize = ( static_cast< int >( scaledSize ) + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width
1170 const double center = imageSize / 2.0;
1171 if ( imageSize * deviceRatio > MAXIMUM_CACHE_WIDTH )
1172 {
1173 return false;
1174 }
1175
1176 mCache = QImage( QSize( imageSize * deviceRatio,
1177 imageSize * deviceRatio ), QImage::Format_ARGB32_Premultiplied );
1178 mCache.setDevicePixelRatio( context.renderContext().devicePixelRatio() );
1179 mCache.setDotsPerMeterX( std::round( context.renderContext().scaleFactor() * 1000 ) );
1180 mCache.setDotsPerMeterY( std::round( context.renderContext().scaleFactor() * 1000 ) );
1181 mCache.fill( 0 );
1182
1183 const bool needsBrush = shapeIsFilled( mShape );
1184
1185 QPainter p;
1186 p.begin( &mCache );
1187 p.setRenderHint( QPainter::Antialiasing );
1188 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1189 p.setPen( mPen );
1190 p.translate( QPointF( center, center ) );
1191 drawMarker( &p, context );
1192 p.end();
1193
1194 // Construct the selected version of the Cache
1195
1196 const QColor selColor = context.renderContext().selectionColor();
1197
1198 mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
1199 mSelCache.fill( 0 );
1200
1201 p.begin( &mSelCache );
1202 p.setRenderHint( QPainter::Antialiasing );
1203 p.setBrush( needsBrush ? mSelBrush : Qt::NoBrush );
1204 p.setPen( mSelPen );
1205 p.translate( QPointF( center, center ) );
1206 drawMarker( &p, context );
1207 p.end();
1208
1209 // Check that the selected version is different. If not, then re-render,
1210 // filling the background with the selection color and using the normal
1211 // colors for the symbol .. could be ugly!
1212
1213 if ( mSelCache == mCache )
1214 {
1215 p.begin( &mSelCache );
1216 p.setRenderHint( QPainter::Antialiasing );
1217 p.fillRect( 0, 0, imageSize, imageSize, selColor );
1218 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1219 p.setPen( mPen );
1220 p.translate( QPointF( center, center ) );
1221 drawMarker( &p, context );
1222 p.end();
1223 }
1224
1225 return true;
1226}
1227
1228void QgsSimpleMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
1229{
1230 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1231 //of the rendered point!
1232
1233 QPainter *p = context.renderContext().painter();
1234 if ( !p )
1235 {
1236 return;
1237 }
1238
1239 QColor brushColor = mColor;
1240 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
1241 mBrush.setColor( brushColor );
1242
1243 QColor penColor = mStrokeColor;
1244 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
1245 mPen.setColor( penColor );
1246
1247 bool ok = true;
1249 {
1252 if ( ok )
1253 {
1254 c.setAlphaF( c.alphaF() * context.opacity() );
1255 mBrush.setColor( c );
1256 }
1257 }
1259 {
1262 if ( ok )
1263 {
1264 c.setAlphaF( c.alphaF() * context.opacity() );
1265 mPen.setColor( c );
1266 mSelPen.setColor( c );
1267 }
1268 }
1270 {
1273 if ( ok )
1274 {
1277 }
1278 }
1280 {
1283 if ( ok )
1284 {
1287 }
1288 }
1290 {
1293 if ( ok )
1294 {
1295 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1296 mSelPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1297 }
1298 }
1300 {
1303 if ( ok )
1304 {
1305 mPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1306 mSelPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1307 }
1308 }
1309
1310 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
1311 if ( shapeIsFilled( shape ) )
1312 {
1313 p->setBrush( useSelectedColor ? mSelBrush : mBrush );
1314 }
1315 else
1316 {
1317 p->setBrush( Qt::NoBrush );
1318 }
1319 p->setPen( useSelectedColor ? mSelPen : mPen );
1320
1321 if ( !polygon.isEmpty() )
1322 p->drawPolygon( polygon );
1323 else
1324 p->drawPath( path );
1325}
1326
1328{
1329 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1330 //of the rendered point!
1331
1332 QPainter *p = context.renderContext().painter();
1333 if ( !p )
1334 {
1335 return;
1336 }
1337
1338 if ( mUsingCache && qgsDoubleNear( mCachedOpacity, context.opacity() ) )
1339 {
1340 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
1341 const QImage &img = useSelectedColor ? mSelCache : mCache;
1342 const double s = img.width() / img.devicePixelRatioF();
1343
1344 bool hasDataDefinedSize = false;
1345 const double scaledSize = calculateSize( context, hasDataDefinedSize );
1346
1347 bool hasDataDefinedRotation = false;
1348 QPointF offset;
1349 double angle = 0;
1350 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
1351
1352 p->drawImage( QRectF( point.x() - s / 2.0 + offset.x(),
1353 point.y() - s / 2.0 + offset.y(),
1354 s, s ), img );
1355 }
1356 else
1357 {
1359 }
1360}
1361
1363{
1364 QVariantMap map;
1365 map[QStringLiteral( "name" )] = encodeShape( mShape );
1366 map[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mColor );
1367 map[QStringLiteral( "outline_color" )] = QgsColorUtils::colorToString( mStrokeColor );
1368 map[QStringLiteral( "size" )] = QString::number( mSize );
1369 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1370 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1371 map[QStringLiteral( "angle" )] = QString::number( mAngle );
1372 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1373 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1374 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1375 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1376 map[QStringLiteral( "outline_style" )] = QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle );
1377 map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
1378 map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
1379 map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
1380 map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
1381 map[QStringLiteral( "cap_style" )] = QgsSymbolLayerUtils::encodePenCapStyle( mPenCapStyle );
1382 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1383 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1384 return map;
1385}
1386
1406
1407void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1408{
1409 // <Graphic>
1410 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
1411 element.appendChild( graphicElem );
1412
1414 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
1416
1417 // <Rotation>
1418 QString angleFunc;
1419
1421 {
1423 }
1424 else
1425 {
1426 bool ok;
1427 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
1428 if ( !ok )
1429 {
1430 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
1431 }
1432 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
1433 {
1434 angleFunc = QString::number( angle + mAngle );
1435 }
1436 }
1437
1438 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
1439
1440 // <Displacement>
1441 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
1443}
1444
1445QString QgsSimpleMarkerSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
1446{
1447 Q_UNUSED( mmScaleFactor )
1448 Q_UNUSED( mapUnitScaleFactor )
1449#if 0
1450 QString ogrType = "3"; //default is circle
1451 if ( mName == "square" )
1452 {
1453 ogrType = "5";
1454 }
1455 else if ( mName == "triangle" )
1456 {
1457 ogrType = "7";
1458 }
1459 else if ( mName == "star" )
1460 {
1461 ogrType = "9";
1462 }
1463 else if ( mName == "circle" )
1464 {
1465 ogrType = "3";
1466 }
1467 else if ( mName == "cross" )
1468 {
1469 ogrType = "0";
1470 }
1471 else if ( mName == "x" || mName == "cross2" )
1472 {
1473 ogrType = "1";
1474 }
1475 else if ( mName == "line" )
1476 {
1477 ogrType = "10";
1478 }
1479
1480 QString ogrString;
1481 ogrString.append( "SYMBOL(" );
1482 ogrString.append( "id:" );
1483 ogrString.append( '\"' );
1484 ogrString.append( "ogr-sym-" );
1485 ogrString.append( ogrType );
1486 ogrString.append( '\"' );
1487 ogrString.append( ",c:" );
1488 ogrString.append( mColor.name() );
1489 ogrString.append( ",o:" );
1490 ogrString.append( mStrokeColor.name() );
1491 ogrString.append( QString( ",s:%1mm" ).arg( mSize ) );
1492 ogrString.append( ')' );
1493 return ogrString;
1494#endif //0
1495
1496 QString ogrString;
1497 ogrString.append( "PEN(" );
1498 ogrString.append( "c:" );
1499 ogrString.append( mColor.name() );
1500 ogrString.append( ",w:" );
1501 ogrString.append( QString::number( mSize ) );
1502 ogrString.append( "mm" );
1503 ogrString.append( ")" );
1504 return ogrString;
1505}
1506
1508{
1509 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
1510
1511 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
1512 if ( graphicElem.isNull() )
1513 return nullptr;
1514
1515 QString name = QStringLiteral( "square" );
1516 QColor color, strokeColor;
1517 double strokeWidth, size;
1518 Qt::PenStyle strokeStyle;
1519
1521 return nullptr;
1522
1523 double angle = 0.0;
1524 QString angleFunc;
1525 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
1526 {
1527 bool ok;
1528 const double d = angleFunc.toDouble( &ok );
1529 if ( ok )
1530 angle = d;
1531 }
1532
1533 QPointF offset;
1535
1536 const Qgis::MarkerShape shape = decodeShape( name );
1537
1538 double scaleFactor = 1.0;
1539 const QString uom = element.attribute( QStringLiteral( "uom" ) );
1540 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
1541 size = size * scaleFactor;
1542 offset.setX( offset.x() * scaleFactor );
1543 offset.setY( offset.y() * scaleFactor );
1544
1546 m->setOutputUnit( sldUnitSize );
1547 m->setColor( color );
1549 m->setAngle( angle );
1550 m->setOffset( offset );
1553 return m;
1554}
1555
1557{
1558 Q_UNUSED( context )
1559
1560 if ( mPolygon.count() != 0 )
1561 {
1562 p->drawPolygon( mPolygon );
1563 }
1564 else
1565 {
1566 p->drawPath( mPath );
1567 }
1568}
1569
1570bool QgsSimpleMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
1571{
1572 //data defined size?
1573 double size = mSize;
1574
1575 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
1576
1577 //data defined size
1578 bool ok = true;
1579 if ( hasDataDefinedSize )
1580 {
1582
1583 if ( ok )
1584 {
1585 switch ( mScaleMethod )
1586 {
1588 size = std::sqrt( size );
1589 break;
1591 break;
1592 }
1593 }
1594 }
1595
1597 {
1598 size *= mmMapUnitScaleFactor;
1599 }
1600
1602 {
1604 }
1605 const double halfSize = size / 2.0;
1606
1607 //strokeWidth
1608 double strokeWidth = mStrokeWidth;
1609
1611 {
1614 }
1617 {
1619 }
1620
1621 //color
1622 QColor pc = mPen.color();
1623 QColor bc = mBrush.color();
1625 {
1628 }
1630 {
1633 }
1634
1635 //offset
1636 double offsetX = 0;
1637 double offsetY = 0;
1638 markerOffset( context, offsetX, offsetY );
1639 offsetX *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1640 offsetY *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1641
1642
1643 QPointF off( offsetX, offsetY );
1644
1645 //angle
1646 double angle = mAngle + mLineAngle;
1648 {
1651 }
1652
1655 {
1657 const QString shapeName = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::Name, context.renderContext().expressionContext(), QString(), &ok );
1658 if ( ok )
1659 {
1660 shape = decodeShape( shapeName, &ok );
1661 if ( !ok )
1662 shape = mShape;
1663 }
1664 }
1665
1666 if ( angle )
1667 off = _rotatedOffset( off, angle );
1668
1670
1671 QTransform t;
1672 t.translate( shift.x() + off.x(), shift.y() - off.y() );
1673
1674 if ( !qgsDoubleNear( angle, 0.0 ) )
1675 t.rotate( -angle );
1676
1677 QPolygonF polygon;
1678 if ( shapeToPolygon( shape, polygon ) )
1679 {
1680 t.scale( halfSize, -halfSize );
1681
1682 polygon = t.map( polygon );
1683
1685 p.reserve( polygon.size() );
1686 for ( int i = 0; i < polygon.size(); i++ )
1687 {
1688 p << QgsPoint( polygon[i] );
1689 }
1690
1691 if ( mBrush.style() != Qt::NoBrush )
1692 e.writePolygon( QgsRingSequence() << p, layerName, QStringLiteral( "SOLID" ), bc );
1693 if ( mPen.style() != Qt::NoPen )
1694 e.writePolyline( p, layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1695 }
1696 else if ( shape == Qgis::MarkerShape::Circle )
1697 {
1698 shift += QPointF( off.x(), -off.y() );
1699 if ( mBrush.style() != Qt::NoBrush )
1700 e.writeFilledCircle( layerName, bc, QgsPoint( shift ), halfSize );
1701 if ( mPen.style() != Qt::NoPen )
1702 e.writeCircle( layerName, pc, QgsPoint( shift ), halfSize, QStringLiteral( "CONTINUOUS" ), strokeWidth );
1703 }
1704 else if ( shape == Qgis::MarkerShape::Line )
1705 {
1706 const QPointF pt1 = t.map( QPointF( 0, -halfSize ) );
1707 const QPointF pt2 = t.map( QPointF( 0, halfSize ) );
1708
1709 if ( mPen.style() != Qt::NoPen )
1710 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1711 }
1712 else if ( shape == Qgis::MarkerShape::Cross )
1713 {
1714 if ( mPen.style() != Qt::NoPen )
1715 {
1716 const QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
1717 const QPointF pt2 = t.map( QPointF( halfSize, 0 ) );
1718 const QPointF pt3 = t.map( QPointF( 0, -halfSize ) );
1719 const QPointF pt4 = t.map( QPointF( 0, halfSize ) );
1720
1721 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1722 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1723 }
1724 }
1725 else if ( shape == Qgis::MarkerShape::Cross2 )
1726 {
1727 if ( mPen.style() != Qt::NoPen )
1728 {
1729 const QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
1730 const QPointF pt2 = t.map( QPointF( halfSize, halfSize ) );
1731 const QPointF pt3 = t.map( QPointF( halfSize, -halfSize ) );
1732 const QPointF pt4 = t.map( QPointF( -halfSize, halfSize ) );
1733
1734 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1735 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1736 }
1737 }
1738 else if ( shape == Qgis::MarkerShape::ArrowHead )
1739 {
1740 if ( mPen.style() != Qt::NoPen )
1741 {
1742 const QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
1743 const QPointF pt2 = t.map( QPointF( 0, 0 ) );
1744 const QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
1745
1746 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1747 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1748 }
1749 }
1750 else
1751 {
1752 QgsDebugError( QStringLiteral( "Unsupported dxf marker name %1" ).arg( encodeShape( shape ) ) );
1753 return false;
1754 }
1755
1756 return true;
1757}
1758
1759
1765
1774
1780
1789
1796
1798{
1799 QRectF symbolBounds = QgsSimpleMarkerSymbolLayerBase::bounds( point, context );
1800
1801 // need to account for stroke width
1802 double penWidth = mStrokeWidth;
1803 bool ok = true;
1805 {
1808 if ( ok )
1809 {
1810 penWidth = strokeWidth;
1811 }
1812 }
1815 {
1818 if ( ok && strokeStyle == QLatin1String( "no" ) )
1819 {
1820 penWidth = 0.0;
1821 }
1822 }
1823 else if ( mStrokeStyle == Qt::NoPen )
1824 penWidth = 0;
1825
1826 //antialiasing, add 1 pixel
1827 penWidth += 1;
1828
1829 //extend bounds by pen width / 2.0
1830 symbolBounds.adjust( -penWidth / 2.0, -penWidth / 2.0,
1831 penWidth / 2.0, penWidth / 2.0 );
1832
1833 return symbolBounds;
1834}
1835
1836void QgsSimpleMarkerSymbolLayer::setColor( const QColor &color )
1837{
1838 if ( shapeIsFilled( mShape ) )
1839 {
1841 }
1842 else
1843 {
1845 }
1846}
1847
1849{
1850 if ( shapeIsFilled( mShape ) )
1851 {
1852 return fillColor();
1853 }
1854 else
1855 {
1856 return strokeColor();
1857 }
1858}
1859
1860
1861
1862
1863//
1864// QgsFilledMarkerSymbolLayer
1865//
1866
1868 : QgsSimpleMarkerSymbolLayerBase( shape, size, angle, scaleMethod )
1869{
1870 mFill.reset( static_cast<QgsFillSymbol *>( QgsFillSymbol::createSimple( QVariantMap() ) ) );
1871}
1872
1874
1876{
1877 QString name = DEFAULT_SIMPLEMARKER_NAME;
1881
1882 if ( props.contains( QStringLiteral( "name" ) ) )
1883 name = props[QStringLiteral( "name" )].toString();
1884 if ( props.contains( QStringLiteral( "size" ) ) )
1885 size = props[QStringLiteral( "size" )].toDouble();
1886 if ( props.contains( QStringLiteral( "angle" ) ) )
1887 angle = props[QStringLiteral( "angle" )].toDouble();
1888 if ( props.contains( QStringLiteral( "scale_method" ) ) )
1889 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1890
1892 if ( props.contains( QStringLiteral( "offset" ) ) )
1893 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1894 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1895 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1896 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1897 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1898 if ( props.contains( QStringLiteral( "size_unit" ) ) )
1899 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1900 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1901 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1902 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1903 {
1904 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1905 }
1906 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1907 {
1908 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1909 }
1910
1912
1914
1915 return m;
1916}
1917
1919{
1920 return QStringLiteral( "FilledMarker" );
1921}
1922
1924{
1925 if ( mFill )
1926 {
1927 mFill->setRenderHints( mFill->renderHints() | Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol );
1928 mFill->startRender( context.renderContext(), context.fields() );
1929 }
1930
1932}
1933
1935{
1936 if ( mFill )
1937 {
1938 mFill->stopRender( context.renderContext() );
1939 }
1940}
1941
1943{
1944 installMasks( context, true );
1945
1946 // The base class version passes this on to the subsymbol, but we deliberately don't do that here.
1947}
1948
1950{
1951 removeMasks( context, true );
1952
1953 // The base class version passes this on to the subsymbol, but we deliberately don't do that here.
1954}
1955
1957{
1958 QVariantMap map;
1959 map[QStringLiteral( "name" )] = encodeShape( mShape );
1960 map[QStringLiteral( "size" )] = QString::number( mSize );
1961 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1962 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1963 map[QStringLiteral( "angle" )] = QString::number( mAngle );
1964 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1965 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1966 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1967 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1968 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
1969 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
1970
1971 if ( mFill )
1972 {
1973 map[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mFill->color() );
1974 }
1975 return map;
1976}
1977
1986
1988{
1989 return mFill.get();
1990}
1991
1993{
1994 if ( symbol && symbol->type() == Qgis::SymbolType::Fill )
1995 {
1996 mFill.reset( static_cast<QgsFillSymbol *>( symbol ) );
1997 return true;
1998 }
1999 else
2000 {
2001 delete symbol;
2002 return false;
2003 }
2004}
2005
2007{
2008 if ( mFill )
2009 {
2010 return QgsSymbolLayerUtils::estimateMaxSymbolBleed( mFill.get(), context );
2011 }
2012 return 0;
2013}
2014
2016{
2017 QSet<QString> attr = QgsSimpleMarkerSymbolLayerBase::usedAttributes( context );
2018 if ( mFill )
2019 attr.unite( mFill->usedAttributes( context ) );
2020 return attr;
2021}
2022
2024{
2026 return true;
2027 if ( mFill && mFill->hasDataDefinedProperties() )
2028 return true;
2029 return false;
2030}
2031
2033{
2034 mColor = c;
2035 if ( mFill )
2036 mFill->setColor( c );
2037}
2038
2040{
2041 return mFill ? mFill->color() : mColor;
2042}
2043
2050
2052{
2054 if ( mFill )
2055 mFill->setOutputUnit( unit );
2056}
2057
2058void QgsFilledMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
2059{
2060 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
2061 //of the rendered point!
2062
2063 QPainter *p = context.renderContext().painter();
2064 if ( !p )
2065 {
2066 return;
2067 }
2068
2069 const double prevOpacity = mFill->opacity();
2070 mFill->setOpacity( mFill->opacity() * context.opacity() );
2071
2072 if ( shapeIsFilled( shape ) )
2073 {
2074 p->setBrush( Qt::red );
2075 }
2076 else
2077 {
2078 p->setBrush( Qt::NoBrush );
2079 }
2080 p->setPen( Qt::black );
2081
2082 const bool prevIsSubsymbol = context.renderContext().flags() & Qgis::RenderContextFlag::RenderingSubSymbol;
2084
2085 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
2086 if ( !polygon.isEmpty() )
2087 {
2088 mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
2089 }
2090 else
2091 {
2092 const QPolygonF poly = path.toFillPolygon();
2093 mFill->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
2094 }
2095
2097
2098 mFill->setOpacity( prevOpacity );
2099}
2100
2101
2103
2104
2105QgsSvgMarkerSymbolLayer::QgsSvgMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
2106{
2107 mSize = size;
2108 mAngle = angle;
2109 mOffset = QPointF( 0, 0 );
2111 mStrokeWidth = 0.2;
2113 mColor = QColor( 35, 35, 35 );
2114 mStrokeColor = QColor( 35, 35, 35 );
2115 setPath( path );
2116}
2117
2119 : QgsMarkerSymbolLayer( other )
2120 , mPath( other.mPath )
2121 , mDefaultAspectRatio( other.mDefaultAspectRatio )
2122 , mFixedAspectRatio( other.mFixedAspectRatio )
2123 , mHasFillParam( other.mHasFillParam )
2124 , mStrokeColor( other.mStrokeColor )
2125 , mStrokeWidth( other.mStrokeWidth )
2126 , mParameters( other.mParameters )
2127 , mStrokeWidthUnit( other.mStrokeWidthUnit )
2128 , mStrokeWidthMapUnitScale( other.mStrokeWidthMapUnitScale )
2129{
2130}
2131
2133
2135{
2136 QString name;
2140
2141 if ( props.contains( QStringLiteral( "name" ) ) )
2142 name = props[QStringLiteral( "name" )].toString();
2143 if ( props.contains( QStringLiteral( "size" ) ) )
2144 size = props[QStringLiteral( "size" )].toDouble();
2145 if ( props.contains( QStringLiteral( "angle" ) ) )
2146 angle = props[QStringLiteral( "angle" )].toDouble();
2147 if ( props.contains( QStringLiteral( "scale_method" ) ) )
2148 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
2149
2151
2152 if ( props.contains( QStringLiteral( "size_unit" ) ) )
2153 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
2154 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
2155 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
2156 if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
2157 m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
2158 if ( props.contains( QStringLiteral( "offset" ) ) )
2159 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
2160 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
2161 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
2162 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2163 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
2164 if ( props.contains( QStringLiteral( "fill" ) ) )
2165 {
2166 //pre 2.5 projects used "fill"
2167 m->setFillColor( QgsColorUtils::colorFromString( props[QStringLiteral( "fill" )].toString() ) );
2168 }
2169 else if ( props.contains( QStringLiteral( "color" ) ) )
2170 {
2171 m->setFillColor( QgsColorUtils::colorFromString( props[QStringLiteral( "color" )].toString() ) );
2172 }
2173 if ( props.contains( QStringLiteral( "outline" ) ) )
2174 {
2175 //pre 2.5 projects used "outline"
2176 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "outline" )].toString() ) );
2177 }
2178 else if ( props.contains( QStringLiteral( "outline_color" ) ) )
2179 {
2180 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "outline_color" )].toString() ) );
2181 }
2182 else if ( props.contains( QStringLiteral( "line_color" ) ) )
2183 {
2184 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "line_color" )].toString() ) );
2185 }
2186
2187 if ( props.contains( QStringLiteral( "outline-width" ) ) )
2188 {
2189 //pre 2.5 projects used "outline-width"
2190 m->setStrokeWidth( props[QStringLiteral( "outline-width" )].toDouble() );
2191 }
2192 else if ( props.contains( QStringLiteral( "outline_width" ) ) )
2193 {
2194 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
2195 }
2196 else if ( props.contains( QStringLiteral( "line_width" ) ) )
2197 {
2198 m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
2199 }
2200
2201 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
2202 {
2203 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
2204 }
2205 else if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
2206 {
2207 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
2208 }
2209 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
2210 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
2211
2212 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2213 {
2214 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2215 }
2216 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2217 {
2218 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2219 }
2220
2222
2224
2225 if ( props.contains( QStringLiteral( "parameters" ) ) )
2226 {
2227 const QVariantMap parameters = props[QStringLiteral( "parameters" )].toMap();
2229 }
2230
2231 return m;
2232}
2233
2234void QgsSvgMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
2235{
2236 const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
2237 if ( it != properties.end() )
2238 {
2239 if ( saving )
2240 {
2241 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
2242 }
2243 else
2244 {
2245 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
2246 }
2247 }
2248}
2249
2250void QgsSvgMarkerSymbolLayer::setPath( const QString &path )
2251{
2253 mHasFillParam = false;
2254 mPath = path;
2255 QColor defaultFillColor, defaultStrokeColor;
2256 double strokeWidth, fillOpacity, strokeOpacity;
2257 bool hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
2258 bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
2259 QgsApplication::svgCache()->containsParams( path, mHasFillParam, hasDefaultFillColor, defaultFillColor,
2260 hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
2261 hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2262 hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
2263 hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
2264
2265 const double newFillOpacity = hasFillOpacityParam ? fillColor().alphaF() : 1.0;
2266 const double newStrokeOpacity = hasStrokeOpacityParam ? strokeColor().alphaF() : 1.0;
2267
2268 if ( hasDefaultFillColor )
2269 {
2270 defaultFillColor.setAlphaF( newFillOpacity );
2271 setFillColor( defaultFillColor );
2272 }
2273 if ( hasDefaultFillOpacity )
2274 {
2275 QColor c = fillColor();
2276 c.setAlphaF( fillOpacity );
2277 setFillColor( c );
2278 }
2279 if ( hasDefaultStrokeColor )
2280 {
2281 defaultStrokeColor.setAlphaF( newStrokeOpacity );
2282 setStrokeColor( defaultStrokeColor );
2283 }
2284 if ( hasDefaultStrokeWidth )
2285 {
2287 }
2288 if ( hasDefaultStrokeOpacity )
2289 {
2290 QColor c = strokeColor();
2291 c.setAlphaF( strokeOpacity );
2292 setStrokeColor( c );
2293 }
2294
2296}
2297
2299{
2300 if ( mDefaultAspectRatio == 0.0 )
2301 {
2302 //size
2303 const double size = mSize;
2304 //assume 88 dpi as standard value
2305 const double widthScaleFactor = 3.465;
2306 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( mPath, size, mColor, mStrokeColor, mStrokeWidth, widthScaleFactor );
2307 // set default aspect ratio
2308 mDefaultAspectRatio = svgViewbox.isValid() ? svgViewbox.height() / svgViewbox.width() : 0.0;
2309 }
2310 return mDefaultAspectRatio;
2311}
2312
2314{
2315 const bool aPreservedAspectRatio = preservedAspectRatio();
2316 if ( aPreservedAspectRatio && !par )
2317 {
2319 }
2320 else if ( !aPreservedAspectRatio && par )
2321 {
2322 mFixedAspectRatio = 0.0;
2323 }
2324 return preservedAspectRatio();
2325}
2326
2327void QgsSvgMarkerSymbolLayer::setParameters( const QMap<QString, QgsProperty> &parameters )
2328{
2330}
2331
2332
2334{
2335 return QStringLiteral( "SvgMarker" );
2336}
2337
2342
2344{
2345 QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
2346 Q_UNUSED( context )
2347}
2348
2350{
2351 Q_UNUSED( context )
2352}
2353
2355{
2356 QPainter *p = context.renderContext().painter();
2357 if ( !p )
2358 return;
2359
2360 bool hasDataDefinedSize = false;
2361 const double scaledWidth = calculateSize( context, hasDataDefinedSize );
2362 const double devicePixelRatio = context.renderContext().devicePixelRatio();
2363 const double width = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2364
2365 //don't render symbols with a width below one or above 10,000 pixels
2366 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2367 {
2368 return;
2369 }
2370
2371 const QgsScopedQPainterState painterState( p );
2372
2373 bool hasDataDefinedAspectRatio = false;
2374 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2375 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2376
2378
2379 double strokeWidth = mStrokeWidth;
2381 {
2384 }
2386
2387 QColor fillColor = mColor;
2388 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
2389 if ( useSelectedColor && mHasFillParam )
2390 {
2392 }
2394 {
2397 }
2398
2399 QColor strokeColor = mStrokeColor;
2401 {
2404 }
2405
2406 QString path = mPath;
2408 {
2411 context.renderContext().pathResolver() );
2413 {
2414 // adjust height of data defined path
2415 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2416 context.renderContext().scaleFactor(), aspectRatio,
2417 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2418 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2419 }
2420 }
2421
2422 QPointF outputOffset;
2423 double angle = 0.0;
2424 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2425
2426 p->translate( point + outputOffset );
2427
2428 const bool rotated = !qgsDoubleNear( angle, 0 );
2429 if ( rotated )
2430 p->rotate( angle );
2431
2432 bool fitsInCache = true;
2433 bool usePict = true;
2435 if ( ( !context.forceVectorRendering() && !rotated ) || ( useSelectedColor && rasterizeSelected ) )
2436 {
2437 QImage img = QgsApplication::svgCache()->svgAsImage( path, width * devicePixelRatio, fillColor, strokeColor, strokeWidth,
2438 context.renderContext().scaleFactor(), fitsInCache, aspectRatio,
2439 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2440 if ( fitsInCache && img.width() > 1 )
2441 {
2442 usePict = false;
2443
2444 if ( useSelectedColor )
2445 {
2447 }
2448
2449 //consider transparency
2450 if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2451 {
2452 QImage transparentImage = img.copy();
2453 QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2454 if ( devicePixelRatio == 1 )
2455 {
2456 p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
2457 }
2458 else
2459 {
2460 p->drawImage( QRectF( -transparentImage.width() / 2.0 / devicePixelRatio, -transparentImage.height() / 2.0 / devicePixelRatio,
2461 transparentImage.width() / devicePixelRatio, transparentImage.height() / devicePixelRatio
2462 ), transparentImage );
2463 }
2464 }
2465 else
2466 {
2467 if ( devicePixelRatio == 1 )
2468 {
2469 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2470 }
2471 else
2472 {
2473 p->drawImage( QRectF( -img.width() / 2.0 / devicePixelRatio, -img.height() / 2.0 / devicePixelRatio,
2474 img.width() / devicePixelRatio, img.height() / devicePixelRatio ), img );
2475 }
2476 }
2477 }
2478 }
2479
2480 if ( usePict || !fitsInCache )
2481 {
2482 p->setOpacity( context.opacity() );
2484 context.renderContext().scaleFactor(), context.forceVectorRendering(), aspectRatio,
2485 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2486 if ( pct.width() > 1 )
2487 {
2488 QgsPainting::drawPicture( p, QPointF( 0, 0 ), pct );
2489 }
2490 }
2491
2492 // workaround issue with nested QPictures forgetting antialiasing flag - see https://github.com/qgis/QGIS/issues/22909
2494}
2495
2496double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2497{
2498 double scaledSize = mSize;
2500
2501 bool ok = true;
2502 if ( hasDataDefinedSize )
2503 {
2506 }
2507 else
2508 {
2510 if ( hasDataDefinedSize )
2511 {
2514 }
2515 }
2516
2517 if ( hasDataDefinedSize && ok )
2518 {
2519 switch ( mScaleMethod )
2520 {
2522 scaledSize = std::sqrt( scaledSize );
2523 break;
2525 break;
2526 }
2527 }
2528
2529 return scaledSize;
2530}
2531
2532double QgsSvgMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2533{
2535 if ( !hasDataDefinedAspectRatio )
2536 return mFixedAspectRatio;
2537
2539 return 0.0;
2540
2541 double scaledAspectRatio = mDefaultAspectRatio;
2542 if ( mFixedAspectRatio > 0.0 )
2543 scaledAspectRatio = mFixedAspectRatio;
2544
2545 const double defaultHeight = mSize * scaledAspectRatio;
2546 scaledAspectRatio = defaultHeight / scaledSize;
2547
2548 bool ok = true;
2549 double scaledHeight = scaledSize * scaledAspectRatio;
2551 {
2552 context.setOriginalValueVariable( defaultHeight );
2554 }
2555
2556 if ( hasDataDefinedAspectRatio && ok )
2557 {
2558 switch ( mScaleMethod )
2559 {
2561 scaledHeight = sqrt( scaledHeight );
2562 break;
2564 break;
2565 }
2566 }
2567
2568 scaledAspectRatio = scaledHeight / scaledSize;
2569
2570 return scaledAspectRatio;
2571}
2572
2573void QgsSvgMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
2574{
2575 //offset
2576 double offsetX = 0;
2577 double offsetY = 0;
2578 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
2579 offset = QPointF( offsetX, offsetY );
2580
2583 {
2586 }
2587
2589 if ( hasDataDefinedRotation )
2590 {
2591 // For non-point markers, "dataDefinedRotation" means following the
2592 // shape (shape-data defined). For them, "field-data defined" does
2593 // not work at all. TODO: if "field-data defined" ever gets implemented
2594 // we'll need a way to distinguish here between the two, possibly
2595 // using another flag in renderHints()
2596 const QgsFeature *f = context.feature();
2597 if ( f )
2598 {
2599 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
2600 {
2601 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2602 angle += m2p.mapRotation();
2603 }
2604 }
2605 }
2606
2607 if ( angle )
2609}
2610
2611
2613{
2614 QVariantMap map;
2615 map[QStringLiteral( "name" )] = mPath;
2616 map[QStringLiteral( "size" )] = QString::number( mSize );
2617 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2618 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2619 map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2620 map[QStringLiteral( "angle" )] = QString::number( mAngle );
2621 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2622 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2623 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2624 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2625 map[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mColor );
2626 map[QStringLiteral( "outline_color" )] = QgsColorUtils::colorToString( mStrokeColor );
2627 map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
2628 map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2629 map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2630 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
2631 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
2632
2633 map[QStringLiteral( "parameters" )] = QgsProperty::propertyMapToVariantMap( mParameters );
2634
2635 return map;
2636}
2637
2644
2649
2655
2657{
2659 if ( unit != mStrokeWidthUnit )
2660 {
2662 }
2663 return unit;
2664}
2665
2671
2680
2681void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
2682{
2683 // <Graphic>
2684 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2685 element.appendChild( graphicElem );
2686
2687 // encode a parametric SVG reference
2688 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
2691
2692 // <Rotation>
2693 QString angleFunc;
2694 bool ok;
2695 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2696 if ( !ok )
2697 {
2698 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
2699 }
2700 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2701 {
2702 angleFunc = QString::number( angle + mAngle );
2703 }
2704
2705 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
2706
2707 // <Displacement>
2708 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2710}
2711
2713{
2714 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
2715
2716 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
2717 if ( graphicElem.isNull() )
2718 return nullptr;
2719
2720 QString path, mimeType;
2721 // Unused and to be DEPRECATED in externalGraphicFromSld
2722 QColor fillColor_;
2723 double size;
2724
2725 if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor_, size ) )
2726 return nullptr;
2727
2728 double scaleFactor = 1.0;
2729 const QString uom = element.attribute( QStringLiteral( "uom" ) );
2730 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
2731 size = size * scaleFactor;
2732
2733 if ( mimeType != QLatin1String( "image/svg+xml" ) )
2734 return nullptr;
2735
2736 double angle = 0.0;
2737 QString angleFunc;
2738 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2739 {
2740 bool ok;
2741 const double d = angleFunc.toDouble( &ok );
2742 if ( ok )
2743 angle = d;
2744 }
2745
2746 QPointF offset;
2748
2749 // Extract parameters from URL
2750 QString realPath { path };
2751 QUrl svgUrl { path };
2752
2753 // Because color definition can start with '#', the url parsing won't recognize the query string entirely
2754 QUrlQuery queryString;
2755
2756 if ( svgUrl.hasQuery() && svgUrl.hasFragment() )
2757 {
2758 const QString queryPart { path.mid( path.indexOf( '?' ) + 1 ) };
2759 queryString.setQuery( queryPart );
2760 }
2761
2762 // Remove query for simple file paths
2763 if ( svgUrl.scheme().isEmpty() || svgUrl.isLocalFile() )
2764 {
2765 svgUrl.setQuery( QString() );
2766 realPath = svgUrl.path();
2767 }
2768
2770
2771 QMap<QString, QgsProperty> params;
2772
2773 bool ok;
2774
2775 if ( queryString.hasQueryItem( QStringLiteral( "fill" ) ) )
2776 {
2777 const QColor fillColor { queryString.queryItemValue( QStringLiteral( "fill" ) ) };
2778 m->setFillColor( fillColor );
2779 }
2780
2781 if ( queryString.hasQueryItem( QStringLiteral( "fill-opacity" ) ) )
2782 {
2783 const double alpha { queryString.queryItemValue( QStringLiteral( "fill-opacity" ) ).toDouble( &ok ) };
2784 if ( ok )
2785 {
2786 params.insert( QStringLiteral( "fill-opacity" ), QgsProperty::fromValue( alpha ) );
2787 }
2788 }
2789
2790 if ( queryString.hasQueryItem( QStringLiteral( "outline" ) ) )
2791 {
2792 const QColor strokeColor { queryString.queryItemValue( QStringLiteral( "outline" ) ) };
2794 }
2795
2796 if ( queryString.hasQueryItem( QStringLiteral( "outline-opacity" ) ) )
2797 {
2798 const double alpha { queryString.queryItemValue( QStringLiteral( "outline-opacity" ) ).toDouble( &ok ) };
2799 if ( ok )
2800 {
2801 params.insert( QStringLiteral( "outline-opacity" ), QgsProperty::fromValue( alpha ) );
2802 }
2803 }
2804
2805 if ( queryString.hasQueryItem( QStringLiteral( "outline-width" ) ) )
2806 {
2807 const int width { queryString.queryItemValue( QStringLiteral( "outline-width" ) ).toInt( &ok )};
2808 if ( ok )
2809 {
2810 m->setStrokeWidth( width );
2811 }
2812 }
2813
2814 if ( ! params.isEmpty() )
2815 {
2816 m->setParameters( params );
2817 }
2818
2819 m->setOutputUnit( sldUnitSize );
2820 m->setAngle( angle );
2821 m->setOffset( offset );
2822 return m;
2823}
2824
2825bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
2826{
2827 //size
2828 double size = mSize;
2829
2830 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
2831
2832 bool ok = true;
2833 if ( hasDataDefinedSize )
2834 {
2837 }
2838
2839 if ( hasDataDefinedSize && ok )
2840 {
2841 switch ( mScaleMethod )
2842 {
2844 size = std::sqrt( size );
2845 break;
2847 break;
2848 }
2849 }
2850
2852 {
2853 size *= mmMapUnitScaleFactor;
2854 }
2855
2856//offset, angle
2857 QPointF offset = mOffset;
2858
2860 {
2863 const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
2864 if ( ok )
2865 offset = res;
2866 }
2867 const double offsetX = offset.x();
2868 const double offsetY = offset.y();
2869
2870 QPointF outputOffset( offsetX, offsetY );
2871
2872 double angle = mAngle + mLineAngle;
2874 {
2877 }
2878
2879 if ( angle )
2880 outputOffset = _rotatedOffset( outputOffset, angle );
2881
2883
2884 QString path = mPath;
2886 {
2889 context.renderContext().pathResolver() );
2890 }
2891
2892 double strokeWidth = mStrokeWidth;
2894 {
2897 }
2899
2900 QColor fillColor = mColor;
2902 {
2905 }
2906
2907 QColor strokeColor = mStrokeColor;
2909 {
2912 }
2913
2915
2916 const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, size, fillColor, strokeColor, strokeWidth,
2918 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2919
2920 QSvgRenderer r( svgContent );
2921 if ( !r.isValid() )
2922 return false;
2923
2924 QgsDxfPaintDevice pd( &e );
2925 pd.setDrawingSize( QSizeF( r.defaultSize() ) );
2926
2927 QSizeF outSize( r.defaultSize() );
2928 outSize.scale( size, size, Qt::KeepAspectRatio );
2929
2930 QPainter p;
2931 p.begin( &pd );
2932 if ( !qgsDoubleNear( angle, 0.0 ) )
2933 {
2934 p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
2935 p.rotate( angle );
2936 p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
2937 }
2938 pd.setShift( shift + QPointF( outputOffset.x(), -outputOffset.y() ) );
2939 pd.setOutputSize( QRectF( -outSize.width() / 2.0, -outSize.height() / 2.0, outSize.width(), outSize.height() ) );
2940 pd.setLayer( layerName );
2941 r.render( &p );
2942 p.end();
2943 return true;
2944}
2945
2947{
2948 bool hasDataDefinedSize = false;
2949 double scaledWidth = calculateSize( context, hasDataDefinedSize );
2950
2951 bool hasDataDefinedAspectRatio = false;
2952 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2953 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2954
2955 scaledWidth = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2956 scaledHeight = context.renderContext().convertToPainterUnits( scaledHeight, mSizeUnit, mSizeMapUnitScale );
2957
2958 //don't render symbols with size below one or above 10,000 pixels
2959 if ( static_cast< int >( scaledWidth ) < 1 || 10000.0 < scaledWidth )
2960 {
2961 return QRectF();
2962 }
2963
2964 QPointF outputOffset;
2965 double angle = 0.0;
2966 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2967
2968 double strokeWidth = mStrokeWidth;
2970 {
2973 }
2975
2976 QString path = mPath;
2978 {
2981 context.renderContext().pathResolver() );
2983 {
2984 // need to get colors to take advantage of cached SVGs
2985 QColor fillColor = mColor;
2987 {
2990 }
2991
2992 const QColor strokeColor = mStrokeColor;
2994 {
2997 }
2998
3000
3001 // adjust height of data defined path
3002 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
3003 context.renderContext().scaleFactor(), aspectRatio,
3004 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
3005 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
3006 }
3007 }
3008
3009 QTransform transform;
3010 // move to the desired position
3011 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3012
3013 if ( !qgsDoubleNear( angle, 0.0 ) )
3014 transform.rotate( angle );
3015
3016 //antialiasing
3017 strokeWidth += 1.0 / 2.0;
3018
3019 QRectF symbolBounds = transform.mapRect( QRectF( -scaledWidth / 2.0,
3020 -scaledHeight / 2.0,
3021 scaledWidth,
3022 scaledHeight ) );
3023
3024 //extend bounds by pen width / 2.0
3025 symbolBounds.adjust( -strokeWidth / 2.0, -strokeWidth / 2.0,
3026 strokeWidth / 2.0, strokeWidth / 2.0 );
3027
3028 return symbolBounds;
3029}
3030
3032
3033QgsRasterMarkerSymbolLayer::QgsRasterMarkerSymbolLayer( const QString &path, double size, double angle, Qgis::ScaleMethod scaleMethod )
3034 : mPath( path )
3035{
3036 mSize = size;
3037 mAngle = angle;
3038 mOffset = QPointF( 0, 0 );
3041}
3042
3044
3046{
3047 QString path;
3051
3052 if ( props.contains( QStringLiteral( "imageFile" ) ) )
3053 path = props[QStringLiteral( "imageFile" )].toString();
3054 if ( props.contains( QStringLiteral( "size" ) ) )
3055 size = props[QStringLiteral( "size" )].toDouble();
3056 if ( props.contains( QStringLiteral( "angle" ) ) )
3057 angle = props[QStringLiteral( "angle" )].toDouble();
3058 if ( props.contains( QStringLiteral( "scale_method" ) ) )
3059 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
3060
3061 auto m = std::make_unique< QgsRasterMarkerSymbolLayer >( path, size, angle, scaleMethod );
3062 m->setCommonProperties( props );
3063 return m.release();
3064}
3065
3066void QgsRasterMarkerSymbolLayer::setCommonProperties( const QVariantMap &properties )
3067{
3068 if ( properties.contains( QStringLiteral( "alpha" ) ) )
3069 {
3070 setOpacity( properties[QStringLiteral( "alpha" )].toDouble() );
3071 }
3072
3073 if ( properties.contains( QStringLiteral( "size_unit" ) ) )
3074 setSizeUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "size_unit" )].toString() ) );
3075 if ( properties.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3076 setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3077 if ( properties.contains( QStringLiteral( "fixedAspectRatio" ) ) )
3078 setFixedAspectRatio( properties[QStringLiteral( "fixedAspectRatio" )].toDouble() );
3079
3080 if ( properties.contains( QStringLiteral( "offset" ) ) )
3081 setOffset( QgsSymbolLayerUtils::decodePoint( properties[QStringLiteral( "offset" )].toString() ) );
3082 if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
3083 setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )].toString() ) );
3084 if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3085 setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3086
3087 if ( properties.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3088 {
3089 setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( properties[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3090 }
3091 if ( properties.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3092 {
3093 setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( properties[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3094 }
3095
3098}
3099
3100void QgsRasterMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
3101{
3102 const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
3103 if ( it != properties.end() && it.value().userType() == QMetaType::Type::QString )
3104 {
3105 if ( saving )
3106 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
3107 else
3108 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
3109 }
3110}
3111
3112void QgsRasterMarkerSymbolLayer::setPath( const QString &path )
3113{
3114 mPath = path;
3116}
3117
3119{
3120 const bool aPreservedAspectRatio = preservedAspectRatio();
3121 if ( aPreservedAspectRatio && !par )
3122 {
3124 }
3125 else if ( !aPreservedAspectRatio && par )
3126 {
3127 mFixedAspectRatio = 0.0;
3128 }
3129 return preservedAspectRatio();
3130}
3131
3133{
3134 if ( mDefaultAspectRatio == 0.0 && !mPath.isEmpty() )
3135 {
3137 mDefaultAspectRatio = ( !size.isNull() && size.isValid() && size.width() > 0 ) ? static_cast< double >( size.height() ) / static_cast< double >( size.width() ) : 0.0;
3138 }
3139 return mDefaultAspectRatio;
3140}
3141
3143{
3144 return QStringLiteral( "RasterMarker" );
3145}
3146
3151
3153{
3154 QPainter *p = context.renderContext().painter();
3155 if ( !p )
3156 return;
3157
3158 QString path = mPath;
3160 {
3163 }
3164
3165 if ( path.isEmpty() )
3166 return;
3167
3168 double width = 0.0;
3169 double height = 0.0;
3170
3171 bool hasDataDefinedSize = false;
3172 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3173
3174 bool hasDataDefinedAspectRatio = false;
3175 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3176
3177 QPointF outputOffset;
3178 double angle = 0.0;
3179
3180 // RenderPercentage Unit Type takes original image size
3182 {
3184 if ( size.isEmpty() )
3185 return;
3186
3187 width = ( scaledSize * static_cast< double >( size.width() ) ) / 100.0;
3188 height = ( scaledSize * static_cast< double >( size.height() ) ) / 100.0;
3189
3190 // don't render symbols with size below one or above 10,000 pixels
3191 if ( static_cast< int >( width ) < 1 || 10000.0 < width || static_cast< int >( height ) < 1 || 10000.0 < height )
3192 return;
3193
3194 calculateOffsetAndRotation( context, width, height, outputOffset, angle );
3195 }
3196 else
3197 {
3198 width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3199 height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3200
3201 if ( preservedAspectRatio() && path != mPath )
3202 {
3204 if ( !size.isNull() && size.isValid() && size.width() > 0 )
3205 {
3206 height = width * ( static_cast< double >( size.height() ) / static_cast< double >( size.width() ) );
3207 }
3208 }
3209
3210 // don't render symbols with size below one or above 10,000 pixels
3211 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
3212 return;
3213
3214 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3215 }
3216
3217 const QgsScopedQPainterState painterState( p );
3218 p->translate( point + outputOffset );
3219
3220 const bool rotated = !qgsDoubleNear( angle, 0 );
3221 if ( rotated )
3222 p->rotate( angle );
3223
3224 double opacity = mOpacity;
3226 {
3229 }
3230 opacity *= context.opacity();
3231
3232 QImage img = fetchImage( context.renderContext(), path, QSize( width, preservedAspectRatio() ? 0 : width * aspectRatio ), preservedAspectRatio(), opacity );
3233 if ( !img.isNull() )
3234 {
3235 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
3236 if ( useSelectedColor )
3237 {
3239 }
3240
3241 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
3242 }
3243}
3244
3245QImage QgsRasterMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
3246{
3247 bool cached = false;
3248 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking );
3249}
3250
3251double QgsRasterMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
3252{
3253 double scaledSize = mSize;
3255
3256 bool ok = true;
3257 if ( hasDataDefinedSize )
3258 {
3261 }
3262 else
3263 {
3265 if ( hasDataDefinedSize )
3266 {
3269 }
3270 }
3271
3272 if ( hasDataDefinedSize && ok )
3273 {
3274 switch ( mScaleMethod )
3275 {
3277 scaledSize = std::sqrt( scaledSize );
3278 break;
3280 break;
3281 }
3282 }
3283
3284 return scaledSize;
3285}
3286
3287double QgsRasterMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
3288{
3290 if ( !hasDataDefinedAspectRatio )
3291 return mFixedAspectRatio;
3292
3294 return 0.0;
3295
3296 double scaledAspectRatio = mDefaultAspectRatio;
3297 if ( mFixedAspectRatio > 0.0 )
3298 scaledAspectRatio = mFixedAspectRatio;
3299
3300 const double defaultHeight = mSize * scaledAspectRatio;
3301 scaledAspectRatio = defaultHeight / scaledSize;
3302
3303 bool ok = true;
3304 double scaledHeight = scaledSize * scaledAspectRatio;
3306 {
3307 context.setOriginalValueVariable( defaultHeight );
3309 }
3310
3311 if ( hasDataDefinedAspectRatio && ok )
3312 {
3313 switch ( mScaleMethod )
3314 {
3316 scaledHeight = sqrt( scaledHeight );
3317 break;
3319 break;
3320 }
3321 }
3322
3323 scaledAspectRatio = scaledHeight / scaledSize;
3324
3325 return scaledAspectRatio;
3326}
3327
3328void QgsRasterMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
3329{
3330 //offset
3331 double offsetX = 0;
3332 double offsetY = 0;
3333 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
3334 offset = QPointF( offsetX, offsetY );
3335
3338 {
3341 }
3342
3344 if ( hasDataDefinedRotation )
3345 {
3346 const QgsFeature *f = context.feature();
3347 if ( f )
3348 {
3349 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3350 {
3351 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3352 angle += m2p.mapRotation();
3353 }
3354 }
3355 }
3356
3357 if ( angle )
3359}
3360
3361
3363{
3364 QVariantMap map;
3365 map[QStringLiteral( "imageFile" )] = mPath;
3366 map[QStringLiteral( "size" )] = QString::number( mSize );
3367 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3368 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3369 map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
3370 map[QStringLiteral( "angle" )] = QString::number( mAngle );
3371 map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
3372 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3373 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3374 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3375 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
3376 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3377 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3378 return map;
3379}
3380
3382{
3383 auto m = std::make_unique< QgsRasterMarkerSymbolLayer >();
3384 m->mPath = mPath;
3385 m->mDefaultAspectRatio = mDefaultAspectRatio;
3386 m->mSize = mSize;
3387 m->mAngle = mAngle;
3388 // other members are copied by:
3389 copyCommonProperties( m.get() );
3390 return m.release();
3391}
3392
3393
3408
3414
3416{
3417 return QColor();
3418}
3419
3424
3429
3431{
3432 bool hasDataDefinedSize = false;
3433 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3434 const double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3435 bool hasDataDefinedAspectRatio = false;
3436 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3437 const double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3438
3439 //don't render symbols with size below one or above 10,000 pixels
3440 if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
3441 {
3442 return QRectF();
3443 }
3444
3445 QPointF outputOffset;
3446 double angle = 0.0;
3447 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3448
3449 QTransform transform;
3450
3451 // move to the desired position
3452 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3453
3454 if ( !qgsDoubleNear( angle, 0.0 ) )
3455 transform.rotate( angle );
3456
3457 QRectF symbolBounds = transform.mapRect( QRectF( -width / 2.0,
3458 -height / 2.0,
3459 width,
3460 height ) );
3461
3462 return symbolBounds;
3463}
3464
3466
3467QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QString chr, double pointSize, const QColor &color, double angle )
3468{
3469 mFontFamily = fontFamily;
3470 mString = chr;
3471 mColor = color;
3472 mAngle = angle;
3473 mSize = pointSize;
3474 mOrigSize = pointSize;
3476 mOffset = QPointF( 0, 0 );
3478 mStrokeColor = DEFAULT_FONTMARKER_BORDERCOLOR;
3479 mStrokeWidth = 0.0;
3480 mStrokeWidthUnit = Qgis::RenderUnit::Millimeters;
3481 mPenJoinStyle = DEFAULT_FONTMARKER_JOINSTYLE;
3482}
3483
3485
3487{
3489 QString string = DEFAULT_FONTMARKER_CHR;
3490 double pointSize = DEFAULT_FONTMARKER_SIZE;
3493
3494 if ( props.contains( QStringLiteral( "font" ) ) )
3495 fontFamily = props[QStringLiteral( "font" )].toString();
3496 if ( props.contains( QStringLiteral( "chr" ) ) && props[QStringLiteral( "chr" )].toString().length() > 0 )
3497 {
3498 string = props["chr"].toString();
3499 const thread_local QRegularExpression charRegExp( QStringLiteral( "%1([0-9]+)%1" ).arg( FONTMARKER_CHR_FIX ) );
3500 QRegularExpressionMatch match = charRegExp.match( string );
3501 while ( match.hasMatch() )
3502 {
3503 QChar replacement = QChar( match.captured( 1 ).toUShort() );
3504 string = string.mid( 0, match.capturedStart( 0 ) ) + replacement + string.mid( match.capturedEnd( 0 ) );
3505 match = charRegExp.match( string );
3506 }
3507 }
3508
3509 if ( props.contains( QStringLiteral( "size" ) ) )
3510 pointSize = props[QStringLiteral( "size" )].toDouble();
3511 if ( props.contains( QStringLiteral( "color" ) ) )
3512 color = QgsColorUtils::colorFromString( props[QStringLiteral( "color" )].toString() );
3513 if ( props.contains( QStringLiteral( "angle" ) ) )
3514 angle = props[QStringLiteral( "angle" )].toDouble();
3515
3517
3518 if ( props.contains( QStringLiteral( "font_style" ) ) )
3519 m->setFontStyle( props[QStringLiteral( "font_style" )].toString() );
3520 if ( props.contains( QStringLiteral( "outline_color" ) ) )
3521 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "outline_color" )].toString() ) );
3522 if ( props.contains( QStringLiteral( "outline_width" ) ) )
3523 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
3524 if ( props.contains( QStringLiteral( "offset" ) ) )
3525 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
3526 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
3527 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
3528 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3529 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3530 if ( props.contains( QStringLiteral( "size_unit" ) ) )
3531 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
3532 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3533 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3534 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
3535 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
3536 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
3537 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
3538 if ( props.contains( QStringLiteral( "joinstyle" ) ) )
3539 m->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() ) );
3540 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3541 m->setHorizontalAnchorPoint( QgsMarkerSymbolLayer::HorizontalAnchorPoint( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3542 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3543 m->setVerticalAnchorPoint( QgsMarkerSymbolLayer::VerticalAnchorPoint( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3544
3546
3547 return m;
3548}
3549
3551{
3552 return QStringLiteral( "FontMarker" );
3553}
3554
3559
3561{
3562 QColor brushColor = mColor;
3563 QColor penColor = mStrokeColor;
3564
3565 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
3566 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
3567
3568 mBrush = QBrush( brushColor );
3569 mPen = QPen( penColor );
3570 mPen.setJoinStyle( mPenJoinStyle );
3571 mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
3572
3573 mFont = QgsFontUtils::createFont( QgsApplication::fontManager()->processFontFamilyName( mFontFamily ) );
3574 if ( !mFontStyle.isEmpty() )
3575 {
3576 mFont.setStyleName( QgsFontUtils::translateNamedStyle( mFontStyle ) );
3577 }
3578
3579 double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3580 mNonZeroFontSize = !qgsDoubleNear( sizePixels, 0.0 );
3581
3582 if ( mNonZeroFontSize && sizePixels > MAX_FONT_CHARACTER_SIZE_IN_PIXELS )
3583 {
3584 // if font is too large (e.g using map units and map is very zoomed in), then we limit
3585 // the font size and instead scale up the painter.
3586 // this avoids issues with massive font sizes (eg https://github.com/qgis/QGIS/issues/42270)
3587 mFontSizeScale = sizePixels / MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3588 sizePixels = MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3589 }
3590 else
3591 mFontSizeScale = 1.0;
3592
3593 // if a non zero, but small pixel size results, round up to 2 pixels so that a "dot" is at least visible
3594 // (if we set a <=1 pixel size here Qt will reset the font to a default size, leading to much too large symbols)
3595 mFont.setPixelSize( std::max( 2, static_cast< int >( std::round( sizePixels ) ) ) );
3596 mFontMetrics.reset( new QFontMetrics( mFont ) );
3597 mChrWidth = mFontMetrics->horizontalAdvance( mString );
3598 mChrOffset = QPointF( mChrWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3599 mOrigSize = mSize; // save in case the size would be data defined
3600
3601 // use caching only when not using a data defined character
3605 if ( mUseCachedPath )
3606 {
3607 QPointF chrOffset = mChrOffset;
3608 double chrWidth;
3609 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3610 mCachedPath = QPainterPath();
3611 mCachedPath.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3612 }
3613}
3614
3616{
3617 Q_UNUSED( context )
3618}
3619
3620QString QgsFontMarkerSymbolLayer::characterToRender( QgsSymbolRenderContext &context, QPointF &charOffset, double &charWidth )
3621{
3622 charOffset = mChrOffset;
3623 QString stringToRender = mString;
3625 {
3626 context.setOriginalValueVariable( mString );
3628 if ( stringToRender != mString )
3629 {
3630 charWidth = mFontMetrics->horizontalAdvance( stringToRender );
3631 charOffset = QPointF( charWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3632 }
3633 }
3634 return stringToRender;
3635}
3636
3637void QgsFontMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
3638 double scaledSize,
3639 bool &hasDataDefinedRotation,
3640 QPointF &offset,
3641 double &angle ) const
3642{
3643 //offset
3644 double offsetX = 0;
3645 double offsetY = 0;
3646 markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
3647 offset = QPointF( offsetX, offsetY );
3648
3649 //angle
3650 bool ok = true;
3653 {
3656
3657 // If the expression evaluation was not successful, fallback to static value
3658 if ( !ok )
3660 }
3661
3662 hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation;
3663 if ( hasDataDefinedRotation )
3664 {
3665 // For non-point markers, "dataDefinedRotation" means following the
3666 // shape (shape-data defined). For them, "field-data defined" does
3667 // not work at all. TODO: if "field-data defined" ever gets implemented
3668 // we'll need a way to distinguish here between the two, possibly
3669 // using another flag in renderHints()
3670 const QgsFeature *f = context.feature();
3671 if ( f )
3672 {
3673 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3674 {
3675 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3676 angle += m2p.mapRotation();
3677 }
3678 }
3679 }
3680
3681 if ( angle )
3683}
3684
3685double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context )
3686{
3687 double scaledSize = mSize;
3688 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
3689
3690 bool ok = true;
3691 if ( hasDataDefinedSize )
3692 {
3695 }
3696
3697 if ( hasDataDefinedSize && ok )
3698 {
3699 switch ( mScaleMethod )
3700 {
3702 scaledSize = std::sqrt( scaledSize );
3703 break;
3705 break;
3706 }
3707 }
3708 return scaledSize;
3709}
3710
3712{
3713 QPainter *p = context.renderContext().painter();
3714 if ( !p || !mNonZeroFontSize )
3715 return;
3716
3717 QTransform transform;
3718
3719 bool ok;
3720 QColor brushColor = mColor;
3722 {
3725 }
3726 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
3727 brushColor = useSelectedColor ? context.renderContext().selectionColor() : brushColor;
3728 if ( !useSelectedColor || !SELECTION_IS_OPAQUE )
3729 {
3730 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
3731 }
3732 mBrush.setColor( brushColor );
3733
3734 QColor penColor = mStrokeColor;
3736 {
3739 }
3740 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
3741
3742 double penWidth = context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3744 {
3745 context.setOriginalValueVariable( mStrokeWidth );
3747 if ( ok )
3748 {
3749 penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3750 }
3751 }
3752
3754 {
3757 if ( ok )
3758 {
3759 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
3760 }
3761 }
3762
3763 const QgsScopedQPainterState painterState( p );
3764 p->setBrush( mBrush );
3765 if ( !qgsDoubleNear( penWidth, 0.0 ) )
3766 {
3767 mPen.setColor( penColor );
3768 mPen.setWidthF( penWidth );
3769 p->setPen( mPen );
3770 }
3771 else
3772 {
3773 p->setPen( Qt::NoPen );
3774 }
3775
3777 {
3778 context.setOriginalValueVariable( mFontFamily );
3780 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( ok ? fontFamily : mFontFamily );
3781 QgsFontUtils::setFontFamily( mFont, processedFamily );
3782 }
3784 {
3785 context.setOriginalValueVariable( mFontStyle );
3788 }
3790 {
3791 mFontMetrics.reset( new QFontMetrics( mFont ) );
3792 }
3793
3794 QPointF chrOffset = mChrOffset;
3795 double chrWidth;
3796 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3797
3798 const double sizeToRender = calculateSize( context );
3799
3800 bool hasDataDefinedRotation = false;
3801 QPointF offset;
3802 double angle = 0;
3803 calculateOffsetAndRotation( context, sizeToRender, hasDataDefinedRotation, offset, angle );
3804
3805 p->translate( point.x() + offset.x(), point.y() + offset.y() );
3806
3807 if ( !qgsDoubleNear( angle, 0.0 ) )
3808 transform.rotate( angle );
3809
3810 if ( !qgsDoubleNear( sizeToRender, mOrigSize ) )
3811 {
3812 const double s = sizeToRender / mOrigSize;
3813 transform.scale( s, s );
3814 }
3815
3816 if ( !qgsDoubleNear( mFontSizeScale, 1.0 ) )
3817 transform.scale( mFontSizeScale, mFontSizeScale );
3818
3819 if ( mUseCachedPath )
3820 {
3821 p->drawPath( transform.map( mCachedPath ) );
3822 }
3823 else
3824 {
3825 QPainterPath path;
3826 path.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3827 p->drawPath( transform.map( path ) );
3828 }
3829}
3830
3832{
3833 QVariantMap props;
3834 props[QStringLiteral( "font" )] = mFontFamily;
3835 props[QStringLiteral( "font_style" )] = mFontStyle;
3836 QString chr = mString;
3837 for ( int i = 0; i < 32; i++ )
3838 {
3839 if ( i == 9 || i == 10 || i == 13 )
3840 {
3841 continue;
3842 }
3843 chr.replace( QChar( i ), QStringLiteral( "%1%2%1" ).arg( FONTMARKER_CHR_FIX, QString::number( i ) ) );
3844 }
3845 props[QStringLiteral( "chr" )] = chr;
3846 props[QStringLiteral( "size" )] = QString::number( mSize );
3847 props[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3848 props[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3849 props[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mColor );
3850 props[QStringLiteral( "outline_color" )] = QgsColorUtils::colorToString( mStrokeColor );
3851 props[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
3852 props[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
3853 props[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
3854 props[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
3855 props[QStringLiteral( "angle" )] = QString::number( mAngle );
3856 props[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3857 props[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3858 props[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3859 props[QStringLiteral( "horizontal_anchor_point" )] = QString::number( mHorizontalAnchorPoint );
3860 props[QStringLiteral( "vertical_anchor_point" )] = QString::number( mVerticalAnchorPoint );
3861 return props;
3862}
3863
3865{
3866 QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( mFontFamily, mString, mSize, mColor, mAngle );
3867 m->setFontStyle( mFontStyle );
3868 m->setStrokeColor( mStrokeColor );
3869 m->setStrokeWidth( mStrokeWidth );
3870 m->setStrokeWidthUnit( mStrokeWidthUnit );
3871 m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
3872 m->setPenJoinStyle( mPenJoinStyle );
3873 m->setOffset( mOffset );
3876 m->setSizeUnit( mSizeUnit );
3881 copyPaintEffect( m );
3882 return m;
3883}
3884
3885void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
3886{
3887 // <Graphic>
3888 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
3889 element.appendChild( graphicElem );
3890
3891 const QString fontPath = QStringLiteral( "ttf://%1" ).arg( mFontFamily );
3892 int markIndex = !mString.isEmpty() ? mString.at( 0 ).unicode() : 0;
3893 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
3894 QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, QStringLiteral( "ttf" ), &markIndex, mColor, size );
3895
3896 // <Rotation>
3897 QString angleFunc;
3898 bool ok;
3899 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
3900 if ( !ok )
3901 {
3902 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
3903 }
3904 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
3905 {
3906 angleFunc = QString::number( angle + mAngle );
3907 }
3908 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc );
3909
3910 // <Displacement>
3911 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
3913}
3914
3921
3923{
3925 mStrokeWidthUnit = unit;
3926}
3927
3929{
3930 QPointF chrOffset = mChrOffset;
3931 double chrWidth = mChrWidth;
3932 //calculate width of rendered character
3933 ( void )characterToRender( context, chrOffset, chrWidth );
3934
3935 if ( !mFontMetrics )
3936 mFontMetrics.reset( new QFontMetrics( mFont ) );
3937
3938 double scaledSize = calculateSize( context );
3939 if ( !qgsDoubleNear( scaledSize, mOrigSize ) )
3940 {
3941 chrWidth *= scaledSize / mOrigSize;
3942 }
3943 chrWidth *= mFontSizeScale;
3944
3945 bool hasDataDefinedRotation = false;
3946 QPointF offset;
3947 double angle = 0;
3948 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
3949 scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3950
3951 QTransform transform;
3952
3953 // move to the desired position
3954 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
3955
3956 if ( !qgsDoubleNear( angle, 0.0 ) )
3957 transform.rotate( angle );
3958
3959 QRectF symbolBounds = transform.mapRect( QRectF( -chrWidth / 2.0,
3960 -scaledSize / 2.0,
3961 chrWidth,
3962 scaledSize ) );
3963 return symbolBounds;
3964}
3965
3967{
3968 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
3969
3970 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
3971 if ( graphicElem.isNull() )
3972 return nullptr;
3973
3974 QString name, format;
3975 QColor color;
3976 double size;
3977 int chr;
3978
3979 if ( !QgsSymbolLayerUtils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
3980 return nullptr;
3981
3982 if ( !name.startsWith( QLatin1String( "ttf://" ) ) || format != QLatin1String( "ttf" ) )
3983 return nullptr;
3984
3985 const QString fontFamily = name.mid( 6 );
3986
3987 double angle = 0.0;
3988 QString angleFunc;
3989 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
3990 {
3991 bool ok;
3992 const double d = angleFunc.toDouble( &ok );
3993 if ( ok )
3994 angle = d;
3995 }
3996
3997 QPointF offset;
3999
4000 double scaleFactor = 1.0;
4001 const QString uom = element.attribute( QStringLiteral( "uom" ) );
4002 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
4003 offset.setX( offset.x() * scaleFactor );
4004 offset.setY( offset.y() * scaleFactor );
4005 size = size * scaleFactor;
4006
4008 m->setOutputUnit( sldUnitSize );
4009 m->setAngle( angle );
4010 m->setOffset( offset );
4011 return m;
4012}
4013
4014void QgsFontMarkerSymbolLayer::resolveFonts( const QVariantMap &properties, const QgsReadWriteContext &context )
4015{
4016 const QString fontFamily = properties.value( QStringLiteral( "font" ), DEFAULT_FONTMARKER_FONT ).toString();
4017 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( fontFamily );
4018 QString matched;
4019 if ( !QgsFontUtils::fontFamilyMatchOnSystem( processedFamily )
4020 && !QgsApplication::fontManager()->tryToDownloadFontFamily( processedFamily, matched ) )
4021 {
4022 context.pushMessage( QObject::tr( "Font “%1” not available on system" ).arg( processedFamily ) );
4023 }
4024}
4025
4027{
4028 QMap<QString, QgsProperty>::iterator it = mParameters.begin();
4029 for ( ; it != mParameters.end(); ++it )
4030 it.value().prepare( context.renderContext().expressionContext() );
4031
4033}
4034
4035
4037{
4038 QSet<QString> attrs = QgsMarkerSymbolLayer::usedAttributes( context );
4039
4040 QMap<QString, QgsProperty>::const_iterator it = mParameters.constBegin();
4041 for ( ; it != mParameters.constEnd(); ++it )
4042 {
4043 attrs.unite( it.value().referencedFields( context.expressionContext(), true ) );
4044 }
4045
4046 return attrs;
4047}
4048
4049//
4050// QgsAnimatedMarkerSymbolLayer
4051//
4052
4053QgsAnimatedMarkerSymbolLayer::QgsAnimatedMarkerSymbolLayer( const QString &path, double size, double angle )
4054 : QgsRasterMarkerSymbolLayer( path, size, angle )
4055{
4056
4057}
4058
4060
4061QgsSymbolLayer *QgsAnimatedMarkerSymbolLayer::create( const QVariantMap &properties ) // cppcheck-suppress duplInheritedMember
4062{
4063 QString path;
4066
4067 if ( properties.contains( QStringLiteral( "imageFile" ) ) )
4068 path = properties[QStringLiteral( "imageFile" )].toString();
4069 if ( properties.contains( QStringLiteral( "size" ) ) )
4070 size = properties[QStringLiteral( "size" )].toDouble();
4071 if ( properties.contains( QStringLiteral( "angle" ) ) )
4072 angle = properties[QStringLiteral( "angle" )].toDouble();
4073
4074 auto m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( path, size, angle );
4075 m->setFrameRate( properties.value( QStringLiteral( "frameRate" ), QStringLiteral( "10" ) ).toDouble() );
4076
4077 m->setCommonProperties( properties );
4078 return m.release();
4079}
4080
4082{
4083 return QStringLiteral( "AnimatedMarker" );
4084}
4085
4087{
4088 QVariantMap res = QgsRasterMarkerSymbolLayer::properties();
4089 res.insert( QStringLiteral( "frameRate" ), mFrameRateFps );
4090 return res;
4091}
4092
4094{
4095 auto m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( mPath, mSize, mAngle );
4096 m->setFrameRate( mFrameRateFps );
4097 copyCommonProperties( m.get() );
4098 return m.release();
4099}
4100
4102{
4104
4105 mPreparedPaths.clear();
4107 {
4109 mStaticPath = true;
4110 }
4111 else
4112 {
4113 mStaticPath = false;
4114 }
4115}
4116
4117QImage QgsAnimatedMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
4118{
4119 if ( !mStaticPath && !mPreparedPaths.contains( path ) )
4120 {
4122 mPreparedPaths.insert( path );
4123 }
4124
4125 const long long mapFrameNumber = context.currentFrame();
4127 const double markerAnimationDuration = totalFrameCount / mFrameRateFps;
4128
4129 double animationTimeSeconds = 0;
4130 if ( mapFrameNumber >= 0 && context.frameRate() > 0 )
4131 {
4132 // render is part of an animation, so we base the calculated frame on that
4133 animationTimeSeconds = mapFrameNumber / context.frameRate();
4134 }
4135 else
4136 {
4137 // render is outside of animation, so base the calculated frame on the current epoch
4138 animationTimeSeconds = QDateTime::currentMSecsSinceEpoch() / 1000.0;
4139 }
4140
4141 const double markerAnimationProgressSeconds = std::fmod( animationTimeSeconds, markerAnimationDuration );
4142 const int movieFrame = static_cast< int >( std::floor( markerAnimationProgressSeconds * mFrameRateFps ) );
4143
4144 bool cached = false;
4145 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking, 96, movieFrame );
4146}
4147
@ 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:2940
@ 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:4930
@ 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.
void stopFeatureRender(const QgsFeature &feature, QgsRenderContext &context) override
Called after the layer has been rendered for a particular feature.
void startFeatureRender(const QgsFeature &feature, QgsRenderContext &context) override
Called before the layer will be rendered for a particular feature.
~QgsFilledMarkerSymbolLayer() override
bool setSubSymbol(QgsSymbol *symbol) override
Sets layer's subsymbol. takes ownership of the passed symbol.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
double estimateMaxBleed(const QgsRenderContext &context) const override
Returns the estimated maximum distance which the layer style will bleed outside the drawn shape when ...
void setColor(const QColor &c) override
Sets the "representative" color for the symbol layer.
QString layerType() const override
Returns a string that represents this layer type.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsFilledMarkerSymbolLayer.
QgsFilledMarkerSymbolLayer(Qgis::MarkerShape shape=Qgis::MarkerShape::Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructor for QgsFilledMarkerSymbolLayer.
QString processFontFamilyName(const QString &name) const
Processes a font family name, applying any matching fontFamilyReplacements() to the name.
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.
bool installMasks(QgsRenderContext &context, bool recursive, const QRectF &rect=QRectF())
When rendering, install masks on context painter.
void removeMasks(QgsRenderContext &context, bool recursive)
When rendering, remove previously installed masks from context painter if recursive is true masks are...
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns the set of attributes referenced by the layer.
void copyDataDefinedProperties(QgsSymbolLayer *destLayer) const
Copies all data defined properties of this layer to another symbol layer.
@ StrokeStyle
Stroke style (eg solid, dashed)
@ Name
Name, eg shape name for simple markers.
@ Character
Character, eg for font marker symbol layers.
@ StrokeColor
Stroke color.
@ CapStyle
Line cap style.
@ JoinStyle
Line join style.
@ StrokeWidth
Stroke width.
@ FontFamily
Font family.
@ Offset
Symbol offset.
@ Height
Symbol height.
void restoreOldDataDefinedProperties(const QVariantMap &stringMap)
Restores older data defined properties from string map.
virtual void startRender(QgsSymbolRenderContext &context)=0
Called before a set of rendering operations commences on the supplied render context.
virtual void prepareExpressions(const QgsSymbolRenderContext &context)
Prepares all data defined property expressions for evaluation.
virtual 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:6125
QMap< QString, QString > QgsStringMap
Definition qgis.h:6663
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
#define DEG2RAD(x)
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:41
#define QgsDebugError(str)
Definition qgslogger.h:40
#define DEFAULT_FONTMARKER_JOINSTYLE
#define DEFAULT_RASTERMARKER_ANGLE
#define DEFAULT_RASTERMARKER_SIZE
#define DEFAULT_SVGMARKER_ANGLE
#define DEFAULT_SIMPLEMARKER_JOINSTYLE
#define DEFAULT_FONTMARKER_CHR
#define DEFAULT_SIMPLEMARKER_BORDERCOLOR
#define DEFAULT_SIMPLEMARKER_SIZE
#define DEFAULT_SIMPLEMARKER_NAME
#define DEFAULT_SIMPLEMARKER_ANGLE
#define DEFAULT_SVGMARKER_SIZE
#define DEFAULT_FONTMARKER_FONT
#define DEFAULT_FONTMARKER_BORDERCOLOR
#define DEFAULT_FONTMARKER_ANGLE
#define DEFAULT_FONTMARKER_COLOR
#define DEFAULT_FONTMARKER_SIZE
#define DEFAULT_SIMPLEMARKER_COLOR
#define DEFAULT_SCALE_METHOD
#define FONTMARKER_CHR_FIX