QGIS API Documentation 3.99.0-Master (21b3aa880ba)
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
18#include <cmath>
19#include <memory>
20
21#include "qgscolorutils.h"
22#include "qgsdxfexport.h"
23#include "qgsdxfpaintdevice.h"
24#include "qgsfillsymbol.h"
25#include "qgsfontmanager.h"
26#include "qgsfontutils.h"
27#include "qgsimagecache.h"
28#include "qgsimageoperation.h"
29#include "qgslogger.h"
30#include "qgspainting.h"
31#include "qgsrendercontext.h"
32#include "qgssldexportcontext.h"
33#include "qgssvgcache.h"
34#include "qgssymbol.h"
35#include "qgssymbollayerutils.h"
36#include "qgsunittypes.h"
37
38#include <QDir>
39#include <QDomDocument>
40#include <QDomElement>
41#include <QFileInfo>
42#include <QMimeDatabase>
43#include <QMimeType>
44#include <QPainter>
45#include <QSvgRenderer>
46#include <QUrlQuery>
47
48static constexpr int MAX_FONT_CHARACTER_SIZE_IN_PIXELS = 500;
49
51
52
53//
54// QgsSimpleMarkerSymbolLayerBase
55//
56
100
111
113
115{
116 switch ( shape )
117 {
148 return true;
149
157 return false;
158 }
159 return true;
160}
161
163{
164 const bool hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation
166 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
167
168 // use either QPolygonF or QPainterPath for drawing
169 if ( !prepareMarkerShape( mShape ) ) // drawing as a polygon
170 {
171 prepareMarkerPath( mShape ); // drawing as a painter path
172 }
173
174 QTransform transform;
175
176 // scale the shape (if the size is not going to be modified)
177 if ( !hasDataDefinedSize )
178 {
179 double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
181 {
182 // rendering for symbol previews -- an size in meters in map units can't be calculated, so treat the size as millimeters
183 // and clamp it to a reasonable range. It's the best we can do in this situation!
184 scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
185 }
186
187 const double half = scaledSize / 2.0;
188 transform.scale( half, half );
189 }
190
191 // rotate if the rotation is not going to be changed during the rendering
192 if ( !hasDataDefinedRotation && !qgsDoubleNear( mAngle, 0.0 ) )
193 {
194 transform.rotate( mAngle );
195 }
196
197 if ( !mPolygon.isEmpty() )
198 mPolygon = transform.map( mPolygon );
199 else
200 mPath = transform.map( mPath );
201
203}
204
206{
207 Q_UNUSED( context )
208}
209
211{
212 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
213 //of the rendered point!
214
215 QPainter *p = context.renderContext().painter();
216 if ( !p )
217 {
218 return;
219 }
220
221 bool hasDataDefinedSize = false;
222 const double scaledSize = calculateSize( context, hasDataDefinedSize );
223
224 bool hasDataDefinedRotation = false;
225 QPointF offset;
226 double angle = 0;
227 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
228
229 //data defined shape?
230 bool createdNewPath = false;
231 bool ok = true;
232 Qgis::MarkerShape symbol = mShape;
234 {
235 context.setOriginalValueVariable( encodeShape( symbol ) );
236 const QVariant exprVal = mDataDefinedProperties.value( QgsSymbolLayer::Property::Name, context.renderContext().expressionContext() );
237 if ( !QgsVariantUtils::isNull( exprVal ) )
238 {
239 const Qgis::MarkerShape decoded = decodeShape( exprVal.toString(), &ok );
240 if ( ok )
241 {
242 symbol = decoded;
243
244 if ( !prepareMarkerShape( symbol ) ) // drawing as a polygon
245 {
246 prepareMarkerPath( symbol ); // drawing as a painter path
247 }
248 createdNewPath = true;
249 }
250 }
251 else
252 {
253 symbol = mShape;
254 }
255 }
256
257 QTransform transform;
258
259 // move to the desired position
260 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
261
262 // resize if necessary
263 if ( hasDataDefinedSize || createdNewPath )
264 {
265 double s = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
267 {
268 // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
269 // and clamp it to a reasonable range. It's the best we can do in this situation!
270 s = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
271 }
272 const double half = s / 2.0;
273 transform.scale( half, half );
274 }
275
276 if ( !qgsDoubleNear( angle, 0.0 ) && ( hasDataDefinedRotation || createdNewPath ) )
277 {
278 transform.rotate( angle );
279 }
280
281 //need to pass: symbol, polygon, path
282
283 QPolygonF polygon;
284 QPainterPath path;
285 if ( !mPolygon.isEmpty() )
286 {
287 polygon = transform.map( mPolygon );
288 }
289 else
290 {
291 path = transform.map( mPath );
292 }
293 draw( context, symbol, polygon, path );
294}
295
297{
298 bool hasDataDefinedSize = false;
299 double scaledSize = calculateSize( context, hasDataDefinedSize );
300
301 bool hasDataDefinedRotation = false;
302 QPointF offset;
303 double angle = 0;
304 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
305
306 scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
307
308 QTransform transform;
309
310 // move to the desired position
311 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
312
313 if ( !qgsDoubleNear( angle, 0.0 ) )
314 transform.rotate( angle );
315
316 return transform.mapRect( QRectF( -scaledSize / 2.0,
317 -scaledSize / 2.0,
318 scaledSize,
319 scaledSize ) );
320}
321
323{
324 if ( ok )
325 *ok = true;
326 const QString cleaned = name.toLower().trimmed();
327
328 if ( cleaned == QLatin1String( "square" ) || cleaned == QLatin1String( "rectangle" ) )
330 else if ( cleaned == QLatin1String( "trapezoid" ) )
332 else if ( cleaned == QLatin1String( "parallelogram_right" ) )
334 else if ( cleaned == QLatin1String( "parallelogram_left" ) )
336 else if ( cleaned == QLatin1String( "square_with_corners" ) )
338 else if ( cleaned == QLatin1String( "rounded_square" ) )
340 else if ( cleaned == QLatin1String( "diamond" ) )
342 else if ( cleaned == QLatin1String( "shield" ) )
344 else if ( cleaned == QLatin1String( "pentagon" ) )
346 else if ( cleaned == QLatin1String( "hexagon" ) )
348 else if ( cleaned == QLatin1String( "octagon" ) )
350 else if ( cleaned == QLatin1String( "decagon" ) )
352 else if ( cleaned == QLatin1String( "triangle" ) )
354 else if ( cleaned == QLatin1String( "equilateral_triangle" ) )
356 else if ( cleaned == QLatin1String( "star_diamond" ) )
358 else if ( cleaned == QLatin1String( "star" ) || cleaned == QLatin1String( "regular_star" ) )
360 else if ( cleaned == QLatin1String( "heart" ) )
362 else if ( cleaned == QLatin1String( "arrow" ) )
364 else if ( cleaned == QLatin1String( "circle" ) )
366 else if ( cleaned == QLatin1String( "cross" ) )
368 else if ( cleaned == QLatin1String( "cross_fill" ) )
370 else if ( cleaned == QLatin1String( "cross2" ) || cleaned == QLatin1String( "x" ) )
372 else if ( cleaned == QLatin1String( "line" ) )
374 else if ( cleaned == QLatin1String( "arrowhead" ) )
376 else if ( cleaned == QLatin1String( "filled_arrowhead" ) )
378 else if ( cleaned == QLatin1String( "semi_circle" ) )
380 else if ( cleaned == QLatin1String( "third_circle" ) )
382 else if ( cleaned == QLatin1String( "quarter_circle" ) )
384 else if ( cleaned == QLatin1String( "quarter_square" ) )
386 else if ( cleaned == QLatin1String( "half_square" ) )
388 else if ( cleaned == QLatin1String( "diagonal_half_square" ) )
390 else if ( cleaned == QLatin1String( "right_half_triangle" ) )
392 else if ( cleaned == QLatin1String( "left_half_triangle" ) )
394 else if ( cleaned == QLatin1String( "asterisk_fill" ) )
396 else if ( cleaned == QLatin1String( "half_arc" ) )
398 else if ( cleaned == QLatin1String( "third_arc" ) )
400 else if ( cleaned == QLatin1String( "quarter_arc" ) )
402
403 if ( ok )
404 *ok = false;
406}
407
409{
410 switch ( shape )
411 {
413 return QStringLiteral( "square" );
415 return QStringLiteral( "quarter_square" );
417 return QStringLiteral( "half_square" );
419 return QStringLiteral( "diagonal_half_square" );
421 return QStringLiteral( "parallelogram_right" );
423 return QStringLiteral( "parallelogram_left" );
425 return QStringLiteral( "trapezoid" );
427 return QStringLiteral( "shield" );
429 return QStringLiteral( "diamond" );
431 return QStringLiteral( "pentagon" );
433 return QStringLiteral( "hexagon" );
435 return QStringLiteral( "octagon" );
437 return QStringLiteral( "decagon" );
439 return QStringLiteral( "square_with_corners" );
441 return QStringLiteral( "rounded_square" );
443 return QStringLiteral( "triangle" );
445 return QStringLiteral( "equilateral_triangle" );
447 return QStringLiteral( "left_half_triangle" );
449 return QStringLiteral( "right_half_triangle" );
451 return QStringLiteral( "star_diamond" );
453 return QStringLiteral( "star" );
455 return QStringLiteral( "heart" );
457 return QStringLiteral( "arrow" );
459 return QStringLiteral( "filled_arrowhead" );
461 return QStringLiteral( "cross_fill" );
463 return QStringLiteral( "circle" );
465 return QStringLiteral( "cross" );
467 return QStringLiteral( "cross2" );
469 return QStringLiteral( "line" );
471 return QStringLiteral( "arrowhead" );
473 return QStringLiteral( "semi_circle" );
475 return QStringLiteral( "third_circle" );
477 return QStringLiteral( "quarter_circle" );
479 return QStringLiteral( "asterisk_fill" );
481 return QStringLiteral( "half_arc" );
483 return QStringLiteral( "third_arc" );
485 return QStringLiteral( "quarter_arc" );
486 }
487 return QString();
488}
489
494
496{
497 polygon.clear();
498
499 switch ( shape )
500 {
502 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 1, 1 ) ) );
503 return true;
504
506 {
507 static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 0.6072;
508
509 polygon << 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 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, -1 )
515 << QPointF( -1, -VERTEX_OFFSET_FROM_ORIGIN )
516 << QPointF( -1, VERTEX_OFFSET_FROM_ORIGIN )
517 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 );
518 return true;
519 }
520
522 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 0 ) ) );
523 return true;
524
526 polygon = QPolygonF( QRectF( QPointF( -1, -1 ), QPointF( 0, 1 ) ) );
527 return true;
528
530 polygon << QPointF( -1, -1 ) << QPointF( 1, 1 ) << QPointF( -1, 1 ) << QPointF( -1, -1 );
531 return true;
532
534 polygon << QPointF( 0.5, -0.5 )
535 << QPointF( 1, 0.5 )
536 << QPointF( -1, 0.5 )
537 << QPointF( -0.5, -0.5 )
538 << QPointF( 0.5, -0.5 );
539 return true;
540
542 polygon << QPointF( 0.5, 0.5 )
543 << QPointF( 1, -0.5 )
544 << QPointF( -0.5, -0.5 )
545 << QPointF( -1, 0.5 )
546 << QPointF( 0.5, 0.5 );
547 return true;
548
550 polygon << QPointF( 1, 0.5 )
551 << QPointF( 0.5, -0.5 )
552 << QPointF( -1, -0.5 )
553 << QPointF( -0.5, 0.5 )
554 << QPointF( 1, 0.5 );
555 return true;
556
558 polygon << QPointF( -1, 0 ) << QPointF( 0, 1 )
559 << QPointF( 1, 0 ) << QPointF( 0, -1 ) << QPointF( -1, 0 );
560 return true;
561
563 polygon << QPointF( 1, 0.5 )
564 << QPointF( 1, -1 )
565 << QPointF( -1, -1 )
566 << QPointF( -1, 0.5 )
567 << QPointF( 0, 1 )
568 << QPointF( 1, 0.5 );
569 return true;
570
572 /* angular-representation of hardcoded values used
573 polygon << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288.0 ) ) )
574 << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) )
575 << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) )
576 << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) )
577 << QPointF( 0, -1 ); */
578 polygon << QPointF( -0.9511, -0.3090 )
579 << QPointF( -0.5878, 0.8090 )
580 << QPointF( 0.5878, 0.8090 )
581 << QPointF( 0.9511, -0.3090 )
582 << QPointF( 0, -1 )
583 << QPointF( -0.9511, -0.3090 );
584 return true;
585
587 /* angular-representation of hardcoded values used
588 polygon << QPointF( std::sin( DEG2RAD( 300.0 ) ), - std::cos( DEG2RAD( 300.0 ) ) )
589 << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
590 << QPointF( std::sin( DEG2RAD( 180.0 ) ), - std::cos( DEG2RAD( 180.0 ) ) )
591 << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
592 << QPointF( std::sin( DEG2RAD( 60.0 ) ), - std::cos( DEG2RAD( 60.0 ) ) )
593 << QPointF( 0, -1 ); */
594 polygon << QPointF( -0.8660, -0.5 )
595 << QPointF( -0.8660, 0.5 )
596 << QPointF( 0, 1 )
597 << QPointF( 0.8660, 0.5 )
598 << QPointF( 0.8660, -0.5 )
599 << QPointF( 0, -1 )
600 << QPointF( -0.8660, -0.5 );
601 return true;
602
604 {
605 static constexpr double VERTEX_OFFSET_FROM_ORIGIN = 1.0 / ( 1 + M_SQRT2 );
606
607 polygon << 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 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, -1 )
613 << QPointF( -1, -VERTEX_OFFSET_FROM_ORIGIN )
614 << QPointF( -1, VERTEX_OFFSET_FROM_ORIGIN )
615 << QPointF( -VERTEX_OFFSET_FROM_ORIGIN, 1 );
616 return true;
617 }
618
620 {
621
622 polygon << QPointF( 0.587785252, 0.809016994 )
623 << QPointF( 0.951056516, 0.309016994 )
624 << QPointF( 0.951056516, -0.309016994 )
625 << QPointF( 0.587785252, -0.809016994 )
626 << QPointF( 0, -1 )
627 << QPointF( -0.587785252, -0.809016994 )
628 << QPointF( -0.951056516, -0.309016994 )
629 << QPointF( -0.951056516, 0.309016994 )
630 << QPointF( -0.587785252, 0.809016994 )
631 << QPointF( 0, 1 )
632 << QPointF( 0.587785252, 0.809016994 );
633 return true;
634 }
635
637 polygon << QPointF( -1, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
638 return true;
639
641 /* angular-representation of hardcoded values used
642 polygon << QPointF( std::sin( DEG2RAD( 240.0 ) ), - std::cos( DEG2RAD( 240.0 ) ) )
643 << QPointF( std::sin( DEG2RAD( 120.0 ) ), - std::cos( DEG2RAD( 120.0 ) ) )
644 << QPointF( 0, -1 ); */
645 polygon << QPointF( -0.8660, 0.5 )
646 << QPointF( 0.8660, 0.5 )
647 << QPointF( 0, -1 )
648 << QPointF( -0.8660, 0.5 );
649 return true;
650
652 polygon << QPointF( 0, 1 ) << QPointF( 1, 1 ) << QPointF( 0, -1 ) << QPointF( 0, 1 );
653 return true;
654
656 polygon << QPointF( -1, 1 ) << QPointF( 0, 1 ) << QPointF( 0, -1 ) << QPointF( -1, 1 );
657 return true;
658
660 {
661 const double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
662
663 polygon << QPointF( inner_r * std::sin( DEG2RAD( 315.0 ) ), - inner_r * std::cos( DEG2RAD( 315.0 ) ) )
664 << QPointF( std::sin( DEG2RAD( 270 ) ), - std::cos( DEG2RAD( 270 ) ) )
665 << QPointF( inner_r * std::sin( DEG2RAD( 225.0 ) ), - inner_r * std::cos( DEG2RAD( 225.0 ) ) )
666 << QPointF( std::sin( DEG2RAD( 180 ) ), - std::cos( DEG2RAD( 180 ) ) )
667 << QPointF( inner_r * std::sin( DEG2RAD( 135.0 ) ), - inner_r * std::cos( DEG2RAD( 135.0 ) ) )
668 << QPointF( std::sin( DEG2RAD( 90 ) ), - std::cos( DEG2RAD( 90 ) ) )
669 << QPointF( inner_r * std::sin( DEG2RAD( 45.0 ) ), - inner_r * std::cos( DEG2RAD( 45.0 ) ) )
670 << QPointF( std::sin( DEG2RAD( 0 ) ), - std::cos( DEG2RAD( 0 ) ) )
671 << QPointF( inner_r * std::sin( DEG2RAD( 315.0 ) ), - inner_r * std::cos( DEG2RAD( 315.0 ) ) );
672 return true;
673 }
674
676 {
677 const double inner_r = std::cos( DEG2RAD( 72.0 ) ) / std::cos( DEG2RAD( 36.0 ) );
678
679 polygon << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ) // 324
680 << QPointF( std::sin( DEG2RAD( 288.0 ) ), - std::cos( DEG2RAD( 288 ) ) ) // 288
681 << QPointF( inner_r * std::sin( DEG2RAD( 252.0 ) ), - inner_r * std::cos( DEG2RAD( 252.0 ) ) ) // 252
682 << QPointF( std::sin( DEG2RAD( 216.0 ) ), - std::cos( DEG2RAD( 216.0 ) ) ) // 216
683 << QPointF( 0, inner_r ) // 180
684 << QPointF( std::sin( DEG2RAD( 144.0 ) ), - std::cos( DEG2RAD( 144.0 ) ) ) // 144
685 << QPointF( inner_r * std::sin( DEG2RAD( 108.0 ) ), - inner_r * std::cos( DEG2RAD( 108.0 ) ) ) // 108
686 << QPointF( std::sin( DEG2RAD( 72.0 ) ), - std::cos( DEG2RAD( 72.0 ) ) ) // 72
687 << QPointF( inner_r * std::sin( DEG2RAD( 36.0 ) ), - inner_r * std::cos( DEG2RAD( 36.0 ) ) ) // 36
688 << QPointF( 0, -1 )
689 << QPointF( inner_r * std::sin( DEG2RAD( 324.0 ) ), - inner_r * std::cos( DEG2RAD( 324.0 ) ) ); // 324; // 0
690 return true;
691 }
692
694 polygon << QPointF( 0, -1 )
695 << QPointF( 0.5, -0.5 )
696 << QPointF( 0.25, -0.5 )
697 << QPointF( 0.25, 1 )
698 << QPointF( -0.25, 1 )
699 << QPointF( -0.25, -0.5 )
700 << QPointF( -0.5, -0.5 )
701 << QPointF( 0, -1 );
702 return true;
703
705 polygon << QPointF( 0, 0 ) << QPointF( -1, 1 ) << QPointF( -1, -1 ) << QPointF( 0, 0 );
706 return true;
707
709 polygon << QPointF( -1, -0.2 )
710 << QPointF( -1, -0.2 )
711 << QPointF( -1, 0.2 )
712 << QPointF( -0.2, 0.2 )
713 << QPointF( -0.2, 1 )
714 << QPointF( 0.2, 1 )
715 << QPointF( 0.2, 0.2 )
716 << QPointF( 1, 0.2 )
717 << QPointF( 1, -0.2 )
718 << QPointF( 0.2, -0.2 )
719 << QPointF( 0.2, -1 )
720 << QPointF( -0.2, -1 )
721 << QPointF( -0.2, -0.2 )
722 << QPointF( -1, -0.2 );
723 return true;
724
726 {
727 static constexpr double THICKNESS = 0.3;
728 static constexpr double HALF_THICKNESS = THICKNESS / 2.0;
729 static constexpr double INTERSECTION_POINT = THICKNESS / M_SQRT2;
730 static constexpr double DIAGONAL1 = M_SQRT1_2 - INTERSECTION_POINT * 0.5;
731 static constexpr double DIAGONAL2 = M_SQRT1_2 + INTERSECTION_POINT * 0.5;
732
733 polygon << QPointF( -HALF_THICKNESS, -1 )
734 << QPointF( HALF_THICKNESS, -1 )
735 << QPointF( HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
736 << QPointF( DIAGONAL1, -DIAGONAL2 )
737 << QPointF( DIAGONAL2, -DIAGONAL1 )
738 << QPointF( HALF_THICKNESS + INTERSECTION_POINT, -HALF_THICKNESS )
739 << QPointF( 1, -HALF_THICKNESS )
740 << QPointF( 1, HALF_THICKNESS )
741 << QPointF( HALF_THICKNESS + INTERSECTION_POINT, HALF_THICKNESS )
742 << QPointF( DIAGONAL2, DIAGONAL1 )
743 << QPointF( DIAGONAL1, DIAGONAL2 )
744 << QPointF( HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
745 << QPointF( HALF_THICKNESS, 1 )
746 << QPointF( -HALF_THICKNESS, 1 )
747 << QPointF( -HALF_THICKNESS, HALF_THICKNESS + INTERSECTION_POINT )
748 << QPointF( -DIAGONAL1, DIAGONAL2 )
749 << QPointF( -DIAGONAL2, DIAGONAL1 )
750 << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, HALF_THICKNESS )
751 << QPointF( -1, HALF_THICKNESS )
752 << QPointF( -1, -HALF_THICKNESS )
753 << QPointF( -HALF_THICKNESS - INTERSECTION_POINT, -HALF_THICKNESS )
754 << QPointF( -DIAGONAL2, -DIAGONAL1 )
755 << QPointF( -DIAGONAL1, -DIAGONAL2 )
756 << QPointF( -HALF_THICKNESS, -HALF_THICKNESS - INTERSECTION_POINT )
757 << QPointF( -HALF_THICKNESS, -1 );
758 return true;
759 }
760
774 return false;
775 }
776
777 return false;
778}
779
781{
782 mPath = QPainterPath();
783
784 switch ( symbol )
785 {
787
788 mPath.addEllipse( QRectF( -1, -1, 2, 2 ) ); // x,y,w,h
789 return true;
790
792 mPath.moveTo( -1, -1 );
793 mPath.addRoundedRect( -1, -1, 2, 2, 0.25, 0.25 );
794 return true;
795
797 mPath.arcTo( -1, -1, 2, 2, 0, 180 );
798 mPath.lineTo( 0, 0 );
799 return true;
800
802 mPath.arcTo( -1, -1, 2, 2, 90, 120 );
803 mPath.lineTo( 0, 0 );
804 return true;
805
807 mPath.arcTo( -1, -1, 2, 2, 90, 90 );
808 mPath.lineTo( 0, 0 );
809 return true;
810
812 mPath.moveTo( 1, 0 );
813 mPath.arcTo( -1, -1, 2, 2, 0, 180 );
814 return true;
815
817 mPath.moveTo( 0, -1 );
818 mPath.arcTo( -1, -1, 2, 2, 90, 120 );
819 return true;
820
822 mPath.moveTo( 0, -1 );
823 mPath.arcTo( -1, -1, 2, 2, 90, 90 );
824 return true;
825
827 mPath.moveTo( -1, 0 );
828 mPath.lineTo( 1, 0 ); // horizontal
829 mPath.moveTo( 0, -1 );
830 mPath.lineTo( 0, 1 ); // vertical
831 return true;
832
834 mPath.moveTo( -1, -1 );
835 mPath.lineTo( 1, 1 );
836 mPath.moveTo( 1, -1 );
837 mPath.lineTo( -1, 1 );
838 return true;
839
841 mPath.moveTo( 0, -1 );
842 mPath.lineTo( 0, 1 ); // vertical line
843 return true;
844
846 mPath.moveTo( -1, -1 );
847 mPath.lineTo( 0, 0 );
848 mPath.lineTo( -1, 1 );
849 return true;
850
852 mPath.moveTo( 0, 0.75 );
853 mPath.arcTo( 0, -1, 1, 1, -45, 210 );
854 mPath.arcTo( -1, -1, 1, 1, 15, 210 );
855 mPath.lineTo( 0, 0.75 );
856 return true;
857
882 return false;
883 }
884 return false;
885}
886
887double QgsSimpleMarkerSymbolLayerBase::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
888{
889 double scaledSize = mSize;
890
891 hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
892 bool ok = true;
893 if ( hasDataDefinedSize )
894 {
897 mSize, &ok );
898 }
899
900 if ( hasDataDefinedSize && ok )
901 {
902 switch ( mScaleMethod )
903 {
905 scaledSize = std::sqrt( scaledSize );
906 break;
908 break;
909 }
910 }
911
912 return scaledSize;
913}
914
915void QgsSimpleMarkerSymbolLayerBase::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedRotation, QPointF &offset, double &angle ) const
916{
917 //offset
918 double offsetX = 0;
919 double offsetY = 0;
920 markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
921 offset = QPointF( offsetX, offsetY );
922
923 hasDataDefinedRotation = false;
924 //angle
925 bool ok = true;
928 {
931
932 // If the expression evaluation was not successful, fallback to static value
933 if ( !ok )
935
936 hasDataDefinedRotation = true;
937 }
938
939 hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || hasDataDefinedRotation;
940
941 if ( hasDataDefinedRotation )
942 {
943 // For non-point markers, "dataDefinedRotation" means following the
944 // shape (shape-data defined). For them, "field-data defined" does
945 // not work at all. TODO: if "field-data defined" ever gets implemented
946 // we'll need a way to distinguish here between the two, possibly
947 // using another flag in renderHints()
948 const QgsFeature *f = context.feature();
949 if ( f )
950 {
951 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
952 {
953 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
954 angle += m2p.mapRotation();
955 }
956 }
957 }
958
959 if ( angle )
961}
962
963
964//
965// QgsSimpleMarkerSymbolLayer
966//
967
975
977
979{
987
988 if ( props.contains( QStringLiteral( "name" ) ) )
989 {
990 shape = decodeShape( props[QStringLiteral( "name" )].toString() );
991 }
992 if ( props.contains( QStringLiteral( "color" ) ) )
993 color = QgsColorUtils::colorFromString( props[QStringLiteral( "color" )].toString() );
994 if ( props.contains( QStringLiteral( "color_border" ) ) )
995 {
996 //pre 2.5 projects use "color_border"
997 strokeColor = QgsColorUtils::colorFromString( props[QStringLiteral( "color_border" )].toString() );
998 }
999 else if ( props.contains( QStringLiteral( "outline_color" ) ) )
1000 {
1001 strokeColor = QgsColorUtils::colorFromString( props[QStringLiteral( "outline_color" )].toString() );
1002 }
1003 else if ( props.contains( QStringLiteral( "line_color" ) ) )
1004 {
1005 strokeColor = QgsColorUtils::colorFromString( props[QStringLiteral( "line_color" )].toString() );
1006 }
1007 if ( props.contains( QStringLiteral( "joinstyle" ) ) )
1008 {
1009 penJoinStyle = QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() );
1010 }
1011 if ( props.contains( QStringLiteral( "size" ) ) )
1012 size = props[QStringLiteral( "size" )].toDouble();
1013 if ( props.contains( QStringLiteral( "angle" ) ) )
1014 angle = props[QStringLiteral( "angle" )].toDouble();
1015 if ( props.contains( QStringLiteral( "scale_method" ) ) )
1016 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1017
1019 if ( props.contains( QStringLiteral( "offset" ) ) )
1020 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1021 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1022 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1023 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1024 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1025 if ( props.contains( QStringLiteral( "size_unit" ) ) )
1026 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1027 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1028 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1029
1030 if ( props.contains( QStringLiteral( "outline_style" ) ) )
1031 {
1032 m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "outline_style" )].toString() ) );
1033 }
1034 else if ( props.contains( QStringLiteral( "line_style" ) ) )
1035 {
1036 m->setStrokeStyle( QgsSymbolLayerUtils::decodePenStyle( props[QStringLiteral( "line_style" )].toString() ) );
1037 }
1038 if ( props.contains( QStringLiteral( "outline_width" ) ) )
1039 {
1040 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
1041 }
1042 else if ( props.contains( QStringLiteral( "line_width" ) ) )
1043 {
1044 m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
1045 }
1046 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
1047 {
1048 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
1049 }
1050 if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
1051 {
1052 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
1053 }
1054 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
1055 {
1056 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
1057 }
1058
1059 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1060 {
1061 m->setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1062 }
1063 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1064 {
1065 m->setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1066 }
1067
1068 if ( props.contains( QStringLiteral( "cap_style" ) ) )
1069 {
1070 m->setPenCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( props[QStringLiteral( "cap_style" )].toString() ) );
1071 }
1072
1074
1075 return m;
1076}
1077
1078
1080{
1081 return QStringLiteral( "SimpleMarker" );
1082}
1083
1088
1090{
1092
1093 QColor brushColor = mColor;
1094 QColor penColor = mStrokeColor;
1095
1096 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
1097 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
1098
1099 mBrush = QBrush( brushColor );
1100 mPen = QPen( penColor );
1101 mPen.setStyle( mStrokeStyle );
1102 mPen.setCapStyle( mPenCapStyle );
1103 mPen.setJoinStyle( mPenJoinStyle );
1105
1106 QColor selBrushColor = context.renderContext().selectionColor();
1107 QColor selPenColor = selBrushColor == mColor ? selBrushColor : mStrokeColor;
1108 if ( context.opacity() < 1 && !SELECTION_IS_OPAQUE )
1109 {
1110 selBrushColor.setAlphaF( context.opacity() );
1111 selPenColor.setAlphaF( context.opacity() );
1112 }
1113 mSelBrush = QBrush( selBrushColor );
1114 mSelPen = QPen( selPenColor );
1115 mSelPen.setStyle( mStrokeStyle );
1117
1118 const bool hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Angle );
1119 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
1120
1121 // use caching only when:
1122 // - size, rotation, shape, color, stroke color is not data-defined
1123 // - drawing to screen (not printer)
1124 mUsingCache = !hasDataDefinedRotation && !hasDataDefinedSize && !context.forceVectorRendering()
1128
1129 if ( mUsingCache )
1130 mCachedOpacity = context.opacity();
1131
1132 if ( !shapeIsFilled( mShape ) )
1133 {
1134 // some markers can't be drawn as a polygon (circle, cross)
1135 // For these set the selected stroke color to the selected color
1136 mSelPen.setColor( selBrushColor );
1137 }
1138
1139
1140 if ( mUsingCache )
1141 {
1142 if ( !prepareCache( context ) )
1143 {
1144 mUsingCache = false;
1145 }
1146 }
1147 else
1148 {
1149 mCache = QImage();
1150 mSelCache = QImage();
1151 }
1152}
1153
1154
1156{
1157 double scaledSize = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
1158 const double deviceRatio = context.renderContext().devicePixelRatio();
1160 {
1161 // rendering for symbol previews -- a size in meters in map units can't be calculated, so treat the size as millimeters
1162 // and clamp it to a reasonable range. It's the best we can do in this situation!
1163 scaledSize = std::min( std::max( context.renderContext().convertToPainterUnits( mSize, Qgis::RenderUnit::Millimeters ), 3.0 ), 100.0 );
1164 }
1165
1166 // take into account angle (which is not data-defined otherwise cache wouldn't be used)
1167 if ( !qgsDoubleNear( mAngle, 0.0 ) )
1168 {
1169 scaledSize = ( std::abs( std::sin( mAngle * M_PI / 180 ) ) + std::abs( std::cos( mAngle * M_PI / 180 ) ) ) * scaledSize;
1170 }
1171 // calculate necessary image size for the cache
1172 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
1173 const int imageSize = ( static_cast< int >( scaledSize ) + pw ) / 2 * 2 + 1; // make image width, height odd; account for pen width
1174 const double center = imageSize / 2.0;
1175 if ( imageSize * deviceRatio > MAXIMUM_CACHE_WIDTH )
1176 {
1177 return false;
1178 }
1179
1180 mCache = QImage( QSize( imageSize * deviceRatio,
1181 imageSize * deviceRatio ), QImage::Format_ARGB32_Premultiplied );
1182 mCache.setDevicePixelRatio( context.renderContext().devicePixelRatio() );
1183 mCache.setDotsPerMeterX( std::round( context.renderContext().scaleFactor() * 1000 ) );
1184 mCache.setDotsPerMeterY( std::round( context.renderContext().scaleFactor() * 1000 ) );
1185 mCache.fill( 0 );
1186
1187 const bool needsBrush = shapeIsFilled( mShape );
1188
1189 QPainter p;
1190 p.begin( &mCache );
1191 p.setRenderHint( QPainter::Antialiasing );
1192 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1193 p.setPen( mPen );
1194 p.translate( QPointF( center, center ) );
1195 drawMarker( &p, context );
1196 p.end();
1197
1198 // Construct the selected version of the Cache
1199
1200 const QColor selColor = context.renderContext().selectionColor();
1201
1202 mSelCache = QImage( QSize( imageSize, imageSize ), QImage::Format_ARGB32_Premultiplied );
1203 mSelCache.fill( 0 );
1204
1205 p.begin( &mSelCache );
1206 p.setRenderHint( QPainter::Antialiasing );
1207 p.setBrush( needsBrush ? mSelBrush : Qt::NoBrush );
1208 p.setPen( mSelPen );
1209 p.translate( QPointF( center, center ) );
1210 drawMarker( &p, context );
1211 p.end();
1212
1213 // Check that the selected version is different. If not, then re-render,
1214 // filling the background with the selection color and using the normal
1215 // colors for the symbol .. could be ugly!
1216
1217 if ( mSelCache == mCache )
1218 {
1219 p.begin( &mSelCache );
1220 p.setRenderHint( QPainter::Antialiasing );
1221 p.fillRect( 0, 0, imageSize, imageSize, selColor );
1222 p.setBrush( needsBrush ? mBrush : Qt::NoBrush );
1223 p.setPen( mPen );
1224 p.translate( QPointF( center, center ) );
1225 drawMarker( &p, context );
1226 p.end();
1227 }
1228
1229 return true;
1230}
1231
1232void QgsSimpleMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
1233{
1234 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1235 //of the rendered point!
1236
1237 QPainter *p = context.renderContext().painter();
1238 if ( !p )
1239 {
1240 return;
1241 }
1242
1243 QColor brushColor = mColor;
1244 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
1245 mBrush.setColor( brushColor );
1246
1247 QColor penColor = mStrokeColor;
1248 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
1249 mPen.setColor( penColor );
1250
1251 bool ok = true;
1253 {
1256 if ( ok )
1257 {
1258 c.setAlphaF( c.alphaF() * context.opacity() );
1259 mBrush.setColor( c );
1260 }
1261 }
1263 {
1266 if ( ok )
1267 {
1268 c.setAlphaF( c.alphaF() * context.opacity() );
1269 mPen.setColor( c );
1270 mSelPen.setColor( c );
1271 }
1272 }
1274 {
1277 if ( ok )
1278 {
1281 }
1282 }
1284 {
1286 const QString strokeStyle = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::StrokeStyle, context.renderContext().expressionContext(), QString(), &ok );
1287 if ( ok )
1288 {
1291 }
1292 }
1294 {
1296 const QString style = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::JoinStyle, context.renderContext().expressionContext(), QString(), &ok );
1297 if ( ok )
1298 {
1299 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1300 mSelPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
1301 }
1302 }
1304 {
1306 const QString style = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::CapStyle, context.renderContext().expressionContext(), QString(), &ok );
1307 if ( ok )
1308 {
1309 mPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1310 mSelPen.setCapStyle( QgsSymbolLayerUtils::decodePenCapStyle( style ) );
1311 }
1312 }
1313
1314 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
1315 if ( shapeIsFilled( shape ) )
1316 {
1317 p->setBrush( useSelectedColor ? mSelBrush : mBrush );
1318 }
1319 else
1320 {
1321 p->setBrush( Qt::NoBrush );
1322 }
1323 p->setPen( useSelectedColor ? mSelPen : mPen );
1324
1325 if ( !polygon.isEmpty() )
1326 p->drawPolygon( polygon );
1327 else
1328 p->drawPath( path );
1329}
1330
1332{
1333 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
1334 //of the rendered point!
1335
1336 QPainter *p = context.renderContext().painter();
1337 if ( !p )
1338 {
1339 return;
1340 }
1341
1342 if ( mUsingCache && qgsDoubleNear( mCachedOpacity, context.opacity() ) )
1343 {
1344 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
1345 const QImage &img = useSelectedColor ? mSelCache : mCache;
1346 const double s = img.width() / img.devicePixelRatioF();
1347
1348 bool hasDataDefinedSize = false;
1349 const double scaledSize = calculateSize( context, hasDataDefinedSize );
1350
1351 bool hasDataDefinedRotation = false;
1352 QPointF offset;
1353 double angle = 0;
1354 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
1355
1356 p->drawImage( QRectF( point.x() - s / 2.0 + offset.x(),
1357 point.y() - s / 2.0 + offset.y(),
1358 s, s ), img );
1359 }
1360 else
1361 {
1363 }
1364}
1365
1367{
1368 QVariantMap map;
1369 map[QStringLiteral( "name" )] = encodeShape( mShape );
1370 map[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mColor );
1371 map[QStringLiteral( "outline_color" )] = QgsColorUtils::colorToString( mStrokeColor );
1372 map[QStringLiteral( "size" )] = QString::number( mSize );
1373 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1374 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1375 map[QStringLiteral( "angle" )] = QString::number( mAngle );
1376 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1377 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1378 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1379 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1380 map[QStringLiteral( "outline_style" )] = QgsSymbolLayerUtils::encodePenStyle( mStrokeStyle );
1381 map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
1382 map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
1383 map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
1384 map[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
1385 map[QStringLiteral( "cap_style" )] = QgsSymbolLayerUtils::encodePenCapStyle( mPenCapStyle );
1386 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
1387 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
1388 return map;
1389}
1390
1410
1411void QgsSimpleMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1412{
1413 QgsSldExportContext context;
1414 context.setExtraProperties( props );
1415 toSld( doc, element, context );
1416}
1417
1418bool QgsSimpleMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
1419{
1420 return QgsSimpleMarkerSymbolLayerBase::toSld( doc, element, context );
1421}
1422
1423void QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1424{
1425 QgsSldExportContext context;
1426 context.setExtraProperties( props );
1427 writeSldMarker( doc, element, context );
1428}
1429
1430bool QgsSimpleMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
1431{
1432 // <Graphic>
1433 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
1434 element.appendChild( graphicElem );
1435
1436 const QVariantMap props = context.extraProperties();
1438 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
1440
1441 // <Rotation>
1442 QString angleFunc;
1443
1445 {
1446 angleFunc = mDataDefinedProperties.property( QgsSymbolLayer::Property::Angle ).asExpression();
1447 }
1448 else
1449 {
1450 bool ok;
1451 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
1452 if ( !ok )
1453 {
1454 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
1455 }
1456 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
1457 {
1458 angleFunc = QString::number( angle + mAngle );
1459 }
1460 }
1461
1462 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc, context );
1463
1464 // <Displacement>
1465 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
1467 return true;
1468}
1469
1470QString QgsSimpleMarkerSymbolLayer::ogrFeatureStyle( double mmScaleFactor, double mapUnitScaleFactor ) const
1471{
1472 Q_UNUSED( mmScaleFactor )
1473 Q_UNUSED( mapUnitScaleFactor )
1474#if 0
1475 QString ogrType = "3"; //default is circle
1476 if ( mName == "square" )
1477 {
1478 ogrType = "5";
1479 }
1480 else if ( mName == "triangle" )
1481 {
1482 ogrType = "7";
1483 }
1484 else if ( mName == "star" )
1485 {
1486 ogrType = "9";
1487 }
1488 else if ( mName == "circle" )
1489 {
1490 ogrType = "3";
1491 }
1492 else if ( mName == "cross" )
1493 {
1494 ogrType = "0";
1495 }
1496 else if ( mName == "x" || mName == "cross2" )
1497 {
1498 ogrType = "1";
1499 }
1500 else if ( mName == "line" )
1501 {
1502 ogrType = "10";
1503 }
1504
1505 QString ogrString;
1506 ogrString.append( "SYMBOL(" );
1507 ogrString.append( "id:" );
1508 ogrString.append( '\"' );
1509 ogrString.append( "ogr-sym-" );
1510 ogrString.append( ogrType );
1511 ogrString.append( '\"' );
1512 ogrString.append( ",c:" );
1513 ogrString.append( mColor.name() );
1514 ogrString.append( ",o:" );
1515 ogrString.append( mStrokeColor.name() );
1516 ogrString.append( QString( ",s:%1mm" ).arg( mSize ) );
1517 ogrString.append( ')' );
1518 return ogrString;
1519#endif //0
1520
1521 QString ogrString;
1522 ogrString.append( "PEN(" );
1523 ogrString.append( "c:" );
1524 ogrString.append( mColor.name() );
1525 ogrString.append( ",w:" );
1526 ogrString.append( QString::number( mSize ) );
1527 ogrString.append( "mm" );
1528 ogrString.append( ")" );
1529 return ogrString;
1530}
1531
1533{
1534 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
1535
1536 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
1537 if ( graphicElem.isNull() )
1538 return nullptr;
1539
1540 QString name = QStringLiteral( "square" );
1541 QColor color, strokeColor;
1542 double strokeWidth, size;
1543 Qt::PenStyle strokeStyle;
1544
1546 return nullptr;
1547
1548 double angle = 0.0;
1549 QString angleFunc;
1550 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
1551 {
1552 bool ok;
1553 const double d = angleFunc.toDouble( &ok );
1554 if ( ok )
1555 angle = d;
1556 }
1557
1558 QPointF offset;
1560
1561 const Qgis::MarkerShape shape = decodeShape( name );
1562
1563 double scaleFactor = 1.0;
1564 const QString uom = element.attribute( QStringLiteral( "uom" ) );
1565 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
1566 size = size * scaleFactor;
1567 offset.setX( offset.x() * scaleFactor );
1568 offset.setY( offset.y() * scaleFactor );
1569
1571 m->setOutputUnit( sldUnitSize );
1572 m->setColor( color );
1574 m->setAngle( angle );
1575 m->setOffset( offset );
1578 return m;
1579}
1580
1582{
1583 Q_UNUSED( context )
1584
1585 if ( mPolygon.count() != 0 )
1586 {
1587 p->drawPolygon( mPolygon );
1588 }
1589 else
1590 {
1591 p->drawPath( mPath );
1592 }
1593}
1594
1595bool QgsSimpleMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
1596{
1597 //data defined size?
1598 double size = mSize;
1599
1600 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
1601
1602 //data defined size
1603 bool ok = true;
1604 if ( hasDataDefinedSize )
1605 {
1607
1608 if ( ok )
1609 {
1610 switch ( mScaleMethod )
1611 {
1613 size = std::sqrt( size );
1614 break;
1616 break;
1617 }
1618 }
1619 }
1620
1622 {
1623 size *= mmMapUnitScaleFactor;
1624 }
1625
1627 {
1629 }
1630 const double halfSize = size / 2.0;
1631
1632 //strokeWidth
1633 double strokeWidth = mStrokeWidth;
1634
1636 {
1639 }
1642 {
1644 }
1645
1646 //color
1647 QColor pc = mPen.color();
1648 QColor bc = mBrush.color();
1650 {
1653 }
1655 {
1658 }
1659
1660 //offset
1661 double offsetX = 0;
1662 double offsetY = 0;
1663 markerOffset( context, offsetX, offsetY );
1664 offsetX *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1665 offsetY *= context.renderContext().mapToPixel().mapUnitsPerPixel();
1666
1667
1668 QPointF off( offsetX, offsetY );
1669
1670 //angle
1671 double angle = mAngle + mLineAngle;
1673 {
1676 }
1677
1680 {
1682 const QString shapeName = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::Name, context.renderContext().expressionContext(), QString(), &ok );
1683 if ( ok )
1684 {
1685 shape = decodeShape( shapeName, &ok );
1686 if ( !ok )
1687 shape = mShape;
1688 }
1689 }
1690
1691 if ( angle )
1692 off = _rotatedOffset( off, angle );
1693
1695
1696 QTransform t;
1697 t.translate( shift.x() + off.x(), shift.y() - off.y() );
1698
1699 if ( !qgsDoubleNear( angle, 0.0 ) )
1700 t.rotate( -angle );
1701
1702 QPolygonF polygon;
1703 if ( shapeToPolygon( shape, polygon ) )
1704 {
1705 t.scale( halfSize, -halfSize );
1706
1707 polygon = t.map( polygon );
1708
1710 p.reserve( polygon.size() );
1711 for ( int i = 0; i < polygon.size(); i++ )
1712 {
1713 p << QgsPoint( polygon[i] );
1714 }
1715
1716 if ( mBrush.style() != Qt::NoBrush )
1717 e.writePolygon( QgsRingSequence() << p, layerName, QStringLiteral( "SOLID" ), bc );
1718 if ( mPen.style() != Qt::NoPen )
1719 e.writePolyline( p, layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1720 }
1721 else if ( shape == Qgis::MarkerShape::Circle )
1722 {
1723 shift += QPointF( off.x(), -off.y() );
1724 if ( mBrush.style() != Qt::NoBrush )
1725 e.writeFilledCircle( layerName, bc, QgsPoint( shift ), halfSize );
1726 if ( mPen.style() != Qt::NoPen )
1727 e.writeCircle( layerName, pc, QgsPoint( shift ), halfSize, QStringLiteral( "CONTINUOUS" ), strokeWidth );
1728 }
1729 else if ( shape == Qgis::MarkerShape::Line )
1730 {
1731 const QPointF pt1 = t.map( QPointF( 0, -halfSize ) );
1732 const QPointF pt2 = t.map( QPointF( 0, halfSize ) );
1733
1734 if ( mPen.style() != Qt::NoPen )
1735 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1736 }
1737 else if ( shape == Qgis::MarkerShape::Cross )
1738 {
1739 if ( mPen.style() != Qt::NoPen )
1740 {
1741 const QPointF pt1 = t.map( QPointF( -halfSize, 0 ) );
1742 const QPointF pt2 = t.map( QPointF( halfSize, 0 ) );
1743 const QPointF pt3 = t.map( QPointF( 0, -halfSize ) );
1744 const QPointF pt4 = t.map( QPointF( 0, halfSize ) );
1745
1746 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1747 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1748 }
1749 }
1750 else if ( shape == Qgis::MarkerShape::Cross2 )
1751 {
1752 if ( mPen.style() != Qt::NoPen )
1753 {
1754 const QPointF pt1 = t.map( QPointF( -halfSize, -halfSize ) );
1755 const QPointF pt2 = t.map( QPointF( halfSize, halfSize ) );
1756 const QPointF pt3 = t.map( QPointF( halfSize, -halfSize ) );
1757 const QPointF pt4 = t.map( QPointF( -halfSize, halfSize ) );
1758
1759 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1760 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt4 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1761 }
1762 }
1763 else if ( shape == Qgis::MarkerShape::ArrowHead )
1764 {
1765 if ( mPen.style() != Qt::NoPen )
1766 {
1767 const QPointF pt1 = t.map( QPointF( -halfSize, halfSize ) );
1768 const QPointF pt2 = t.map( QPointF( 0, 0 ) );
1769 const QPointF pt3 = t.map( QPointF( -halfSize, -halfSize ) );
1770
1771 e.writeLine( QgsPoint( pt1 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1772 e.writeLine( QgsPoint( pt3 ), QgsPoint( pt2 ), layerName, QStringLiteral( "CONTINUOUS" ), pc, strokeWidth );
1773 }
1774 }
1775 else
1776 {
1777 QgsDebugError( QStringLiteral( "Unsupported dxf marker name %1" ).arg( encodeShape( shape ) ) );
1778 return false;
1779 }
1780
1781 return true;
1782}
1783
1784
1790
1799
1805
1814
1821
1823{
1824 QRectF symbolBounds = QgsSimpleMarkerSymbolLayerBase::bounds( point, context );
1825
1826 // need to account for stroke width
1827 double penWidth = mStrokeWidth;
1828 bool ok = true;
1830 {
1833 if ( ok )
1834 {
1835 penWidth = strokeWidth;
1836 }
1837 }
1840 {
1842 const QString strokeStyle = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::StrokeStyle, context.renderContext().expressionContext(), QString(), &ok );
1843 if ( ok && strokeStyle == QLatin1String( "no" ) )
1844 {
1845 penWidth = 0.0;
1846 }
1847 }
1848 else if ( mStrokeStyle == Qt::NoPen )
1849 penWidth = 0;
1850
1851 //antialiasing, add 1 pixel
1852 penWidth += 1;
1853
1854 //extend bounds by pen width / 2.0
1855 symbolBounds.adjust( -penWidth / 2.0, -penWidth / 2.0,
1856 penWidth / 2.0, penWidth / 2.0 );
1857
1858 return symbolBounds;
1859}
1860
1862{
1863 if ( shapeIsFilled( mShape ) )
1864 {
1866 }
1867 else
1868 {
1870 }
1871}
1872
1874{
1875 if ( shapeIsFilled( mShape ) )
1876 {
1877 return fillColor();
1878 }
1879 else
1880 {
1881 return strokeColor();
1882 }
1883}
1884
1885
1886
1887
1888//
1889// QgsFilledMarkerSymbolLayer
1890//
1891
1897
1899
1901{
1902 QString name = DEFAULT_SIMPLEMARKER_NAME;
1906
1907 if ( props.contains( QStringLiteral( "name" ) ) )
1908 name = props[QStringLiteral( "name" )].toString();
1909 if ( props.contains( QStringLiteral( "size" ) ) )
1910 size = props[QStringLiteral( "size" )].toDouble();
1911 if ( props.contains( QStringLiteral( "angle" ) ) )
1912 angle = props[QStringLiteral( "angle" )].toDouble();
1913 if ( props.contains( QStringLiteral( "scale_method" ) ) )
1914 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
1915
1917 if ( props.contains( QStringLiteral( "offset" ) ) )
1918 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
1919 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
1920 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
1921 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
1922 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
1923 if ( props.contains( QStringLiteral( "size_unit" ) ) )
1924 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
1925 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
1926 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
1927 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
1928 {
1929 m->setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
1930 }
1931 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
1932 {
1933 m->setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
1934 }
1935
1936 m->setSubSymbol( QgsFillSymbol::createSimple( props ).release() );
1937
1939
1940 return m;
1941}
1942
1944{
1945 return QStringLiteral( "FilledMarker" );
1946}
1947
1949{
1950 if ( mFill )
1951 {
1952 mFill->setRenderHints( mFill->renderHints() | Qgis::SymbolRenderHint::IsSymbolLayerSubSymbol );
1953 mFill->startRender( context.renderContext(), context.fields() );
1954 }
1955
1957}
1958
1960{
1961 if ( mFill )
1962 {
1963 mFill->stopRender( context.renderContext() );
1964 }
1965}
1966
1968{
1969 installMasks( context, true );
1970
1971 // The base class version passes this on to the subsymbol, but we deliberately don't do that here.
1972}
1973
1975{
1976 removeMasks( context, true );
1977
1978 // The base class version passes this on to the subsymbol, but we deliberately don't do that here.
1979}
1980
1982{
1983 QVariantMap map;
1984 map[QStringLiteral( "name" )] = encodeShape( mShape );
1985 map[QStringLiteral( "size" )] = QString::number( mSize );
1986 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
1987 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
1988 map[QStringLiteral( "angle" )] = QString::number( mAngle );
1989 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
1990 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
1991 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
1992 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
1993 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
1994 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
1995
1996 if ( mFill )
1997 {
1998 map[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mFill->color() );
1999 }
2000 return map;
2001}
2002
2011
2013{
2014 return mFill.get();
2015}
2016
2018{
2019 if ( symbol && symbol->type() == Qgis::SymbolType::Fill )
2020 {
2021 mFill.reset( static_cast<QgsFillSymbol *>( symbol ) );
2022 return true;
2023 }
2024 else
2025 {
2026 delete symbol;
2027 return false;
2028 }
2029}
2030
2032{
2033 if ( mFill )
2034 {
2035 return QgsSymbolLayerUtils::estimateMaxSymbolBleed( mFill.get(), context );
2036 }
2037 return 0;
2038}
2039
2041{
2042 QSet<QString> attr = QgsSimpleMarkerSymbolLayerBase::usedAttributes( context );
2043 if ( mFill )
2044 attr.unite( mFill->usedAttributes( context ) );
2045 return attr;
2046}
2047
2049{
2051 return true;
2052 if ( mFill && mFill->hasDataDefinedProperties() )
2053 return true;
2054 return false;
2055}
2056
2058{
2059 mColor = c;
2060 if ( mFill )
2061 mFill->setColor( c );
2062}
2063
2065{
2066 return mFill ? mFill->color() : mColor;
2067}
2068
2075
2077{
2079 if ( mFill )
2080 mFill->setOutputUnit( unit );
2081}
2082
2083void QgsFilledMarkerSymbolLayer::draw( QgsSymbolRenderContext &context, Qgis::MarkerShape shape, const QPolygonF &polygon, const QPainterPath &path )
2084{
2085 //making changes here? Don't forget to also update ::bounds if the changes affect the bounding box
2086 //of the rendered point!
2087
2088 QPainter *p = context.renderContext().painter();
2089 if ( !p )
2090 {
2091 return;
2092 }
2093
2094 const double prevOpacity = mFill->opacity();
2095 mFill->setOpacity( mFill->opacity() * context.opacity() );
2096
2097 if ( shapeIsFilled( shape ) )
2098 {
2099 p->setBrush( Qt::red );
2100 }
2101 else
2102 {
2103 p->setBrush( Qt::NoBrush );
2104 }
2105 p->setPen( Qt::black );
2106
2107 const bool prevIsSubsymbol = context.renderContext().flags() & Qgis::RenderContextFlag::RenderingSubSymbol;
2109
2110 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
2111 if ( !polygon.isEmpty() )
2112 {
2113 mFill->renderPolygon( polygon, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
2114 }
2115 else
2116 {
2117 const QPolygonF poly = path.toFillPolygon();
2118 mFill->renderPolygon( poly, /* rings */ nullptr, context.feature(), context.renderContext(), -1, useSelectedColor );
2119 }
2120
2122
2123 mFill->setOpacity( prevOpacity );
2124}
2125
2126
2128
2129
2131{
2132 mSize = size;
2133 mAngle = angle;
2134 mOffset = QPointF( 0, 0 );
2136 mStrokeWidth = 0.2;
2138 mColor = QColor( 35, 35, 35 );
2139 mStrokeColor = QColor( 35, 35, 35 );
2140 setPath( path );
2141}
2142
2156
2158
2160{
2161 QString name;
2165
2166 if ( props.contains( QStringLiteral( "name" ) ) )
2167 name = props[QStringLiteral( "name" )].toString();
2168 if ( props.contains( QStringLiteral( "size" ) ) )
2169 size = props[QStringLiteral( "size" )].toDouble();
2170 if ( props.contains( QStringLiteral( "angle" ) ) )
2171 angle = props[QStringLiteral( "angle" )].toDouble();
2172 if ( props.contains( QStringLiteral( "scale_method" ) ) )
2173 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
2174
2176
2177 if ( props.contains( QStringLiteral( "size_unit" ) ) )
2178 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
2179 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
2180 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
2181 if ( props.contains( QStringLiteral( "fixedAspectRatio" ) ) )
2182 m->setFixedAspectRatio( props[QStringLiteral( "fixedAspectRatio" )].toDouble() );
2183 if ( props.contains( QStringLiteral( "offset" ) ) )
2184 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
2185 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
2186 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
2187 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
2188 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
2189 if ( props.contains( QStringLiteral( "fill" ) ) )
2190 {
2191 //pre 2.5 projects used "fill"
2192 m->setFillColor( QgsColorUtils::colorFromString( props[QStringLiteral( "fill" )].toString() ) );
2193 }
2194 else if ( props.contains( QStringLiteral( "color" ) ) )
2195 {
2196 m->setFillColor( QgsColorUtils::colorFromString( props[QStringLiteral( "color" )].toString() ) );
2197 }
2198 if ( props.contains( QStringLiteral( "outline" ) ) )
2199 {
2200 //pre 2.5 projects used "outline"
2201 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "outline" )].toString() ) );
2202 }
2203 else if ( props.contains( QStringLiteral( "outline_color" ) ) )
2204 {
2205 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "outline_color" )].toString() ) );
2206 }
2207 else if ( props.contains( QStringLiteral( "line_color" ) ) )
2208 {
2209 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "line_color" )].toString() ) );
2210 }
2211
2212 if ( props.contains( QStringLiteral( "outline-width" ) ) )
2213 {
2214 //pre 2.5 projects used "outline-width"
2215 m->setStrokeWidth( props[QStringLiteral( "outline-width" )].toDouble() );
2216 }
2217 else if ( props.contains( QStringLiteral( "outline_width" ) ) )
2218 {
2219 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
2220 }
2221 else if ( props.contains( QStringLiteral( "line_width" ) ) )
2222 {
2223 m->setStrokeWidth( props[QStringLiteral( "line_width" )].toDouble() );
2224 }
2225
2226 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
2227 {
2228 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
2229 }
2230 else if ( props.contains( QStringLiteral( "line_width_unit" ) ) )
2231 {
2232 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "line_width_unit" )].toString() ) );
2233 }
2234 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
2235 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
2236
2237 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
2238 {
2239 m->setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
2240 }
2241 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
2242 {
2243 m->setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
2244 }
2245
2247
2249
2250 if ( props.contains( QStringLiteral( "parameters" ) ) )
2251 {
2252 const QVariantMap parameters = props[QStringLiteral( "parameters" )].toMap();
2254 }
2255
2256 return m;
2257}
2258
2259void QgsSvgMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
2260{
2261 const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
2262 if ( it != properties.end() )
2263 {
2264 if ( saving )
2265 {
2266 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
2267 }
2268 else
2269 {
2270 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
2271 }
2272 }
2273}
2274
2276{
2278 mHasFillParam = false;
2279 mPath = path;
2280 QColor defaultFillColor, defaultStrokeColor;
2281 double strokeWidth, fillOpacity, strokeOpacity;
2282 bool hasFillOpacityParam = false, hasStrokeParam = false, hasStrokeWidthParam = false, hasStrokeOpacityParam = false;
2283 bool hasDefaultFillColor = false, hasDefaultFillOpacity = false, hasDefaultStrokeColor = false, hasDefaultStrokeWidth = false, hasDefaultStrokeOpacity = false;
2284 QgsApplication::svgCache()->containsParams( path, mHasFillParam, hasDefaultFillColor, defaultFillColor,
2285 hasFillOpacityParam, hasDefaultFillOpacity, fillOpacity,
2286 hasStrokeParam, hasDefaultStrokeColor, defaultStrokeColor,
2287 hasStrokeWidthParam, hasDefaultStrokeWidth, strokeWidth,
2288 hasStrokeOpacityParam, hasDefaultStrokeOpacity, strokeOpacity );
2289
2290 const double newFillOpacity = hasFillOpacityParam ? fillColor().alphaF() : 1.0;
2291 const double newStrokeOpacity = hasStrokeOpacityParam ? strokeColor().alphaF() : 1.0;
2292
2293 if ( hasDefaultFillColor )
2294 {
2295 defaultFillColor.setAlphaF( newFillOpacity );
2296 setFillColor( defaultFillColor );
2297 }
2298 if ( hasDefaultFillOpacity )
2299 {
2300 QColor c = fillColor();
2301 c.setAlphaF( fillOpacity );
2302 setFillColor( c );
2303 }
2304 if ( hasDefaultStrokeColor )
2305 {
2306 defaultStrokeColor.setAlphaF( newStrokeOpacity );
2307 setStrokeColor( defaultStrokeColor );
2308 }
2309 if ( hasDefaultStrokeWidth )
2310 {
2312 }
2313 if ( hasDefaultStrokeOpacity )
2314 {
2315 QColor c = strokeColor();
2316 c.setAlphaF( strokeOpacity );
2317 setStrokeColor( c );
2318 }
2319
2321}
2322
2324{
2325 if ( mDefaultAspectRatio == 0.0 )
2326 {
2327 //size
2328 const double size = mSize;
2329 //assume 88 dpi as standard value
2330 const double widthScaleFactor = 3.465;
2331 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( mPath, size, mColor, mStrokeColor, mStrokeWidth, widthScaleFactor );
2332 // set default aspect ratio
2333 mDefaultAspectRatio = svgViewbox.isValid() ? svgViewbox.height() / svgViewbox.width() : 0.0;
2334 }
2335 return mDefaultAspectRatio;
2336}
2337
2339{
2340 const bool aPreservedAspectRatio = preservedAspectRatio();
2341 if ( aPreservedAspectRatio && !par )
2342 {
2344 }
2345 else if ( !aPreservedAspectRatio && par )
2346 {
2347 mFixedAspectRatio = 0.0;
2348 }
2349 return preservedAspectRatio();
2350}
2351
2352void QgsSvgMarkerSymbolLayer::setParameters( const QMap<QString, QgsProperty> &parameters )
2353{
2355}
2356
2357
2359{
2360 return QStringLiteral( "SvgMarker" );
2361}
2362
2367
2369{
2370 QgsMarkerSymbolLayer::startRender( context ); // get anchor point expressions
2371 Q_UNUSED( context )
2372}
2373
2375{
2376 Q_UNUSED( context )
2377}
2378
2380{
2381 QPainter *p = context.renderContext().painter();
2382 if ( !p )
2383 return;
2384
2385 bool hasDataDefinedSize = false;
2386 const double scaledWidth = calculateSize( context, hasDataDefinedSize );
2387 const double devicePixelRatio = context.renderContext().devicePixelRatio();
2388 const double width = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
2389
2390 //don't render symbols with a width below one or above 10,000 pixels
2391 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
2392 {
2393 return;
2394 }
2395
2396 const QgsScopedQPainterState painterState( p );
2397
2398 bool hasDataDefinedAspectRatio = false;
2399 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2400 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
2401
2403
2404 double strokeWidth = mStrokeWidth;
2406 {
2409 }
2411
2412 QColor fillColor = mColor;
2413 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
2414 if ( useSelectedColor && mHasFillParam )
2415 {
2417 }
2419 {
2422 }
2423
2424 QColor strokeColor = mStrokeColor;
2426 {
2429 }
2430
2431 QString path = mPath;
2433 {
2436 context.renderContext().pathResolver() );
2437 if ( path != mPath && qgsDoubleNear( aspectRatio, 0.0 ) && !mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Height ) )
2438 {
2439 // adjust height of data defined path
2440 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
2441 context.renderContext().scaleFactor(), aspectRatio,
2442 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2443 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
2444 }
2445 }
2446
2447 QPointF outputOffset;
2448 double angle = 0.0;
2449 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
2450
2451 p->translate( point + outputOffset );
2452
2453 const bool rotated = !qgsDoubleNear( angle, 0 );
2454 if ( rotated )
2455 p->rotate( angle );
2456
2457 bool fitsInCache = true;
2458 bool usePict = true;
2459 const bool rasterizeSelected = !mHasFillParam || mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Name );
2460 if ( ( !context.forceVectorRendering() && !rotated ) || ( useSelectedColor && rasterizeSelected ) )
2461 {
2462 QImage img = QgsApplication::svgCache()->svgAsImage( path, width * devicePixelRatio, fillColor, strokeColor, strokeWidth,
2463 context.renderContext().scaleFactor(), fitsInCache, aspectRatio,
2464 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2465 if ( fitsInCache && img.width() > 1 )
2466 {
2467 usePict = false;
2468
2469 if ( useSelectedColor )
2470 {
2472 }
2473
2474 //consider transparency
2475 if ( !qgsDoubleNear( context.opacity(), 1.0 ) )
2476 {
2477 QImage transparentImage = img.copy();
2478 QgsSymbolLayerUtils::multiplyImageOpacity( &transparentImage, context.opacity() );
2479 if ( devicePixelRatio == 1 )
2480 {
2481 p->drawImage( -transparentImage.width() / 2.0, -transparentImage.height() / 2.0, transparentImage );
2482 }
2483 else
2484 {
2485 p->drawImage( QRectF( -transparentImage.width() / 2.0 / devicePixelRatio, -transparentImage.height() / 2.0 / devicePixelRatio,
2486 transparentImage.width() / devicePixelRatio, transparentImage.height() / devicePixelRatio
2487 ), transparentImage );
2488 }
2489 }
2490 else
2491 {
2492 if ( devicePixelRatio == 1 )
2493 {
2494 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
2495 }
2496 else
2497 {
2498 p->drawImage( QRectF( -img.width() / 2.0 / devicePixelRatio, -img.height() / 2.0 / devicePixelRatio,
2499 img.width() / devicePixelRatio, img.height() / devicePixelRatio ), img );
2500 }
2501 }
2502 }
2503 }
2504
2505 if ( usePict || !fitsInCache )
2506 {
2507 p->setOpacity( context.opacity() );
2509 context.renderContext().scaleFactor(), context.forceVectorRendering(), aspectRatio,
2510 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2511 if ( pct.width() > 1 )
2512 {
2513 QgsPainting::drawPicture( p, QPointF( 0, 0 ), pct );
2514 }
2515 }
2516
2517 // workaround issue with nested QPictures forgetting antialiasing flag - see https://github.com/qgis/QGIS/issues/22909
2519}
2520
2521double QgsSvgMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
2522{
2523 double scaledSize = mSize;
2525
2526 bool ok = true;
2527 if ( hasDataDefinedSize )
2528 {
2531 }
2532 else
2533 {
2534 hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Width );
2535 if ( hasDataDefinedSize )
2536 {
2538 scaledSize = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::Width, context.renderContext().expressionContext(), mSize, &ok );
2539 }
2540 }
2541
2542 if ( hasDataDefinedSize && ok )
2543 {
2544 switch ( mScaleMethod )
2545 {
2547 scaledSize = std::sqrt( scaledSize );
2548 break;
2550 break;
2551 }
2552 }
2553
2554 return scaledSize;
2555}
2556
2557double QgsSvgMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
2558{
2560 if ( !hasDataDefinedAspectRatio )
2561 return mFixedAspectRatio;
2562
2564 return 0.0;
2565
2566 double scaledAspectRatio = mDefaultAspectRatio;
2567 if ( mFixedAspectRatio > 0.0 )
2568 scaledAspectRatio = mFixedAspectRatio;
2569
2570 const double defaultHeight = mSize * scaledAspectRatio;
2571 scaledAspectRatio = defaultHeight / scaledSize;
2572
2573 bool ok = true;
2574 double scaledHeight = scaledSize * scaledAspectRatio;
2576 {
2577 context.setOriginalValueVariable( defaultHeight );
2578 scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::Height, context.renderContext().expressionContext(), defaultHeight, &ok );
2579 }
2580
2581 if ( hasDataDefinedAspectRatio && ok )
2582 {
2583 switch ( mScaleMethod )
2584 {
2586 scaledHeight = sqrt( scaledHeight );
2587 break;
2589 break;
2590 }
2591 }
2592
2593 scaledAspectRatio = scaledHeight / scaledSize;
2594
2595 return scaledAspectRatio;
2596}
2597
2598void QgsSvgMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
2599{
2600 //offset
2601 double offsetX = 0;
2602 double offsetY = 0;
2603 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
2604 offset = QPointF( offsetX, offsetY );
2605
2608 {
2611 }
2612
2613 const bool hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Angle );
2614 if ( hasDataDefinedRotation )
2615 {
2616 // For non-point markers, "dataDefinedRotation" means following the
2617 // shape (shape-data defined). For them, "field-data defined" does
2618 // not work at all. TODO: if "field-data defined" ever gets implemented
2619 // we'll need a way to distinguish here between the two, possibly
2620 // using another flag in renderHints()
2621 const QgsFeature *f = context.feature();
2622 if ( f )
2623 {
2624 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
2625 {
2626 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
2627 angle += m2p.mapRotation();
2628 }
2629 }
2630 }
2631
2632 if ( angle )
2634}
2635
2636
2638{
2639 QVariantMap map;
2640 map[QStringLiteral( "name" )] = mPath;
2641 map[QStringLiteral( "size" )] = QString::number( mSize );
2642 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
2643 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
2644 map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
2645 map[QStringLiteral( "angle" )] = QString::number( mAngle );
2646 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
2647 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
2648 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
2649 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
2650 map[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mColor );
2651 map[QStringLiteral( "outline_color" )] = QgsColorUtils::colorToString( mStrokeColor );
2652 map[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
2653 map[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
2654 map[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
2655 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
2656 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
2657
2658 map[QStringLiteral( "parameters" )] = QgsProperty::propertyMapToVariantMap( mParameters );
2659
2660 return map;
2661}
2662
2669
2674
2675void QgsSvgMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
2676{
2677 QgsSldExportContext context;
2678 context.setExtraProperties( props );
2679 toSld( doc, element, context );
2680}
2681
2682bool QgsSvgMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
2683{
2684 return QgsMarkerSymbolLayer::toSld( doc, element, context );
2685}
2686
2692
2694{
2696 if ( unit != mStrokeWidthUnit )
2697 {
2699 }
2700 return unit;
2701}
2702
2708
2717
2718void QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
2719{
2720 QgsSldExportContext context;
2721 context.setExtraProperties( props );
2722 writeSldMarker( doc, element, context );
2723}
2724
2725bool QgsSvgMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
2726{
2727 // <Graphic>
2728 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
2729 element.appendChild( graphicElem );
2730
2731 const QVariantMap props = context.extraProperties();
2732 // encode a parametric SVG reference
2733 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
2736
2737 // <Rotation>
2738 QString angleFunc;
2739 bool ok;
2740 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
2741 if ( !ok )
2742 {
2743 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
2744 }
2745 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
2746 {
2747 angleFunc = QString::number( angle + mAngle );
2748 }
2749
2750 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc, context );
2751
2752 // <Displacement>
2753 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
2755 return true;
2756}
2757
2759{
2760 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
2761
2762 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
2763 if ( graphicElem.isNull() )
2764 return nullptr;
2765
2766 QString path, mimeType;
2767 // Unused and to be DEPRECATED in externalGraphicFromSld
2768 QColor fillColor_;
2769 double size;
2770
2771 if ( !QgsSymbolLayerUtils::externalGraphicFromSld( graphicElem, path, mimeType, fillColor_, size ) )
2772 return nullptr;
2773
2774 double scaleFactor = 1.0;
2775 const QString uom = element.attribute( QStringLiteral( "uom" ) );
2776 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
2777 size = size * scaleFactor;
2778
2779 if ( mimeType != QLatin1String( "image/svg+xml" ) )
2780 return nullptr;
2781
2782 double angle = 0.0;
2783 QString angleFunc;
2784 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
2785 {
2786 bool ok;
2787 const double d = angleFunc.toDouble( &ok );
2788 if ( ok )
2789 angle = d;
2790 }
2791
2792 QPointF offset;
2794
2795 // Extract parameters from URL
2796 QString realPath { path };
2797 QUrl svgUrl { path };
2798
2799 // Because color definition can start with '#', the url parsing won't recognize the query string entirely
2800 QUrlQuery queryString;
2801
2802 if ( svgUrl.hasQuery() && svgUrl.hasFragment() )
2803 {
2804 const QString queryPart { path.mid( path.indexOf( '?' ) + 1 ) };
2805 queryString.setQuery( queryPart );
2806 }
2807
2808 // Remove query for simple file paths
2809 if ( svgUrl.scheme().isEmpty() || svgUrl.isLocalFile() )
2810 {
2811 svgUrl.setQuery( QString() );
2812 realPath = svgUrl.path();
2813 }
2814
2816
2817 QMap<QString, QgsProperty> params;
2818
2819 bool ok;
2820
2821 if ( queryString.hasQueryItem( QStringLiteral( "fill" ) ) )
2822 {
2823 const QColor fillColor { queryString.queryItemValue( QStringLiteral( "fill" ) ) };
2824 m->setFillColor( fillColor );
2825 }
2826
2827 if ( queryString.hasQueryItem( QStringLiteral( "fill-opacity" ) ) )
2828 {
2829 const double alpha { queryString.queryItemValue( QStringLiteral( "fill-opacity" ) ).toDouble( &ok ) };
2830 if ( ok )
2831 {
2832 params.insert( QStringLiteral( "fill-opacity" ), QgsProperty::fromValue( alpha ) );
2833 }
2834 }
2835
2836 if ( queryString.hasQueryItem( QStringLiteral( "outline" ) ) )
2837 {
2838 const QColor strokeColor { queryString.queryItemValue( QStringLiteral( "outline" ) ) };
2840 }
2841
2842 if ( queryString.hasQueryItem( QStringLiteral( "outline-opacity" ) ) )
2843 {
2844 const double alpha { queryString.queryItemValue( QStringLiteral( "outline-opacity" ) ).toDouble( &ok ) };
2845 if ( ok )
2846 {
2847 params.insert( QStringLiteral( "outline-opacity" ), QgsProperty::fromValue( alpha ) );
2848 }
2849 }
2850
2851 if ( queryString.hasQueryItem( QStringLiteral( "outline-width" ) ) )
2852 {
2853 const int width { queryString.queryItemValue( QStringLiteral( "outline-width" ) ).toInt( &ok )};
2854 if ( ok )
2855 {
2856 m->setStrokeWidth( width );
2857 }
2858 }
2859
2860 if ( ! params.isEmpty() )
2861 {
2862 m->setParameters( params );
2863 }
2864
2865 m->setOutputUnit( sldUnitSize );
2866 m->setAngle( angle );
2867 m->setOffset( offset );
2868 return m;
2869}
2870
2871bool QgsSvgMarkerSymbolLayer::writeDxf( QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift ) const
2872{
2873 //size
2874 double size = mSize;
2875
2876 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
2877
2878 bool ok = true;
2879 if ( hasDataDefinedSize )
2880 {
2883 }
2884
2885 if ( hasDataDefinedSize && ok )
2886 {
2887 switch ( mScaleMethod )
2888 {
2890 size = std::sqrt( size );
2891 break;
2893 break;
2894 }
2895 }
2896
2898 {
2899 size *= mmMapUnitScaleFactor;
2900 }
2901
2902//offset, angle
2903 QPointF offset = mOffset;
2904
2906 {
2908 const QVariant val = mDataDefinedProperties.value( QgsSymbolLayer::Property::Offset, context.renderContext().expressionContext(), QString() );
2909 const QPointF res = QgsSymbolLayerUtils::toPoint( val, &ok );
2910 if ( ok )
2911 offset = res;
2912 }
2913 const double offsetX = offset.x();
2914 const double offsetY = offset.y();
2915
2916 QPointF outputOffset( offsetX, offsetY );
2917
2918 double angle = mAngle + mLineAngle;
2920 {
2923 }
2924
2925 if ( angle )
2926 outputOffset = _rotatedOffset( outputOffset, angle );
2927
2929
2930 QString path = mPath;
2932 {
2935 context.renderContext().pathResolver() );
2936 }
2937
2938 double strokeWidth = mStrokeWidth;
2940 {
2943 }
2945
2946 QColor fillColor = mColor;
2948 {
2951 }
2952
2953 QColor strokeColor = mStrokeColor;
2955 {
2958 }
2959
2961
2962 const QByteArray &svgContent = QgsApplication::svgCache()->svgContent( path, size, fillColor, strokeColor, strokeWidth,
2964 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
2965
2966 QSvgRenderer r( svgContent );
2967 if ( !r.isValid() )
2968 return false;
2969
2970 QgsDxfPaintDevice pd( &e );
2971 pd.setDrawingSize( QSizeF( r.defaultSize() ) );
2972
2973 QSizeF outSize( r.defaultSize() );
2974 outSize.scale( size, size, Qt::KeepAspectRatio );
2975
2976 QPainter p;
2977 p.begin( &pd );
2978 if ( !qgsDoubleNear( angle, 0.0 ) )
2979 {
2980 p.translate( r.defaultSize().width() / 2.0, r.defaultSize().height() / 2.0 );
2981 p.rotate( angle );
2982 p.translate( -r.defaultSize().width() / 2.0, -r.defaultSize().height() / 2.0 );
2983 }
2984 pd.setShift( shift + QPointF( outputOffset.x(), -outputOffset.y() ) );
2985 pd.setOutputSize( QRectF( -outSize.width() / 2.0, -outSize.height() / 2.0, outSize.width(), outSize.height() ) );
2986 pd.setLayer( layerName );
2987 r.render( &p );
2988 p.end();
2989 return true;
2990}
2991
2993{
2994 bool hasDataDefinedSize = false;
2995 double scaledWidth = calculateSize( context, hasDataDefinedSize );
2996
2997 bool hasDataDefinedAspectRatio = false;
2998 const double aspectRatio = calculateAspectRatio( context, scaledWidth, hasDataDefinedAspectRatio );
2999 double scaledHeight = scaledWidth * ( !qgsDoubleNear( aspectRatio, 0.0 ) ? aspectRatio : mDefaultAspectRatio );
3000
3001 scaledWidth = context.renderContext().convertToPainterUnits( scaledWidth, mSizeUnit, mSizeMapUnitScale );
3002 scaledHeight = context.renderContext().convertToPainterUnits( scaledHeight, mSizeUnit, mSizeMapUnitScale );
3003
3004 //don't render symbols with size below one or above 10,000 pixels
3005 if ( static_cast< int >( scaledWidth ) < 1 || 10000.0 < scaledWidth )
3006 {
3007 return QRectF();
3008 }
3009
3010 QPointF outputOffset;
3011 double angle = 0.0;
3012 calculateOffsetAndRotation( context, scaledWidth, scaledHeight, outputOffset, angle );
3013
3014 double strokeWidth = mStrokeWidth;
3016 {
3019 }
3021
3022 QString path = mPath;
3024 {
3027 context.renderContext().pathResolver() );
3028 if ( path != mPath && qgsDoubleNear( aspectRatio, 0.0 ) && !mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Height ) )
3029 {
3030 // need to get colors to take advantage of cached SVGs
3031 QColor fillColor = mColor;
3033 {
3036 }
3037
3038 const QColor strokeColor = mStrokeColor;
3040 {
3043 }
3044
3046
3047 // adjust height of data defined path
3048 const QSizeF svgViewbox = QgsApplication::svgCache()->svgViewboxSize( path, scaledWidth, fillColor, strokeColor, strokeWidth,
3049 context.renderContext().scaleFactor(), aspectRatio,
3050 ( context.renderContext().flags() & Qgis::RenderContextFlag::RenderBlocking ), evaluatedParameters );
3051 scaledHeight = svgViewbox.isValid() ? scaledWidth * svgViewbox.height() / svgViewbox.width() : scaledWidth;
3052 }
3053 }
3054
3055 QTransform transform;
3056 // move to the desired position
3057 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3058
3059 if ( !qgsDoubleNear( angle, 0.0 ) )
3060 transform.rotate( angle );
3061
3062 //antialiasing
3063 strokeWidth += 1.0 / 2.0;
3064
3065 QRectF symbolBounds = transform.mapRect( QRectF( -scaledWidth / 2.0,
3066 -scaledHeight / 2.0,
3067 scaledWidth,
3068 scaledHeight ) );
3069
3070 //extend bounds by pen width / 2.0
3071 symbolBounds.adjust( -strokeWidth / 2.0, -strokeWidth / 2.0,
3072 strokeWidth / 2.0, strokeWidth / 2.0 );
3073
3074 return symbolBounds;
3075}
3076
3078
3080 : mPath( path )
3081{
3082 mSize = size;
3083 mAngle = angle;
3084 mOffset = QPointF( 0, 0 );
3087}
3088
3090
3092{
3093 QString path;
3097
3098 if ( props.contains( QStringLiteral( "imageFile" ) ) )
3099 path = props[QStringLiteral( "imageFile" )].toString();
3100 if ( props.contains( QStringLiteral( "size" ) ) )
3101 size = props[QStringLiteral( "size" )].toDouble();
3102 if ( props.contains( QStringLiteral( "angle" ) ) )
3103 angle = props[QStringLiteral( "angle" )].toDouble();
3104 if ( props.contains( QStringLiteral( "scale_method" ) ) )
3105 scaleMethod = QgsSymbolLayerUtils::decodeScaleMethod( props[QStringLiteral( "scale_method" )].toString() );
3106
3107 auto m = std::make_unique< QgsRasterMarkerSymbolLayer >( path, size, angle, scaleMethod );
3108 m->setCommonProperties( props );
3109 return m.release();
3110}
3111
3113{
3114 const QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
3115 if ( graphicElem.isNull() )
3116 return nullptr;
3117
3118 const QDomElement externalGraphicElem = graphicElem.firstChildElement( QStringLiteral( "ExternalGraphic" ) );
3119 if ( externalGraphicElem.isNull() )
3120 return nullptr;
3121
3122 const QDomElement onlineResourceElem = externalGraphicElem.firstChildElement( QStringLiteral( "OnlineResource" ) );
3123 const QDomElement inlineContentElem = externalGraphicElem.firstChildElement( QStringLiteral( "InlineContent" ) );
3124
3125 QString url;
3126 if ( !onlineResourceElem.isNull() )
3127 {
3128 url = onlineResourceElem.attribute( QStringLiteral( "href" ) );
3129 // no further processing to do, both base64 data urls and direct file/http urls are compatible with raster markers already
3130 }
3131 else if ( !inlineContentElem.isNull() && inlineContentElem.attribute( QStringLiteral( "encoding" ) ) == QLatin1String( "base64" ) )
3132 {
3133 url = QStringLiteral( "base64:" ) + inlineContentElem.text();
3134 }
3135 else
3136 {
3137 return nullptr;
3138 }
3139
3141 // TODO: parse other attributes from the SLD spec (Opacity, Size, Rotation, AnchorPoint, Displacement)
3142 return m;
3143}
3144
3146{
3147 if ( properties.contains( QStringLiteral( "alpha" ) ) )
3148 {
3149 setOpacity( properties[QStringLiteral( "alpha" )].toDouble() );
3150 }
3151
3152 if ( properties.contains( QStringLiteral( "size_unit" ) ) )
3153 setSizeUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "size_unit" )].toString() ) );
3154 if ( properties.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3155 setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3156 if ( properties.contains( QStringLiteral( "fixedAspectRatio" ) ) )
3157 setFixedAspectRatio( properties[QStringLiteral( "fixedAspectRatio" )].toDouble() );
3158
3159 if ( properties.contains( QStringLiteral( "offset" ) ) )
3160 setOffset( QgsSymbolLayerUtils::decodePoint( properties[QStringLiteral( "offset" )].toString() ) );
3161 if ( properties.contains( QStringLiteral( "offset_unit" ) ) )
3162 setOffsetUnit( QgsUnitTypes::decodeRenderUnit( properties[QStringLiteral( "offset_unit" )].toString() ) );
3163 if ( properties.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3164 setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( properties[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3165
3166 if ( properties.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3167 {
3168 setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( properties[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3169 }
3170 if ( properties.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3171 {
3172 setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( properties[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3173 }
3174
3177}
3178
3179void QgsRasterMarkerSymbolLayer::resolvePaths( QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving )
3180{
3181 const QVariantMap::iterator it = properties.find( QStringLiteral( "name" ) );
3182 if ( it != properties.end() && it.value().userType() == QMetaType::Type::QString )
3183 {
3184 if ( saving )
3185 it.value() = QgsSymbolLayerUtils::svgSymbolPathToName( it.value().toString(), pathResolver );
3186 else
3187 it.value() = QgsSymbolLayerUtils::svgSymbolNameToPath( it.value().toString(), pathResolver );
3188 }
3189}
3190
3192{
3193 mPath = path;
3195}
3196
3198{
3199 const bool aPreservedAspectRatio = preservedAspectRatio();
3200 if ( aPreservedAspectRatio && !par )
3201 {
3203 }
3204 else if ( !aPreservedAspectRatio && par )
3205 {
3206 mFixedAspectRatio = 0.0;
3207 }
3208 return preservedAspectRatio();
3209}
3210
3212{
3213 if ( mDefaultAspectRatio == 0.0 && !mPath.isEmpty() )
3214 {
3216 mDefaultAspectRatio = ( !size.isNull() && size.isValid() && size.width() > 0 ) ? static_cast< double >( size.height() ) / static_cast< double >( size.width() ) : 0.0;
3217 }
3218 return mDefaultAspectRatio;
3219}
3220
3222{
3223 return QStringLiteral( "RasterMarker" );
3224}
3225
3230
3232{
3233 QPainter *p = context.renderContext().painter();
3234 if ( !p )
3235 return;
3236
3237 QString path = mPath;
3239 {
3242 }
3243
3244 if ( path.isEmpty() )
3245 return;
3246
3247 double width = 0.0;
3248 double height = 0.0;
3249
3250 bool hasDataDefinedSize = false;
3251 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3252
3253 bool hasDataDefinedAspectRatio = false;
3254 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3255
3256 QPointF outputOffset;
3257 double angle = 0.0;
3258
3259 // RenderPercentage Unit Type takes original image size
3261 {
3263 if ( size.isEmpty() )
3264 return;
3265
3266 width = ( scaledSize * static_cast< double >( size.width() ) ) / 100.0;
3267 height = ( scaledSize * static_cast< double >( size.height() ) ) / 100.0;
3268
3269 // don't render symbols with size below one or above 10,000 pixels
3270 if ( static_cast< int >( width ) < 1 || 10000.0 < width || static_cast< int >( height ) < 1 || 10000.0 < height )
3271 return;
3272
3273 calculateOffsetAndRotation( context, width, height, outputOffset, angle );
3274 }
3275 else
3276 {
3277 width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3278 height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3279
3280 if ( preservedAspectRatio() && path != mPath )
3281 {
3283 if ( !size.isNull() && size.isValid() && size.width() > 0 )
3284 {
3285 height = width * ( static_cast< double >( size.height() ) / static_cast< double >( size.width() ) );
3286 }
3287 }
3288
3289 // don't render symbols with size below one or above 10,000 pixels
3290 if ( static_cast< int >( width ) < 1 || 10000.0 < width )
3291 return;
3292
3293 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3294 }
3295
3296 const QgsScopedQPainterState painterState( p );
3297 p->translate( point + outputOffset );
3298
3299 const bool rotated = !qgsDoubleNear( angle, 0 );
3300 if ( rotated )
3301 p->rotate( angle );
3302
3303 double opacity = mOpacity;
3305 {
3308 }
3309 opacity *= context.opacity();
3310
3311 QImage img = fetchImage( context.renderContext(), path, QSize( width, preservedAspectRatio() ? 0 : width * aspectRatio ), preservedAspectRatio(), opacity );
3312 if ( !img.isNull() )
3313 {
3314 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
3315 if ( useSelectedColor )
3316 {
3318 }
3319
3320 p->drawImage( -img.width() / 2.0, -img.height() / 2.0, img );
3321 }
3322}
3323
3324QImage QgsRasterMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
3325{
3326 bool cached = false;
3327 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking );
3328}
3329
3330double QgsRasterMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context, bool &hasDataDefinedSize ) const
3331{
3332 double scaledSize = mSize;
3334
3335 bool ok = true;
3336 if ( hasDataDefinedSize )
3337 {
3340 }
3341 else
3342 {
3343 hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Width );
3344 if ( hasDataDefinedSize )
3345 {
3347 scaledSize = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::Width, context.renderContext().expressionContext(), mSize, &ok );
3348 }
3349 }
3350
3351 if ( hasDataDefinedSize && ok )
3352 {
3353 switch ( mScaleMethod )
3354 {
3356 scaledSize = std::sqrt( scaledSize );
3357 break;
3359 break;
3360 }
3361 }
3362
3363 return scaledSize;
3364}
3365
3366double QgsRasterMarkerSymbolLayer::calculateAspectRatio( QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio ) const
3367{
3369 if ( !hasDataDefinedAspectRatio )
3370 return mFixedAspectRatio;
3371
3373 return 0.0;
3374
3375 double scaledAspectRatio = mDefaultAspectRatio;
3376 if ( mFixedAspectRatio > 0.0 )
3377 scaledAspectRatio = mFixedAspectRatio;
3378
3379 const double defaultHeight = mSize * scaledAspectRatio;
3380 scaledAspectRatio = defaultHeight / scaledSize;
3381
3382 bool ok = true;
3383 double scaledHeight = scaledSize * scaledAspectRatio;
3385 {
3386 context.setOriginalValueVariable( defaultHeight );
3387 scaledHeight = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::Height, context.renderContext().expressionContext(), defaultHeight, &ok );
3388 }
3389
3390 if ( hasDataDefinedAspectRatio && ok )
3391 {
3392 switch ( mScaleMethod )
3393 {
3395 scaledHeight = sqrt( scaledHeight );
3396 break;
3398 break;
3399 }
3400 }
3401
3402 scaledAspectRatio = scaledHeight / scaledSize;
3403
3404 return scaledAspectRatio;
3405}
3406
3407void QgsRasterMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context, double scaledWidth, double scaledHeight, QPointF &offset, double &angle ) const
3408{
3409 //offset
3410 double offsetX = 0;
3411 double offsetY = 0;
3412 markerOffset( context, scaledWidth, scaledHeight, offsetX, offsetY );
3413 offset = QPointF( offsetX, offsetY );
3414
3417 {
3420 }
3421
3422 const bool hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Angle );
3423 if ( hasDataDefinedRotation )
3424 {
3425 const QgsFeature *f = context.feature();
3426 if ( f )
3427 {
3428 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3429 {
3430 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3431 angle += m2p.mapRotation();
3432 }
3433 }
3434 }
3435
3436 if ( angle )
3438}
3439
3440
3442{
3443 QVariantMap map;
3444 map[QStringLiteral( "imageFile" )] = mPath;
3445 map[QStringLiteral( "size" )] = QString::number( mSize );
3446 map[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
3447 map[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
3448 map[QStringLiteral( "fixedAspectRatio" )] = QString::number( mFixedAspectRatio );
3449 map[QStringLiteral( "angle" )] = QString::number( mAngle );
3450 map[QStringLiteral( "alpha" )] = QString::number( mOpacity );
3451 map[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
3452 map[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
3453 map[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
3454 map[QStringLiteral( "scale_method" )] = QgsSymbolLayerUtils::encodeScaleMethod( mScaleMethod );
3455 map[QStringLiteral( "horizontal_anchor_point" )] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
3456 map[QStringLiteral( "vertical_anchor_point" )] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
3457 return map;
3458}
3459
3461{
3462 auto m = std::make_unique< QgsRasterMarkerSymbolLayer >();
3463 m->mPath = mPath;
3464 m->mDefaultAspectRatio = mDefaultAspectRatio;
3465 m->mSize = mSize;
3466 m->mAngle = mAngle;
3467 // other members are copied by:
3468 copyCommonProperties( m.get() );
3469 return m.release();
3470}
3471
3472
3487
3493
3495{
3496 return QColor();
3497}
3498
3503
3508
3510{
3511 bool hasDataDefinedSize = false;
3512 const double scaledSize = calculateSize( context, hasDataDefinedSize );
3513 const double width = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
3514 bool hasDataDefinedAspectRatio = false;
3515 const double aspectRatio = calculateAspectRatio( context, scaledSize, hasDataDefinedAspectRatio );
3516 const double height = width * ( preservedAspectRatio() ? defaultAspectRatio() : aspectRatio );
3517
3518 //don't render symbols with size below one or above 10,000 pixels
3519 if ( static_cast< int >( scaledSize ) < 1 || 10000.0 < scaledSize )
3520 {
3521 return QRectF();
3522 }
3523
3524 QPointF outputOffset;
3525 double angle = 0.0;
3526 calculateOffsetAndRotation( context, scaledSize, scaledSize * ( height / width ), outputOffset, angle );
3527
3528 QTransform transform;
3529
3530 // move to the desired position
3531 transform.translate( point.x() + outputOffset.x(), point.y() + outputOffset.y() );
3532
3533 if ( !qgsDoubleNear( angle, 0.0 ) )
3534 transform.rotate( angle );
3535
3536 QRectF symbolBounds = transform.mapRect( QRectF( -width / 2.0,
3537 -height / 2.0,
3538 width,
3539 height ) );
3540
3541 return symbolBounds;
3542}
3543
3544void QgsRasterMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
3545{
3546 QgsSldExportContext context;
3547 context.setExtraProperties( props );
3548 writeSldMarker( doc, element, context );
3549}
3550
3551bool QgsRasterMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
3552{
3553 Q_UNUSED( context )
3554
3555 // <Graphic>
3556 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
3557 element.appendChild( graphicElem );
3558
3559 // <ExternalGraphic>
3560 QDomElement extGraphElem = doc.createElement( QStringLiteral( "se:ExternalGraphic" ) );
3561 graphicElem.appendChild( extGraphElem );
3562
3563 QMimeDatabase mimeDB;
3564 QMimeType mimeType;
3565
3566 QString base64data;
3567 if ( mPath.startsWith( QLatin1String( "base64:" ) ) )
3568 {
3569 base64data = mPath.mid( 7 );
3570 }
3571 else
3572 {
3573 QString mime;
3574 QString data;
3576 {
3577 base64data = data;
3578 }
3579 }
3580
3581 if ( !base64data.isEmpty() )
3582 {
3583 // <InlineContent>
3584 QDomElement inlineContEleme = doc.createElement( QStringLiteral( "se:InlineContent" ) );
3585
3586 inlineContEleme.setAttribute( QStringLiteral( "encoding" ), QStringLiteral( "base64" ) );
3587 inlineContEleme.appendChild( doc.createTextNode( base64data ) );
3588 extGraphElem.appendChild( inlineContEleme );
3589
3590 // determine mime type
3591 const QByteArray ba = QByteArray::fromBase64( base64data.toUtf8() );
3592 mimeType = mimeDB.mimeTypeForData( ba );
3593 }
3594 else
3595 {
3596 // <ExternalGraphic>
3597 QDomElement onlineResElem = doc.createElement( QStringLiteral( "se:OnlineResource" ) );
3598 QString url = mPath;
3599
3600 onlineResElem.setAttribute( QStringLiteral( "xlink:href" ), url );
3601 onlineResElem.setAttribute( QStringLiteral( "xlink:type" ), QStringLiteral( "simple" ) );
3602 extGraphElem.appendChild( onlineResElem );
3603
3604 // determine mime type
3605 if ( mPath.startsWith( QLatin1String( "http://" ) ) || mPath.startsWith( QLatin1String( "https://" ) ) )
3606 {
3607 // Qt can't guess mime type for remote URLs, and it seems geoserver can handle wrong image mime types
3608 // but not generic ones, so let's hardcode to png.
3609 mimeType = mimeDB.mimeTypeForName( "image/png" );
3610 }
3611 else
3612 {
3613 mimeType = mimeDB.mimeTypeForUrl( url );
3614 }
3615 }
3616
3617 QDomElement formatElem = doc.createElement( QStringLiteral( "se:Format" ) );
3618 formatElem.appendChild( doc.createTextNode( mimeType.name() ) );
3619 extGraphElem.appendChild( formatElem );
3620
3621 // TODO: write other attributes from the SLD spec (Opacity, Size, Rotation, AnchorPoint, Displacement)
3622 return true;
3623}
3624
3626
3627QgsFontMarkerSymbolLayer::QgsFontMarkerSymbolLayer( const QString &fontFamily, QString chr, double pointSize, const QColor &color, double angle )
3628{
3629 mFontFamily = fontFamily;
3630 mString = chr;
3631 mColor = color;
3632 mAngle = angle;
3633 mSize = pointSize;
3634 mOrigSize = pointSize;
3636 mOffset = QPointF( 0, 0 );
3638 mStrokeColor = DEFAULT_FONTMARKER_BORDERCOLOR;
3639 mStrokeWidth = 0.0;
3640 mStrokeWidthUnit = Qgis::RenderUnit::Millimeters;
3641 mPenJoinStyle = DEFAULT_FONTMARKER_JOINSTYLE;
3642}
3643
3645
3647{
3649 QString string = DEFAULT_FONTMARKER_CHR;
3650 double pointSize = DEFAULT_FONTMARKER_SIZE;
3653
3654 if ( props.contains( QStringLiteral( "font" ) ) )
3655 fontFamily = props[QStringLiteral( "font" )].toString();
3656 if ( props.contains( QStringLiteral( "chr" ) ) && props[QStringLiteral( "chr" )].toString().length() > 0 )
3657 {
3658 string = props["chr"].toString();
3659 const thread_local QRegularExpression charRegExp( QStringLiteral( "%1([0-9]+)%1" ).arg( FONTMARKER_CHR_FIX ) );
3660 QRegularExpressionMatch match = charRegExp.match( string );
3661 while ( match.hasMatch() )
3662 {
3663 QChar replacement = QChar( match.captured( 1 ).toUShort() );
3664 string = string.mid( 0, match.capturedStart( 0 ) ) + replacement + string.mid( match.capturedEnd( 0 ) );
3665 match = charRegExp.match( string );
3666 }
3667 }
3668
3669 if ( props.contains( QStringLiteral( "size" ) ) )
3670 pointSize = props[QStringLiteral( "size" )].toDouble();
3671 if ( props.contains( QStringLiteral( "color" ) ) )
3672 color = QgsColorUtils::colorFromString( props[QStringLiteral( "color" )].toString() );
3673 if ( props.contains( QStringLiteral( "angle" ) ) )
3674 angle = props[QStringLiteral( "angle" )].toDouble();
3675
3677
3678 if ( props.contains( QStringLiteral( "font_style" ) ) )
3679 m->setFontStyle( props[QStringLiteral( "font_style" )].toString() );
3680 if ( props.contains( QStringLiteral( "outline_color" ) ) )
3681 m->setStrokeColor( QgsColorUtils::colorFromString( props[QStringLiteral( "outline_color" )].toString() ) );
3682 if ( props.contains( QStringLiteral( "outline_width" ) ) )
3683 m->setStrokeWidth( props[QStringLiteral( "outline_width" )].toDouble() );
3684 if ( props.contains( QStringLiteral( "offset" ) ) )
3685 m->setOffset( QgsSymbolLayerUtils::decodePoint( props[QStringLiteral( "offset" )].toString() ) );
3686 if ( props.contains( QStringLiteral( "offset_unit" ) ) )
3687 m->setOffsetUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "offset_unit" )].toString() ) );
3688 if ( props.contains( QStringLiteral( "offset_map_unit_scale" ) ) )
3689 m->setOffsetMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "offset_map_unit_scale" )].toString() ) );
3690 if ( props.contains( QStringLiteral( "size_unit" ) ) )
3691 m->setSizeUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "size_unit" )].toString() ) );
3692 if ( props.contains( QStringLiteral( "size_map_unit_scale" ) ) )
3693 m->setSizeMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "size_map_unit_scale" )].toString() ) );
3694 if ( props.contains( QStringLiteral( "outline_width_unit" ) ) )
3695 m->setStrokeWidthUnit( QgsUnitTypes::decodeRenderUnit( props[QStringLiteral( "outline_width_unit" )].toString() ) );
3696 if ( props.contains( QStringLiteral( "outline_width_map_unit_scale" ) ) )
3697 m->setStrokeWidthMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( props[QStringLiteral( "outline_width_map_unit_scale" )].toString() ) );
3698 if ( props.contains( QStringLiteral( "joinstyle" ) ) )
3699 m->setPenJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( props[QStringLiteral( "joinstyle" )].toString() ) );
3700 if ( props.contains( QStringLiteral( "horizontal_anchor_point" ) ) )
3701 m->setHorizontalAnchorPoint( static_cast< Qgis::HorizontalAnchorPoint >( props[ QStringLiteral( "horizontal_anchor_point" )].toInt() ) );
3702 if ( props.contains( QStringLiteral( "vertical_anchor_point" ) ) )
3703 m->setVerticalAnchorPoint( static_cast< Qgis::VerticalAnchorPoint >( props[ QStringLiteral( "vertical_anchor_point" )].toInt() ) );
3704
3706
3707 return m;
3708}
3709
3711{
3712 return QStringLiteral( "FontMarker" );
3713}
3714
3719
3721{
3722 QColor brushColor = mColor;
3723 QColor penColor = mStrokeColor;
3724
3725 brushColor.setAlphaF( mColor.alphaF() * context.opacity() );
3726 penColor.setAlphaF( mStrokeColor.alphaF() * context.opacity() );
3727
3728 mBrush = QBrush( brushColor );
3729 mPen = QPen( penColor );
3730 mPen.setJoinStyle( mPenJoinStyle );
3731 mPen.setWidthF( context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale ) );
3732
3733 mFont = QgsFontUtils::createFont( QgsApplication::fontManager()->processFontFamilyName( mFontFamily ) );
3734 if ( !mFontStyle.isEmpty() )
3735 {
3736 mFont.setStyleName( QgsFontUtils::translateNamedStyle( mFontStyle ) );
3737 }
3738
3739 double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3740 mNonZeroFontSize = !qgsDoubleNear( sizePixels, 0.0 );
3741
3742 if ( mNonZeroFontSize && sizePixels > MAX_FONT_CHARACTER_SIZE_IN_PIXELS )
3743 {
3744 // if font is too large (e.g using map units and map is very zoomed in), then we limit
3745 // the font size and instead scale up the painter.
3746 // this avoids issues with massive font sizes (eg https://github.com/qgis/QGIS/issues/42270)
3747 mFontSizeScale = sizePixels / MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3748 sizePixels = MAX_FONT_CHARACTER_SIZE_IN_PIXELS;
3749 }
3750 else
3751 mFontSizeScale = 1.0;
3752
3753 // if a non zero, but small pixel size results, round up to 2 pixels so that a "dot" is at least visible
3754 // (if we set a <=1 pixel size here Qt will reset the font to a default size, leading to much too large symbols)
3755 mFont.setPixelSize( std::max( 2, static_cast< int >( std::round( sizePixels ) ) ) );
3756 mFontMetrics = std::make_unique<QFontMetrics>( mFont );
3757 mChrWidth = mFontMetrics->horizontalAdvance( mString );
3758 switch ( mVerticalAnchorPoint )
3759 {
3761 {
3762 mChrOffset = QPointF( mChrWidth / 2.0, -sizePixels / 2.0 );
3763 break;
3764 }
3768 {
3769 mChrOffset = QPointF( mChrWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3770 break;
3771 }
3772 }
3773 mOrigSize = mSize; // save in case the size would be data defined
3774
3775 // use caching only when not using a data defined character
3776 mUseCachedPath = !mDataDefinedProperties.isActive( QgsSymbolLayer::Property::FontFamily ) &&
3779 if ( mUseCachedPath )
3780 {
3781 QPointF chrOffset = mChrOffset;
3782 double chrWidth;
3783 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3784 mCachedPath = QPainterPath();
3785 mCachedPath.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
3786 }
3787}
3788
3790{
3791 Q_UNUSED( context )
3792}
3793
3794QString QgsFontMarkerSymbolLayer::characterToRender( QgsSymbolRenderContext &context, QPointF &charOffset, double &charWidth )
3795{
3796 charOffset = mChrOffset;
3797 QString stringToRender = mString;
3799 {
3800 context.setOriginalValueVariable( mString );
3802 if ( stringToRender != mString )
3803 {
3804 charWidth = mFontMetrics->horizontalAdvance( stringToRender );
3805 switch ( mVerticalAnchorPoint )
3806 {
3808 {
3809 const double sizePixels = context.renderContext().convertToPainterUnits( mSize, mSizeUnit, mSizeMapUnitScale );
3810 charOffset = QPointF( charWidth / 2.0, -sizePixels / 2.0 );
3811 break;
3812 }
3816 {
3817 charOffset = QPointF( charWidth / 2.0, -mFontMetrics->ascent() / 2.0 );
3818 break;
3819 }
3820 }
3821 }
3822 }
3823 return stringToRender;
3824}
3825
3826void QgsFontMarkerSymbolLayer::calculateOffsetAndRotation( QgsSymbolRenderContext &context,
3827 double scaledSize,
3828 bool &hasDataDefinedRotation,
3829 QPointF &offset,
3830 double &angle ) const
3831{
3832 //offset
3833 double offsetX = 0;
3834 double offsetY = 0;
3835 markerOffset( context, scaledSize, scaledSize, offsetX, offsetY );
3836 offset = QPointF( offsetX, offsetY );
3837 hasDataDefinedRotation = false;
3838
3839 //angle
3840 bool ok = true;
3843 {
3846
3847 // If the expression evaluation was not successful, fallback to static value
3848 if ( !ok )
3850
3851 hasDataDefinedRotation = true;
3852 }
3853
3854 hasDataDefinedRotation = context.renderHints() & Qgis::SymbolRenderHint::DynamicRotation || hasDataDefinedRotation;
3855 if ( hasDataDefinedRotation )
3856 {
3857 // For non-point markers, "dataDefinedRotation" means following the
3858 // shape (shape-data defined). For them, "field-data defined" does
3859 // not work at all. TODO: if "field-data defined" ever gets implemented
3860 // we'll need a way to distinguish here between the two, possibly
3861 // using another flag in renderHints()
3862 const QgsFeature *f = context.feature();
3863 if ( f )
3864 {
3865 if ( f->hasGeometry() && f->geometry().type() == Qgis::GeometryType::Point )
3866 {
3867 const QgsMapToPixel &m2p = context.renderContext().mapToPixel();
3868 angle += m2p.mapRotation();
3869 }
3870 }
3871 }
3872
3873 if ( angle )
3875}
3876
3877double QgsFontMarkerSymbolLayer::calculateSize( QgsSymbolRenderContext &context )
3878{
3879 double scaledSize = mSize;
3880 const bool hasDataDefinedSize = mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Size );
3881
3882 bool ok = true;
3883 if ( hasDataDefinedSize )
3884 {
3886 scaledSize = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::Size, context.renderContext().expressionContext(), mSize, &ok );
3887 }
3888
3889 if ( hasDataDefinedSize && ok )
3890 {
3891 switch ( mScaleMethod )
3892 {
3894 scaledSize = std::sqrt( scaledSize );
3895 break;
3897 break;
3898 }
3899 }
3900 return scaledSize;
3901}
3902
3904{
3905 QPainter *p = context.renderContext().painter();
3906 if ( !p || !mNonZeroFontSize )
3907 return;
3908
3909 QTransform transform;
3910
3911 bool ok;
3912 QColor brushColor = mColor;
3914 {
3916 brushColor = mDataDefinedProperties.valueAsColor( QgsSymbolLayer::Property::FillColor, context.renderContext().expressionContext(), brushColor );
3917 }
3918 const bool useSelectedColor = shouldRenderUsingSelectionColor( context );
3919 brushColor = useSelectedColor ? context.renderContext().selectionColor() : brushColor;
3920 if ( !useSelectedColor || !SELECTION_IS_OPAQUE )
3921 {
3922 brushColor.setAlphaF( brushColor.alphaF() * context.opacity() );
3923 }
3924 mBrush.setColor( brushColor );
3925
3926 QColor penColor = mStrokeColor;
3928 {
3930 penColor = mDataDefinedProperties.valueAsColor( QgsSymbolLayer::Property::StrokeColor, context.renderContext().expressionContext(), penColor );
3931 }
3932 penColor.setAlphaF( penColor.alphaF() * context.opacity() );
3933
3934 double penWidth = context.renderContext().convertToPainterUnits( mStrokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3936 {
3937 context.setOriginalValueVariable( mStrokeWidth );
3938 const double strokeWidth = mDataDefinedProperties.valueAsDouble( QgsSymbolLayer::Property::StrokeWidth, context.renderContext().expressionContext(), mStrokeWidth, &ok );
3939 if ( ok )
3940 {
3941 penWidth = context.renderContext().convertToPainterUnits( strokeWidth, mStrokeWidthUnit, mStrokeWidthMapUnitScale );
3942 }
3943 }
3944
3946 {
3948 const QString style = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::JoinStyle, context.renderContext().expressionContext(), QString(), &ok );
3949 if ( ok )
3950 {
3951 mPen.setJoinStyle( QgsSymbolLayerUtils::decodePenJoinStyle( style ) );
3952 }
3953 }
3954
3955 const QgsScopedQPainterState painterState( p );
3956 p->setBrush( mBrush );
3957 if ( !qgsDoubleNear( penWidth, 0.0 ) )
3958 {
3959 mPen.setColor( penColor );
3960 mPen.setWidthF( penWidth );
3961 p->setPen( mPen );
3962 }
3963 else
3964 {
3965 p->setPen( Qt::NoPen );
3966 }
3967
3969 {
3970 context.setOriginalValueVariable( mFontFamily );
3971 const QString fontFamily = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::FontFamily, context.renderContext().expressionContext(), mFontFamily, &ok );
3972 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( ok ? fontFamily : mFontFamily );
3973 QgsFontUtils::setFontFamily( mFont, processedFamily );
3974 }
3976 {
3977 context.setOriginalValueVariable( mFontStyle );
3978 const QString fontStyle = mDataDefinedProperties.valueAsString( QgsSymbolLayer::Property::FontStyle, context.renderContext().expressionContext(), mFontStyle, &ok );
3980 }
3982 {
3983 mFontMetrics = std::make_unique<QFontMetrics>( mFont );
3984 }
3985
3986 QPointF chrOffset = mChrOffset;
3987 double chrWidth;
3988 const QString charToRender = characterToRender( context, chrOffset, chrWidth );
3989
3990 const double sizeToRender = calculateSize( context );
3991
3992 bool hasDataDefinedRotation = false;
3993 QPointF offset;
3994 double angle = 0;
3995 calculateOffsetAndRotation( context, sizeToRender, hasDataDefinedRotation, offset, angle );
3996
3997 p->translate( point.x() + offset.x(), point.y() + offset.y() );
3998
3999 if ( !qgsDoubleNear( angle, 0.0 ) )
4000 transform.rotate( angle );
4001
4002 if ( !qgsDoubleNear( sizeToRender, mOrigSize ) )
4003 {
4004 const double s = sizeToRender / mOrigSize;
4005 transform.scale( s, s );
4006 }
4007
4008 if ( !qgsDoubleNear( mFontSizeScale, 1.0 ) )
4009 transform.scale( mFontSizeScale, mFontSizeScale );
4010
4011 if ( mUseCachedPath )
4012 {
4013 p->drawPath( transform.map( mCachedPath ) );
4014 }
4015 else
4016 {
4017 QPainterPath path;
4018 path.addText( -chrOffset.x(), -chrOffset.y(), mFont, charToRender );
4019 p->drawPath( transform.map( path ) );
4020 }
4021}
4022
4024{
4025 QVariantMap props;
4026 props[QStringLiteral( "font" )] = mFontFamily;
4027 props[QStringLiteral( "font_style" )] = mFontStyle;
4028 QString chr = mString;
4029 for ( int i = 0; i < 32; i++ )
4030 {
4031 if ( i == 9 || i == 10 || i == 13 )
4032 {
4033 continue;
4034 }
4035 chr.replace( QChar( i ), QStringLiteral( "%1%2%1" ).arg( FONTMARKER_CHR_FIX, QString::number( i ) ) );
4036 }
4037 props[QStringLiteral( "chr" )] = chr;
4038 props[QStringLiteral( "size" )] = QString::number( mSize );
4039 props[QStringLiteral( "size_unit" )] = QgsUnitTypes::encodeUnit( mSizeUnit );
4040 props[QStringLiteral( "size_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mSizeMapUnitScale );
4041 props[QStringLiteral( "color" )] = QgsColorUtils::colorToString( mColor );
4042 props[QStringLiteral( "outline_color" )] = QgsColorUtils::colorToString( mStrokeColor );
4043 props[QStringLiteral( "outline_width" )] = QString::number( mStrokeWidth );
4044 props[QStringLiteral( "outline_width_unit" )] = QgsUnitTypes::encodeUnit( mStrokeWidthUnit );
4045 props[QStringLiteral( "outline_width_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mStrokeWidthMapUnitScale );
4046 props[QStringLiteral( "joinstyle" )] = QgsSymbolLayerUtils::encodePenJoinStyle( mPenJoinStyle );
4047 props[QStringLiteral( "angle" )] = QString::number( mAngle );
4048 props[QStringLiteral( "offset" )] = QgsSymbolLayerUtils::encodePoint( mOffset );
4049 props[QStringLiteral( "offset_unit" )] = QgsUnitTypes::encodeUnit( mOffsetUnit );
4050 props[QStringLiteral( "offset_map_unit_scale" )] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetMapUnitScale );
4051 props[QStringLiteral( "horizontal_anchor_point" )] = QString::number( static_cast< int >( mHorizontalAnchorPoint ) );
4052 props[QStringLiteral( "vertical_anchor_point" )] = QString::number( static_cast< int >( mVerticalAnchorPoint ) );
4053 return props;
4054}
4055
4057{
4058 QgsFontMarkerSymbolLayer *m = new QgsFontMarkerSymbolLayer( mFontFamily, mString, mSize, mColor, mAngle );
4059 m->setFontStyle( mFontStyle );
4060 m->setStrokeColor( mStrokeColor );
4061 m->setStrokeWidth( mStrokeWidth );
4062 m->setStrokeWidthUnit( mStrokeWidthUnit );
4063 m->setStrokeWidthMapUnitScale( mStrokeWidthMapUnitScale );
4064 m->setPenJoinStyle( mPenJoinStyle );
4065 m->setOffset( mOffset );
4068 m->setSizeUnit( mSizeUnit );
4073 copyPaintEffect( m );
4074 return m;
4075}
4076
4077void QgsFontMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
4078{
4079 QgsSldExportContext context;
4080 context.setExtraProperties( props );
4081 toSld( doc, element, context );
4082}
4083
4084bool QgsFontMarkerSymbolLayer::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
4085{
4086 return QgsMarkerSymbolLayer::toSld( doc, element, context );
4087}
4088
4089void QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
4090{
4091 QgsSldExportContext context;
4092 context.setExtraProperties( props );
4093 writeSldMarker( doc, element, context );
4094}
4095
4096bool QgsFontMarkerSymbolLayer::writeSldMarker( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
4097{
4098 // <Graphic>
4099 QDomElement graphicElem = doc.createElement( QStringLiteral( "se:Graphic" ) );
4100 element.appendChild( graphicElem );
4101
4102 const QVariantMap props = context.extraProperties();
4103 const QString fontPath = QStringLiteral( "ttf://%1" ).arg( mFontFamily );
4104 int markIndex = !mString.isEmpty() ? mString.at( 0 ).unicode() : 0;
4105 const double size = QgsSymbolLayerUtils::rescaleUom( mSize, mSizeUnit, props );
4106 QgsSymbolLayerUtils::externalMarkerToSld( doc, graphicElem, fontPath, QStringLiteral( "ttf" ), context, &markIndex, mColor, size );
4107
4108 // <Rotation>
4109 QString angleFunc;
4110 bool ok;
4111 const double angle = props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toDouble( &ok );
4112 if ( !ok )
4113 {
4114 angleFunc = QStringLiteral( "%1 + %2" ).arg( props.value( QStringLiteral( "angle" ), QStringLiteral( "0" ) ).toString() ).arg( mAngle );
4115 }
4116 else if ( !qgsDoubleNear( angle + mAngle, 0.0 ) )
4117 {
4118 angleFunc = QString::number( angle + mAngle );
4119 }
4120 QgsSymbolLayerUtils::createRotationElement( doc, graphicElem, angleFunc, context );
4121
4122 // <Displacement>
4123 const QPointF offset = QgsSymbolLayerUtils::rescaleUom( mOffset, mOffsetUnit, props );
4125 return true;
4126}
4127
4134
4136{
4138 mStrokeWidthUnit = unit;
4139}
4140
4142{
4143 QPointF chrOffset = mChrOffset;
4144 double chrWidth = mChrWidth;
4145 //calculate width of rendered character
4146 ( void )characterToRender( context, chrOffset, chrWidth );
4147
4148 if ( !mFontMetrics )
4149 mFontMetrics = std::make_unique<QFontMetrics>( mFont );
4150
4151 double scaledSize = calculateSize( context );
4152 if ( !qgsDoubleNear( scaledSize, mOrigSize ) )
4153 {
4154 chrWidth *= scaledSize / mOrigSize;
4155 }
4156 chrWidth *= mFontSizeScale;
4157
4158 bool hasDataDefinedRotation = false;
4159 QPointF offset;
4160 double angle = 0;
4161 calculateOffsetAndRotation( context, scaledSize, hasDataDefinedRotation, offset, angle );
4162 scaledSize = context.renderContext().convertToPainterUnits( scaledSize, mSizeUnit, mSizeMapUnitScale );
4163
4164 QTransform transform;
4165
4166 // move to the desired position
4167 transform.translate( point.x() + offset.x(), point.y() + offset.y() );
4168
4169 if ( !qgsDoubleNear( angle, 0.0 ) )
4170 transform.rotate( angle );
4171
4172 QRectF symbolBounds = transform.mapRect( QRectF( -chrWidth / 2.0,
4173 -scaledSize / 2.0,
4174 chrWidth,
4175 scaledSize ) );
4176 return symbolBounds;
4177}
4178
4180{
4181 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
4182
4183 QDomElement graphicElem = element.firstChildElement( QStringLiteral( "Graphic" ) );
4184 if ( graphicElem.isNull() )
4185 return nullptr;
4186
4187 QString name, format;
4188 QColor color;
4189 double size;
4190 int chr;
4191
4192 if ( !QgsSymbolLayerUtils::externalMarkerFromSld( graphicElem, name, format, chr, color, size ) )
4193 return nullptr;
4194
4195 if ( !name.startsWith( QLatin1String( "ttf://" ) ) || format != QLatin1String( "ttf" ) )
4196 return nullptr;
4197
4198 const QString fontFamily = name.mid( 6 );
4199
4200 double angle = 0.0;
4201 QString angleFunc;
4202 if ( QgsSymbolLayerUtils::rotationFromSldElement( graphicElem, angleFunc ) )
4203 {
4204 bool ok;
4205 const double d = angleFunc.toDouble( &ok );
4206 if ( ok )
4207 angle = d;
4208 }
4209
4210 QPointF offset;
4212
4213 double scaleFactor = 1.0;
4214 const QString uom = element.attribute( QStringLiteral( "uom" ) );
4215 Qgis::RenderUnit sldUnitSize = QgsSymbolLayerUtils::decodeSldUom( uom, &scaleFactor );
4216 offset.setX( offset.x() * scaleFactor );
4217 offset.setY( offset.y() * scaleFactor );
4218 size = size * scaleFactor;
4219
4221 m->setOutputUnit( sldUnitSize );
4222 m->setAngle( angle );
4223 m->setOffset( offset );
4224 return m;
4225}
4226
4228{
4229 const QString fontFamily = properties.value( QStringLiteral( "font" ), DEFAULT_FONTMARKER_FONT ).toString();
4230 const QString processedFamily = QgsApplication::fontManager()->processFontFamilyName( fontFamily );
4231 QString matched;
4232 if ( !QgsFontUtils::fontFamilyMatchOnSystem( processedFamily )
4233 && !QgsApplication::fontManager()->tryToDownloadFontFamily( processedFamily, matched ) )
4234 {
4235 context.pushMessage( QObject::tr( "Font “%1” not available on system" ).arg( processedFamily ) );
4236 }
4237}
4238
4240{
4241 QMap<QString, QgsProperty>::iterator it = mParameters.begin();
4242 for ( ; it != mParameters.end(); ++it )
4243 it.value().prepare( context.renderContext().expressionContext() );
4244
4246}
4247
4248
4250{
4251 QSet<QString> attrs = QgsMarkerSymbolLayer::usedAttributes( context );
4252
4253 QMap<QString, QgsProperty>::const_iterator it = mParameters.constBegin();
4254 for ( ; it != mParameters.constEnd(); ++it )
4255 {
4256 attrs.unite( it.value().referencedFields( context.expressionContext(), true ) );
4257 }
4258
4259 return attrs;
4260}
4261
4262//
4263// QgsAnimatedMarkerSymbolLayer
4264//
4265
4271
4273
4274QgsSymbolLayer *QgsAnimatedMarkerSymbolLayer::create( const QVariantMap &properties ) // cppcheck-suppress duplInheritedMember
4275{
4276 QString path;
4279
4280 if ( properties.contains( QStringLiteral( "imageFile" ) ) )
4281 path = properties[QStringLiteral( "imageFile" )].toString();
4282 if ( properties.contains( QStringLiteral( "size" ) ) )
4283 size = properties[QStringLiteral( "size" )].toDouble();
4284 if ( properties.contains( QStringLiteral( "angle" ) ) )
4285 angle = properties[QStringLiteral( "angle" )].toDouble();
4286
4287 auto m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( path, size, angle );
4288 m->setFrameRate( properties.value( QStringLiteral( "frameRate" ), QStringLiteral( "10" ) ).toDouble() );
4289
4290 m->setCommonProperties( properties );
4291 return m.release();
4292}
4293
4295{
4296 return QStringLiteral( "AnimatedMarker" );
4297}
4298
4300{
4301 QVariantMap res = QgsRasterMarkerSymbolLayer::properties();
4302 res.insert( QStringLiteral( "frameRate" ), mFrameRateFps );
4303 return res;
4304}
4305
4307{
4308 auto m = std::make_unique< QgsAnimatedMarkerSymbolLayer >( mPath, mSize, mAngle );
4309 m->setFrameRate( mFrameRateFps );
4310 copyCommonProperties( m.get() );
4311 return m.release();
4312}
4313
4315{
4317
4318 mPreparedPaths.clear();
4319 if ( !mDataDefinedProperties.isActive( QgsSymbolLayer::Property::Name ) && !mPath.isEmpty() )
4320 {
4322 mStaticPath = true;
4323 }
4324 else
4325 {
4326 mStaticPath = false;
4327 }
4328}
4329
4330QImage QgsAnimatedMarkerSymbolLayer::fetchImage( QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity ) const
4331{
4332 if ( !mStaticPath && !mPreparedPaths.contains( path ) )
4333 {
4335 mPreparedPaths.insert( path );
4336 }
4337
4338 const long long mapFrameNumber = context.currentFrame();
4340 const double markerAnimationDuration = totalFrameCount / mFrameRateFps;
4341
4342 double animationTimeSeconds = 0;
4343 if ( mapFrameNumber >= 0 && context.frameRate() > 0 )
4344 {
4345 // render is part of an animation, so we base the calculated frame on that
4346 animationTimeSeconds = mapFrameNumber / context.frameRate();
4347 }
4348 else
4349 {
4350 // render is outside of animation, so base the calculated frame on the current epoch
4351 animationTimeSeconds = QDateTime::currentMSecsSinceEpoch() / 1000.0;
4352 }
4353
4354 const double markerAnimationProgressSeconds = std::fmod( animationTimeSeconds, markerAnimationDuration );
4355 const int movieFrame = static_cast< int >( std::floor( markerAnimationProgressSeconds * mFrameRateFps ) );
4356
4357 bool cached = false;
4358 return QgsApplication::imageCache()->pathAsImage( path, size, preserveAspectRatio, opacity, cached, context.flags() & Qgis::RenderContextFlag::RenderBlocking, 96, movieFrame );
4359}
@ DynamicRotation
Rotation of symbol may be changed during rendering and symbol should not be cached.
Definition qgis.h:770
@ IsSymbolLayerSubSymbol
Symbol is being rendered as a sub-symbol of a QgsSymbolLayer.
Definition qgis.h:771
@ CanCalculateMaskGeometryPerFeature
If present, indicates that mask geometry can safely be calculated per feature for the symbol layer....
Definition qgis.h:882
ScaleMethod
Scale methods.
Definition qgis.h:624
@ ScaleDiameter
Calculate scale by the diameter.
Definition qgis.h:626
@ ScaleArea
Calculate scale by the area.
Definition qgis.h:625
QFlags< SymbolLayerFlag > SymbolLayerFlags
Symbol layer flags.
Definition qgis.h:887
MarkerShape
Marker shapes.
Definition qgis.h:3080
@ Pentagon
Pentagon.
Definition qgis.h:3083
@ Arrow
Arrow.
Definition qgis.h:3088
@ Hexagon
Hexagon.
Definition qgis.h:3084
@ EquilateralTriangle
Equilateral triangle.
Definition qgis.h:3086
@ SemiCircle
Semi circle (top half).
Definition qgis.h:3096
@ QuarterCircle
Quarter circle (top left quarter).
Definition qgis.h:3098
@ Circle
Circle.
Definition qgis.h:3089
@ LeftHalfTriangle
Left half of triangle.
Definition qgis.h:3103
@ ArrowHead
Right facing arrow head (unfilled, lines only).
Definition qgis.h:3094
@ ParallelogramRight
Parallelogram that slants right.
Definition qgis.h:3110
@ AsteriskFill
A filled asterisk shape.
Definition qgis.h:3106
@ Octagon
Octagon.
Definition qgis.h:3104
@ HalfArc
A line-only half arc.
Definition qgis.h:3107
@ Line
Vertical line.
Definition qgis.h:3093
@ QuarterSquare
Quarter square (top left quarter).
Definition qgis.h:3099
@ Triangle
Triangle.
Definition qgis.h:3085
@ Cross2
Rotated cross (lines only), 'x' shape.
Definition qgis.h:3092
@ Trapezoid
Trapezoid.
Definition qgis.h:3112
@ ArrowHeadFilled
Right facing filled arrow head.
Definition qgis.h:3095
@ Diamond
Diamond.
Definition qgis.h:3082
@ Shield
A shape consisting of a triangle attached to a rectangle.
Definition qgis.h:3113
@ HalfSquare
Half square (left half).
Definition qgis.h:3100
@ CrossFill
Solid filled cross.
Definition qgis.h:3091
@ Decagon
Decagon.
Definition qgis.h:3116
@ RoundedSquare
A square with rounded corners.
Definition qgis.h:3117
@ RightHalfTriangle
Right half of triangle.
Definition qgis.h:3102
@ Square
Square.
Definition qgis.h:3081
@ ThirdCircle
One third circle (top left third).
Definition qgis.h:3097
@ ThirdArc
A line-only one third arc.
Definition qgis.h:3108
@ SquareWithCorners
A square with diagonal corners.
Definition qgis.h:3105
@ QuarterArc
A line-only one quarter arc.
Definition qgis.h:3109
@ DiamondStar
A 4-sided star.
Definition qgis.h:3114
@ Cross
Cross (lines only).
Definition qgis.h:3090
@ ParallelogramLeft
Parallelogram that slants left.
Definition qgis.h:3111
@ Heart
Heart.
Definition qgis.h:3115
@ DiagonalHalfSquare
Diagonal half square (bottom left half).
Definition qgis.h:3101
VerticalAnchorPoint
Marker symbol vertical anchor points.
Definition qgis.h:814
@ Bottom
Align to bottom of symbol.
Definition qgis.h:817
@ Center
Align to vertical center of symbol.
Definition qgis.h:816
@ Baseline
Align to baseline of symbol, e.g. font baseline for font marker symbol layers. Treated as Bottom if n...
Definition qgis.h:818
@ Top
Align to top of symbol.
Definition qgis.h:815
@ Point
Points.
Definition qgis.h:359
RenderUnit
Rendering size units.
Definition qgis.h:5183
@ Percentage
Percentage of another measurement (e.g., canvas size, feature size).
Definition qgis.h:5187
@ Millimeters
Millimeters.
Definition qgis.h:5184
@ Unknown
Mixed or unknown units.
Definition qgis.h:5190
@ MapUnits
Map units.
Definition qgis.h:5185
@ MetersInMapUnits
Meters value as Map units.
Definition qgis.h:5191
@ RenderingSubSymbol
Set whenever a sub-symbol of a parent symbol is currently being rendered. Can be used during symbol a...
Definition qgis.h:2765
@ RenderSymbolPreview
The render is for a symbol preview only and map based properties may not be available,...
Definition qgis.h:2760
@ RenderLayerTree
The render is for a layer tree display where map based properties are not available and where avoidan...
Definition qgis.h:2771
@ RenderBlocking
Render and load remote sources in the same thread to ensure rendering remote sources (svg and images)...
Definition qgis.h:2759
@ Fill
Fill symbol.
Definition qgis.h:613
HorizontalAnchorPoint
Marker symbol horizontal anchor points.
Definition qgis.h:800
static bool parseBase64DataUrl(const QString &path, QString *mimeType=nullptr, QString *data=nullptr)
Parses a path to determine if it represents a base 64 encoded HTML data URL, and if so,...
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.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsAnimatedMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
~QgsAnimatedMarkerSymbolLayer() override
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
QImage fetchImage(QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity) const override
Fetches the image to render.
QgsAnimatedMarkerSymbolLayer(const QString &path=QString(), double size=DEFAULT_RASTERMARKER_SIZE, double angle=DEFAULT_RASTERMARKER_ANGLE)
Constructor for animated marker symbol layer using the specified source image path.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates an animated marker symbol layer from a string map of properties.
QString layerType() const override
Returns a string that represents this layer type.
static QgsImageCache * imageCache()
Returns the application's image cache, used for caching resampled versions of raster images.
static QgsSvgCache * svgCache()
Returns the application's SVG cache, used for caching SVG images and handling parameter replacement w...
static QgsFontManager * fontManager()
Returns the application font manager, which manages available fonts and font installation for the QGI...
static QColor colorFromString(const QString &string)
Decodes a string into a color value.
static QString colorToString(const QColor &color)
Encodes a color into a string value.
Exports QGIS layers to the DXF format.
void writeFilledCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius)
Write filled circle (as hatch).
void writeCircle(const QString &layer, const QColor &color, const QgsPoint &pt, double radius, const QString &lineStyleName, double width)
Write circle (as polyline).
static double mapUnitScaleFactor(double scale, Qgis::RenderUnit symbolUnits, Qgis::DistanceUnit mapUnits, double mapUnitsPerPixel=1.0)
Returns scale factor for conversion to map units.
void writeLine(const QgsPoint &pt1, const QgsPoint &pt2, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Write line (as a polyline).
void writePolygon(const QgsRingSequence &polygon, const QString &layer, const QString &hatchPattern, const QColor &color)
Draw dxf filled polygon (HATCH).
Qgis::DistanceUnit mapUnits() const
Retrieve map units.
double symbologyScale() const
Returns the reference scale for output.
void clipValueToMapUnitScale(double &value, const QgsMapUnitScale &scale, double pixelToMMFactor) const
Clips value to scale minimum/maximum.
void writePolyline(const QgsPointSequence &line, const QString &layer, const QString &lineStyleName, const QColor &color, double width=-1)
Draw dxf primitives (LWPOLYLINE).
A paint device for drawing into dxf files.
void setShift(QPointF shift)
void setLayer(const QString &layer)
void setOutputSize(const QRectF &r)
void setDrawingSize(QSizeF size)
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
QgsGeometry geometry
Definition qgsfeature.h:69
bool hasGeometry() const
Returns true if the feature has an associated geometry.
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
static std::unique_ptr< QgsFillSymbol > createSimple(const QVariantMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties.
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.
Q_DECL_DEPRECATED void toSld(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Saves the symbol layer as SLD.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
Q_DECL_DEPRECATED void writeSldMarker(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Writes the symbol layer definition as a SLD XML element.
void setStrokeWidth(double width)
Set's the marker's stroke width.
static void resolveFonts(const QVariantMap &properties, const QgsReadWriteContext &context)
Resolves fonts from a properties map, raising warnings in the specified context if the required fonts...
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
void setPenJoinStyle(Qt::PenJoinStyle style)
Sets the stroke join style.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsFontMarkerSymbolLayer from an SLD XML element.
QgsFontMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
QString layerType() const override
Returns a string that represents this layer type.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsFontMarkerSymbolLayer from a property map (see properties()).
Qgis::SymbolLayerFlags flags() const override
Returns flags which control the symbol layer's behavior.
static QString translateNamedStyle(const QString &namedStyle)
Returns the localized named style of a font, if such a translation is available.
static QFont createFont(const QString &family, int pointSize=-1, int weight=-1, bool italic=false)
Creates a font with the specified family.
static bool fontFamilyMatchOnSystem(const QString &family, QString *chosen=nullptr, bool *match=nullptr)
Check whether font family is on system.
static bool updateFontViaStyle(QFont &f, const QString &fontstyle, bool fallback=false)
Updates font with named style and retain all font properties.
static void setFontFamily(QFont &font, const QString &family)
Sets the family for a font object.
Qgis::GeometryType type
QSize originalSize(const QString &path, bool blocking=false) const
Returns the original size (in pixels) of the image at the specified path.
int totalFrameCount(const QString &path, bool blocking=false)
Returns the total frame count of the image at the specified path.
QImage pathAsImage(const QString &path, const QSize size, const bool keepAspectRatio, const double opacity, bool &fitsInCache, bool blocking=false, double targetDpi=96, int frameNumber=-1, bool *isMissing=nullptr)
Returns the specified path rendered as an image.
void prepareAnimation(const QString &path)
Prepares for optimized retrieval of frames for the animation at the given path.
static void overlayColor(QImage &image, const QColor &color)
Overlays a color onto an image.
Perform transforms between map coordinates and device coordinates.
double mapUnitsPerPixel() const
Returns the current map units per pixel.
double mapRotation() const
Returns the current map rotation in degrees (clockwise).
Struct for storing maximum and minimum scales for measurements in map units.
double mSize
Marker size.
void setVerticalAnchorPoint(Qgis::VerticalAnchorPoint v)
Sets the vertical anchor point for positioning the symbol.
QgsMarkerSymbolLayer(const QgsMarkerSymbolLayer &other)
Qgis::RenderUnit mOffsetUnit
Offset units.
QPointF offset() const
Returns the marker's offset, which is the horizontal and vertical displacement which the rendered mar...
double mLineAngle
Line rotation angle (see setLineAngle() for details).
void setOffsetUnit(Qgis::RenderUnit unit)
Sets the units for the symbol's offset.
void setAngle(double angle)
Sets the rotation angle for the marker.
Qgis::ScaleMethod scaleMethod() const
Returns the method to use for scaling the marker's size.
bool toSld(QDomDocument &doc, QDomElement &element, QgsSldExportContext &context) const override
Saves the symbol layer as SLD.
Qgis::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
QPointF mOffset
Marker offset.
QgsMapUnitScale mapUnitScale() const override
void setOffset(QPointF offset)
Sets the marker's offset, which is the horizontal and vertical displacement which the rendered marker...
void setSizeMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol's size.
double size() const
Returns the symbol size.
QgsMapUnitScale mOffsetMapUnitScale
Offset map unit scale.
Qgis::VerticalAnchorPoint mVerticalAnchorPoint
Vertical anchor point.
static QPointF _rotatedOffset(QPointF offset, double angle)
Adjusts a marker offset to account for rotation.
void setHorizontalAnchorPoint(Qgis::HorizontalAnchorPoint h)
Sets the horizontal anchor point for positioning the symbol.
Qgis::ScaleMethod mScaleMethod
Marker size scaling method.
QgsMapUnitScale mSizeMapUnitScale
Marker size map unit scale.
Qgis::RenderUnit mSizeUnit
Marker size unit.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void setSizeUnit(Qgis::RenderUnit unit)
Sets the units for the symbol's size.
void markerOffset(QgsSymbolRenderContext &context, double &offsetX, double &offsetY) const
Calculates the required marker offset, including both the symbol offset and any displacement required...
Qgis::HorizontalAnchorPoint mHorizontalAnchorPoint
Horizontal anchor point.
void setOffsetMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale for the symbol's offset.
double mAngle
Marker rotation angle, in degrees clockwise from north.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
double angle() const
Returns the rotation angle for the marker, in degrees clockwise from north.
void setMapUnitScale(const QgsMapUnitScale &scale) override
static void drawPicture(QPainter *painter, const QPointF &point, const QPicture &picture)
Draws a picture onto a painter, correctly applying workarounds to avoid issues with incorrect scaling...
Resolves relative paths into absolute paths and vice versa.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
bool isActive(int key) const final
Returns true if the collection contains an active property with the specified key.
static QVariantMap propertyMapToVariantMap(const QMap< QString, QgsProperty > &propertyMap)
Convert a map of QgsProperty to a map of QVariant This is useful to save a map of properties.
static QMap< QString, QgsProperty > variantMapToPropertyMap(const QVariantMap &variantMap)
Convert a map of QVariant to a map of QgsProperty This is useful to restore a map of properties.
static QgsProperty fromValue(const QVariant &value, bool isActive=true)
Returns a new StaticProperty created from the specified value.
double mFixedAspectRatio
The marker fixed aspect ratio.
QColor color() const override
Returns the "representative" color of the symbol layer.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsRasterMarkerSymbolLayer from an SLD XML element.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QgsMapUnitScale mapUnitScale() const override
void copyCommonProperties(QgsRasterMarkerSymbolLayer *other) const
Copies common properties to another layer.
void setOpacity(double opacity)
Set the marker opacity.
QString path() const
Returns the marker raster image path.
double calculateAspectRatio(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio) const
Calculates the marker aspect ratio between width and height.
QgsRasterMarkerSymbolLayer(const QString &path=QString(), double size=DEFAULT_SVGMARKER_SIZE, double angle=DEFAULT_SVGMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructs raster marker symbol layer with picture from given absolute path to a raster image file.
void setPath(const QString &path)
Set the marker raster image path.
Q_DECL_DEPRECATED void writeSldMarker(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Writes the symbol layer definition as a SLD XML element.
virtual QImage fetchImage(QgsRenderContext &context, const QString &path, QSize size, bool preserveAspectRatio, double opacity) const
Fetches the image to render.
double defaultAspectRatio() const
Returns the default marker aspect ratio between width and height, 0 if not yet calculated.
Qgis::SymbolLayerFlags flags() const override
Returns flags which control the symbol layer's behavior.
void setFixedAspectRatio(double ratio)
Set the marker aspect ratio between width and height to be used in rendering, if the value set is low...
void setMapUnitScale(const QgsMapUnitScale &scale) override
void setCommonProperties(const QVariantMap &properties)
Sets common class properties from a properties map.
QgsRasterMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
bool preservedAspectRatio() const
Returns the preserved aspect ratio value, true if fixed aspect ratio has been lower or equal to 0.
static void resolvePaths(QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving)
Turns relative paths in properties map to absolute when reading and vice versa when writing.
~QgsRasterMarkerSymbolLayer() override
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a raster marker symbol layer from a string map of properties.
double mOpacity
The marker default opacity.
double updateDefaultAspectRatio()
Calculates the default marker aspect ratio between width and height.
double mDefaultAspectRatio
The marker default aspect ratio.
bool setPreservedAspectRatio(bool par)
Set preserved the marker aspect ratio between width and height.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QString layerType() const override
Returns a string that represents this layer type.
double opacity() const
Returns the marker opacity.
A container for the context for various read/write operations on objects.
void pushMessage(const QString &message, Qgis::MessageLevel level=Qgis::MessageLevel::Warning) const
Append a message to the context.
Contains information about the context of a rendering operation.
double scaleFactor() const
Returns the scaling factor for the render to convert painter units to physical sizes.
double convertToPainterUnits(double size, Qgis::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QPainter * painter()
Returns the destination QPainter for the render operation.
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
QgsExpressionContext & expressionContext()
Gets the expression context.
long long currentFrame() const
Returns the current frame number of the map (in frames per second), for maps which are part of an ani...
float devicePixelRatio() const
Returns the device pixel ratio.
void setFlag(Qgis::RenderContextFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected).
double frameRate() const
Returns the frame rate of the map, for maps which are part of an animation.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
QColor selectionColor() const
Returns the color to use when rendering selected features.
Qgis::RenderContextFlags flags() const
Returns combination of flags used for rendering.
const QgsPathResolver & pathResolver() const
Returns the path resolver for conversion between relative and absolute paths during rendering operati...
Scoped object for saving and restoring a QPainter object's state.
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).
QPen mSelPen
QPen to use as stroke of selected symbols.
void setColor(const QColor &color) override
Sets the "representative" color for the symbol layer.
Q_DECL_DEPRECATED void toSld(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Saves the symbol layer as SLD.
QImage mSelCache
Cached image of selected marker, if using cached version.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
QImage mCache
Cached image of marker, if using cached version.
QBrush mSelBrush
QBrush to use as fill of selected symbols.
void setFillColor(const QColor &color) override
Sets the fill color for the symbol layer.
Qt::PenJoinStyle penJoinStyle() const
Returns the marker's stroke join style (e.g., miter, bevel, etc).
void drawMarker(QPainter *p, QgsSymbolRenderContext &context)
Draws the marker shape in the specified painter.
QPen mPen
QPen corresponding to marker's stroke style.
Qgis::RenderUnit mStrokeWidthUnit
Stroke width units.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates a new QgsSimpleMarkerSymbolLayer.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
void setMapUnitScale(const QgsMapUnitScale &scale) override
Qgis::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
void setStrokeWidthUnit(Qgis::RenderUnit u)
Sets the unit for the width of the marker's stroke.
QColor color() const override
Returns the "representative" color of the symbol layer.
QgsMapUnitScale mapUnitScale() const override
Qt::PenStyle mStrokeStyle
Stroke style.
QgsSimpleMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
static QgsSymbolLayer * createFromSld(QDomElement &element)
Creates a new QgsSimpleMarkerSymbolLayer from an SLD XML element.
~QgsSimpleMarkerSymbolLayer() override
Qt::PenCapStyle mPenCapStyle
Stroke pen cap style.
QString layerType() const override
Returns a string that represents this layer type.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
Sets the map scale for the width of the marker's stroke.
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
void setStrokeStyle(Qt::PenStyle strokeStyle)
Sets the marker's stroke style (e.g., solid, dashed, etc).
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
QColor fillColor() const override
Returns the fill color for the symbol layer.
QColor strokeColor() const override
Returns the marker's stroke color.
QBrush mBrush
QBrush corresponding to marker's fill style.
void setStrokeWidth(double w)
Sets the width of the marker's stroke.
void setStrokeColor(const QColor &color) override
Sets the marker's stroke color.
Qt::PenStyle strokeStyle() const
Returns the marker's stroke style (e.g., solid, dashed, etc).
bool mUsingCache
true if using cached images of markers for drawing.
Qgis::SymbolLayerFlags flags() const override
Returns flags which control the symbol layer's behavior.
void setPenCapStyle(Qt::PenCapStyle style)
Sets the marker's stroke cap style (e.g., flat, round, etc).
bool writeDxf(QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift=QPointF(0.0, 0.0)) const override
write as DXF
Q_DECL_DEPRECATED void writeSldMarker(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Writes the symbol layer definition as a SLD XML element.
static const int MAXIMUM_CACHE_WIDTH
Maximum width/height of cache image.
QString ogrFeatureStyle(double mmScaleFactor, double mapUnitScaleFactor) const override
bool prepareCache(QgsSymbolRenderContext &context)
Prepares cache image.
QgsMapUnitScale mStrokeWidthMapUnitScale
Stroke width map unit scale.
QgsSimpleMarkerSymbolLayer(Qgis::MarkerShape shape=Qgis::MarkerShape::Circle, double size=DEFAULT_SIMPLEMARKER_SIZE, double angle=DEFAULT_SIMPLEMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD, const QColor &color=DEFAULT_SIMPLEMARKER_COLOR, const QColor &strokeColor=DEFAULT_SIMPLEMARKER_BORDERCOLOR, Qt::PenJoinStyle penJoinStyle=DEFAULT_SIMPLEMARKER_JOINSTYLE)
Constructor for QgsSimpleMarkerSymbolLayer.
double strokeWidth() const
Returns the width of the marker's stroke.
Qt::PenJoinStyle mPenJoinStyle
Stroke pen join style.
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
Holds SLD export options and other information related to SLD export of a QGIS layer style.
void setExtraProperties(const QVariantMap &properties)
Sets the open ended set of properties that can drive/inform the SLD encoding.
QVariantMap extraProperties() const
Returns the open ended set of properties that can drive/inform the SLD encoding.
QSizeF svgViewboxSize(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >())
Calculates the viewbox size of a (possibly cached) SVG file.
QPicture svgAsPicture(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool forceVectorOutput=false, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >())
Returns an SVG drawing as a QPicture.
void containsParams(const QString &path, bool &hasFillParam, QColor &defaultFillColor, bool &hasStrokeParam, QColor &defaultStrokeColor, bool &hasStrokeWidthParam, double &defaultStrokeWidth, bool blocking=false) const
Tests if an SVG file contains parameters for fill, stroke color, stroke width.
QImage svgAsImage(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, bool &fitsInCache, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >())
Returns an SVG drawing as a QImage.
QByteArray svgContent(const QString &path, double size, const QColor &fill, const QColor &stroke, double strokeWidth, double widthScaleFactor, double fixedAspectRatio=0, bool blocking=false, const QMap< QString, QString > &parameters=QMap< QString, QString >(), bool *isMissingImage=nullptr)
Gets the SVG content corresponding to the given path.
QgsSvgMarkerSymbolLayer * clone() const override
Shall be reimplemented by subclasses to create a deep copy of the instance.
QColor fillColor() const override
Returns the fill color for the symbol layer.
QgsMapUnitScale mapUnitScale() const override
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the layer.
static QgsSymbolLayer * create(const QVariantMap &properties=QVariantMap())
Creates the symbol.
double mDefaultAspectRatio
The marker default aspect ratio.
QString layerType() const override
Returns a string that represents this layer type.
void setStrokeWidthMapUnitScale(const QgsMapUnitScale &scale)
QString path() const
Returns the marker SVG path.
Qgis::SymbolLayerFlags flags() const override
Returns flags which control the symbol layer's behavior.
bool preservedAspectRatio() const
Returns the preserved aspect ratio value, true if fixed aspect ratio has been lower or equal to 0.
void stopRender(QgsSymbolRenderContext &context) override
Called after a set of rendering operations has finished on the supplied render context.
Q_DECL_DEPRECATED void toSld(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Saves the symbol layer as SLD.
void setOutputUnit(Qgis::RenderUnit unit) override
Sets the units to use for sizes and widths within the symbol layer.
void prepareExpressions(const QgsSymbolRenderContext &context) override
Prepares all data defined property expressions for evaluation.
QMap< QString, QgsProperty > mParameters
bool setPreservedAspectRatio(bool par)
Set preserved the marker aspect ratio between width and height.
QVariantMap properties() const override
Should be reimplemented by subclasses to return a string map that contains the configuration informat...
Qgis::RenderUnit outputUnit() const override
Returns the units to use for sizes and widths within the symbol layer.
double calculateAspectRatio(QgsSymbolRenderContext &context, double scaledSize, bool &hasDataDefinedAspectRatio) const
Calculates the marker aspect ratio between width and height.
bool usesMapUnits() const override
Returns true if the symbol layer has any components which use map unit based sizes.
static QgsSymbolLayer * createFromSld(QDomElement &element)
void setStrokeWidthUnit(Qgis::RenderUnit unit)
Sets the units for the stroke width.
void setStrokeColor(const QColor &c) override
Sets the stroke color for the symbol layer.
void setMapUnitScale(const QgsMapUnitScale &scale) override
double updateDefaultAspectRatio()
Calculates the default marker aspect ratio between width and height.
QColor strokeColor() const override
Returns the stroke color for the symbol layer.
void setFillColor(const QColor &color) override
Sets the fill color for the symbol layer.
QRectF bounds(QPointF point, QgsSymbolRenderContext &context) override
Returns the approximate bounding box of the marker symbol layer, taking into account any data defined...
QgsSvgMarkerSymbolLayer(const QString &path, double size=DEFAULT_SVGMARKER_SIZE, double angle=DEFAULT_SVGMARKER_ANGLE, Qgis::ScaleMethod scaleMethod=DEFAULT_SCALE_METHOD)
Constructs SVG marker symbol layer with picture from given absolute path to a SVG file.
Q_DECL_DEPRECATED void writeSldMarker(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const override
Writes the symbol layer definition as a SLD XML element.
void renderPoint(QPointF point, QgsSymbolRenderContext &context) override
Renders a marker at the specified point.
~QgsSvgMarkerSymbolLayer() override
void startRender(QgsSymbolRenderContext &context) override
Called before a set of rendering operations commences on the supplied render context.
static void resolvePaths(QVariantMap &properties, const QgsPathResolver &pathResolver, bool saving)
Turns relative paths in properties map to absolute when reading and vice versa when writing.
QMap< QString, QgsProperty > parameters() const
Returns the dynamic SVG parameters.
QgsMapUnitScale mStrokeWidthMapUnitScale
void setParameters(const QMap< QString, QgsProperty > &parameters)
Sets the dynamic SVG parameters.
double mFixedAspectRatio
The marker fixed aspect ratio.
bool writeDxf(QgsDxfExport &e, double mmMapUnitScaleFactor, const QString &layerName, QgsSymbolRenderContext &context, QPointF shift=QPointF(0.0, 0.0)) const override
write as DXF
void setFixedAspectRatio(double ratio)
Set the marker aspect ratio between width and height to be used in rendering, if the value set is low...
void setPath(const QString &path)
Set the marker SVG path.
static bool externalMarkerFromSld(QDomElement &element, QString &path, QString &format, int &markIndex, QColor &color, double &size)
static bool rotationFromSldElement(QDomElement &element, QString &rotationFunc)
static QString encodePenStyle(Qt::PenStyle style)
static Qt::PenJoinStyle decodePenJoinStyle(const QString &str)
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
static QgsStringMap evaluatePropertiesMap(const QMap< QString, QgsProperty > &propertiesMap, const QgsExpressionContext &context)
Evaluates a map of properties using the given context and returns a variant map with evaluated expres...
static bool displacementFromSldElement(QDomElement &element, QPointF &offset)
static QString svgSymbolPathToName(const QString &path, const QgsPathResolver &pathResolver)
Determines an SVG symbol's name from its path.
static QPointF toPoint(const QVariant &value, bool *ok=nullptr)
Converts a value to a point.
static void multiplyImageOpacity(QImage *image, qreal opacity)
Multiplies opacity of image pixel values with a (global) transparency value.
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
static double rescaleUom(double size, Qgis::RenderUnit unit, const QVariantMap &props)
Rescales the given size based on the uomScale found in the props, if any is found,...
static Qt::PenCapStyle decodePenCapStyle(const QString &str)
static bool externalGraphicFromSld(QDomElement &element, QString &path, QString &mime, QColor &color, double &size)
static Q_DECL_DEPRECATED void parametricSvgToSld(QDomDocument &doc, QDomElement &graphicElem, const QString &path, const QColor &fillColor, double size, const QColor &strokeColor, double strokeWidth)
Encodes a reference to a parametric SVG into SLD, as a succession of parametric SVG using URL paramet...
static Qgis::ScaleMethod decodeScaleMethod(const QString &str)
Decodes a symbol scale method from a string.
static QString encodePenCapStyle(Qt::PenCapStyle style)
static Q_DECL_DEPRECATED void externalMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &path, const QString &format, int *markIndex=nullptr, const QColor &color=QColor(), double size=-1)
Exports a marker to an SLD definition.
static bool wellKnownMarkerFromSld(QDomElement &element, QString &name, QColor &color, QColor &strokeColor, Qt::PenStyle &strokeStyle, double &strokeWidth, double &size)
Extracts properties from an SLD marker definition.
static void createDisplacementElement(QDomDocument &doc, QDomElement &element, QPointF offset)
static QString svgSymbolNameToPath(const QString &name, const QgsPathResolver &pathResolver)
Determines an SVG symbol's path from its name.
static QString encodeColor(const QColor &color)
static Qgis::RenderUnit decodeSldUom(const QString &str, double *scaleFactor=nullptr)
Decodes a SLD unit of measure string to a render unit.
static double estimateMaxSymbolBleed(QgsSymbol *symbol, const QgsRenderContext &context)
Returns the maximum estimated bleed for the symbol.
static Q_DECL_DEPRECATED void wellKnownMarkerToSld(QDomDocument &doc, QDomElement &element, const QString &name, const QColor &color, const QColor &strokeColor, Qt::PenStyle strokeStyle, double strokeWidth=-1, double size=-1)
Exports a marker to SLD.
static QString encodeScaleMethod(Qgis::ScaleMethod scaleMethod)
Encodes a symbol scale method to a string.
static Qt::PenStyle decodePenStyle(const QString &str)
static Q_DECL_DEPRECATED void createRotationElement(QDomDocument &doc, QDomElement &element, const QString &rotationFunc)
Creates SLD rotation element.
static QString encodePoint(QPointF point)
Encodes a QPointF to a string.
static QString encodePenJoinStyle(Qt::PenJoinStyle style)
static QPointF decodePoint(const QString &string)
Decodes a QSizeF from a string.
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.
@ CapStyle
Line cap style.
@ JoinStyle
Line join style.
void restoreOldDataDefinedProperties(const QVariantMap &stringMap)
Restores older data defined properties from string map.
virtual void startRender(QgsSymbolRenderContext &context)=0
Called before a set of rendering operations commences on the supplied render context.
virtual void prepareExpressions(const QgsSymbolRenderContext &context)
Prepares all data defined property expressions for evaluation.
virtual Q_DECL_DEPRECATED void toSld(QDomDocument &doc, QDomElement &element, const QVariantMap &props) const
Saves the symbol layer as SLD.
virtual QColor color() const
Returns the "representative" color of the symbol layer.
virtual void setOutputUnit(Qgis::RenderUnit unit)
Sets the units to use for sizes and widths within the symbol layer.
void copyPaintEffect(QgsSymbolLayer *destLayer) const
Copies paint effect of this layer to another symbol layer.
virtual Qgis::SymbolLayerFlags flags() const
Returns flags which control the symbol layer's behavior.
QgsPropertyCollection mDataDefinedProperties
virtual bool hasDataDefinedProperties() const
Returns true if the symbol layer (or any of its sub-symbols) contains data defined properties.
QgsSymbolLayer(const QgsSymbolLayer &other)
Encapsulates the context in which a symbol is being rendered.
const QgsFeature * feature() const
Returns the current feature being rendered.
QgsFields fields() const
Fields of the layer.
Qgis::SymbolRenderHints renderHints() const
Returns the rendering hint flags for the symbol.
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for data defined symbology.
bool forceVectorRendering() const
Returns true if symbol must be rendered using vector methods, and optimisations like pre-rendered ima...
qreal opacity() const
Returns the opacity for the symbol.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
Abstract base class for all rendered symbols.
Definition qgssymbol.h:231
Qgis::SymbolType type() const
Returns the symbol's type.
Definition qgssymbol.h:294
static Q_INVOKABLE Qgis::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6607
QMap< QString, QString > QgsStringMap
Definition qgis.h:7132
QVector< QgsPointSequence > QgsRingSequence
QVector< QgsPoint > QgsPointSequence
#define DEG2RAD(x)
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:61
#define QgsDebugError(str)
Definition qgslogger.h:57
#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