QGIS API Documentation 4.3.0-Master (c8cdfe19d7b)
Loading...
Searching...
No Matches
qgscallout.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgscallout.cpp
3 ----------------
4 begin : July 2019
5 copyright : (C) 2019 Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "qgscallout.h"
19
20#include <mutex>
21
22#include "qgscircularstring.h"
23#include "qgsdatadefinedutils.h"
24#include "qgsfillsymbol.h"
25#include "qgsfillsymbollayer.h"
26#include "qgsgeometryutils.h"
27#include "qgsgeos.h"
28#include "qgslinestring.h"
29#include "qgslinesymbol.h"
30#include "qgslinesymbollayer.h"
31#include "qgsmarkersymbol.h"
32#include "qgspainting.h"
33#include "qgsrendercontext.h"
34#include "qgsshapegenerator.h"
35#include "qgssymbol.h"
36#include "qgssymbollayerutils.h"
37#include "qgsunittypes.h"
38#include "qgsvariantutils.h"
39#include "qgsxmlutils.h"
40
41#include <QString>
42
43using namespace Qt::StringLiterals;
44
45QgsPropertiesDefinition QgsCallout::sPropertyDefinitions;
46
47void QgsCallout::initPropertyDefinitions()
48{
49 const QString origin = u"callouts"_s;
50
51 sPropertyDefinitions = QgsPropertiesDefinition {
52 { static_cast< int >( QgsCallout::Property::MinimumCalloutLength ),
53 QgsPropertyDefinition( "MinimumCalloutLength", QObject::tr( "Minimum callout length" ), QgsPropertyDefinition::DoublePositive, origin ) },
54 { static_cast< int >( QgsCallout::Property::OffsetFromAnchor ), QgsPropertyDefinition( "OffsetFromAnchor", QObject::tr( "Offset from feature" ), QgsPropertyDefinition::DoublePositive, origin ) },
55 { static_cast< int >( QgsCallout::Property::OffsetFromLabel ), QgsPropertyDefinition( "OffsetFromLabel", QObject::tr( "Offset from label" ), QgsPropertyDefinition::DoublePositive, origin ) },
56 { static_cast< int >( QgsCallout::Property::DrawCalloutToAllParts ),
57 QgsPropertyDefinition( "DrawCalloutToAllParts", QObject::tr( "Draw lines to all feature parts" ), QgsPropertyDefinition::Boolean, origin ) },
58 { static_cast< int >( QgsCallout::Property::AnchorPointPosition ),
59 QgsPropertyDefinition( "AnchorPointPosition", QgsPropertyDefinition::DataTypeString, QObject::tr( "Feature's anchor point position" ), QObject::tr( "string " ) + "[<b>pole_of_inaccessibility</b>|<b>point_on_exterior</b>|<b>point_on_surface</b>|<b>centroid</b>]", origin ) },
61 QgsPropertyDefinition(
62 "LabelAnchorPointPosition",
64 QObject::tr( "Label's anchor point position" ),
65 QObject::tr( "string " )
66 + "[<b>point_on_exterior</b>|<b>centroid</b>|<b>top_left</b>=Top left|<b>top</b>=Top middle|"
67 "<b>top_right</b>=Top right|<br>"
68 "<b>left</b>=Left|<b>right</b>=Right|<br>"
69 "<b>bottom_left</b>=Bottom left|<b>bottom</b>=Bottom middle|"
70 "<b>bottom_right</b>=Bottom right]",
71 origin
72 ) },
73 { static_cast< int >( QgsCallout::Property::OriginX ), QgsPropertyDefinition( "OriginX", QObject::tr( "Callout origin (X)" ), QgsPropertyDefinition::Double, origin ) },
74 { static_cast< int >( QgsCallout::Property::OriginY ), QgsPropertyDefinition( "OriginY", QObject::tr( "Callout origin (Y)" ), QgsPropertyDefinition::Double, origin ) },
75 { static_cast< int >( QgsCallout::Property::DestinationX ), QgsPropertyDefinition( "DestinationX", QObject::tr( "Callout destination (X)" ), QgsPropertyDefinition::Double, origin ) },
76 { static_cast< int >( QgsCallout::Property::DestinationY ), QgsPropertyDefinition( "DestinationY", QObject::tr( "Callout destination (Y)" ), QgsPropertyDefinition::Double, origin ) },
77 { static_cast< int >( QgsCallout::Property::Curvature ), QgsPropertyDefinition( "Curvature", QObject::tr( "Callout line curvature" ), QgsPropertyDefinition::Double, origin ) },
78 { static_cast< int >( QgsCallout::Property::Orientation ),
79 QgsPropertyDefinition( "Orientation", QgsPropertyDefinition::DataTypeString, QObject::tr( "Callout curve orientation" ), QObject::tr( "string " ) + "[<b>auto</b>|<b>clockwise</b>|<b>counter_clockwise</b>]", origin ) },
80 { static_cast< int >( QgsCallout::Property::Margins ),
81 QgsPropertyDefinition( "Margins", QgsPropertyDefinition::DataTypeString, QObject::tr( "Margins" ), QObject::tr( "string of four doubles '<b>top,right,bottom,left</b>' or array of doubles <b>[top, right, bottom, left]</b>" ) ) },
82 { static_cast< int >( QgsCallout::Property::WedgeWidth ), QgsPropertyDefinition( "WedgeWidth", QObject::tr( "Wedge width" ), QgsPropertyDefinition::DoublePositive, origin ) },
83 { static_cast< int >( QgsCallout::Property::CornerRadius ), QgsPropertyDefinition( "CornerRadius", QObject::tr( "Corner radius" ), QgsPropertyDefinition::DoublePositive, origin ) },
84 { static_cast< int >( QgsCallout::Property::BlendMode ), QgsPropertyDefinition( "BlendMode", QObject::tr( "Callout blend mode" ), QgsPropertyDefinition::BlendMode, origin ) },
85 };
86}
87
88
91
92QVariantMap QgsCallout::properties( const QgsReadWriteContext & ) const
93{
94 QVariantMap props;
95 props.insert( u"enabled"_s, mEnabled ? "1" : "0" );
96 props.insert( u"anchorPoint"_s, encodeAnchorPoint( mAnchorPoint ) );
97 props.insert( u"labelAnchorPoint"_s, encodeLabelAnchorPoint( mLabelAnchorPoint ) );
98 props.insert( u"blendMode"_s, static_cast< int >( QgsPainting::getBlendModeEnum( mBlendMode ) ) );
99 props.insert( u"ddProperties"_s, mDataDefinedProperties.toVariant( propertyDefinitions() ) );
100 return props;
101}
102
103void QgsCallout::readProperties( const QVariantMap &props, const QgsReadWriteContext & )
104{
105 mEnabled = props.value( u"enabled"_s, u"0"_s ).toInt();
106 mAnchorPoint = decodeAnchorPoint( props.value( u"anchorPoint"_s, QString() ).toString() );
107 mLabelAnchorPoint = decodeLabelAnchorPoint( props.value( u"labelAnchorPoint"_s, QString() ).toString() );
108 mBlendMode = QgsPainting::getCompositionMode( static_cast< Qgis::BlendMode >( props.value( u"blendMode"_s, QString::number( static_cast< int >( Qgis::BlendMode::Normal ) ) ).toUInt() ) );
109 mDataDefinedProperties.loadVariant( props.value( u"ddProperties"_s ), propertyDefinitions() );
110}
111
112bool QgsCallout::saveProperties( QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context ) const
113{
114 if ( element.isNull() )
115 {
116 return false;
117 }
118
119 const QDomElement calloutPropsElement = QgsXmlUtils::writeVariant( properties( context ), doc );
120
121 QDomElement calloutElement = doc.createElement( u"callout"_s );
122 calloutElement.setAttribute( u"type"_s, type() );
123 calloutElement.appendChild( calloutPropsElement );
124
125 element.appendChild( calloutElement );
126 return true;
127}
128
129void QgsCallout::restoreProperties( const QDomElement &element, const QgsReadWriteContext &context )
130{
131 const QVariantMap props = QgsXmlUtils::readVariant( element.firstChildElement() ).toMap();
132 readProperties( props, context );
133}
134
139
141{
142 return mBlendMode != QPainter::CompositionMode_SourceOver || dataDefinedProperties().isActive( QgsCallout::Property::BlendMode );
143}
144
145QSet<QString> QgsCallout::referencedFields( const QgsRenderContext &context ) const
146{
147 mDataDefinedProperties.prepare( context.expressionContext() );
148 return mDataDefinedProperties.referencedFields( context.expressionContext() );
149}
150
155
156void QgsCallout::render( QgsRenderContext &context, const QRectF &rect, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext )
157{
158 QPainter *painter = context.painter();
160 {
161 const QPainter::CompositionMode blendMode = mBlendMode;
163 {
164 context.expressionContext().setOriginalValueVariable( QString() );
166 }
167
168 painter->setCompositionMode( blendMode );
169 }
170
171#if 0 // for debugging
172 painter->save();
173 painter->setRenderHint( QPainter::Antialiasing, false );
174 painter->translate( rect.center() );
175 painter->rotate( -angle );
176
177 painter->setBrush( QColor( 255, 0, 0, 100 ) );
178 painter->setPen( QColor( 255, 0, 0, 150 ) );
179
180 painter->drawRect( rect.width() * -0.5, rect.height() * -0.5, rect.width(), rect.height() );
181 painter->restore();
182
183 painter->setBrush( QColor( 0, 255, 0, 100 ) );
184 painter->setPen( QColor( 0, 255, 0, 150 ) );
185
186 painter->drawRect( anchor.boundingBox( ).buffered( 30 ).toRectF() );
187#endif
188
189 draw( context, rect, angle, anchor, calloutContext );
190
191 painter->setCompositionMode( QPainter::CompositionMode_SourceOver ); // just to be sure
192}
193
195{
196 mEnabled = enabled;
197}
198
200{
201 static std::once_flag initialized;
202 std::call_once( initialized, initPropertyDefinitions );
203 return sPropertyDefinitions;
204}
205
207{
208 if ( ok )
209 *ok = true;
210
211 static constexpr auto ANCHOR_MAP = makeDataDefinedStringDecoder<QgsCallout::AnchorPoint>( {
212 { u"poleofinaccessibility", QgsCallout::AnchorPoint::PoleOfInaccessibility },
213 { u"pointonexterior", QgsCallout::AnchorPoint::PointOnExterior },
214 { u"pointonsurface", QgsCallout::AnchorPoint::PointOnSurface },
215 { u"centroid", QgsCallout::AnchorPoint::Centroid },
216 } );
217
218 auto evaluatedAnchor = ANCHOR_MAP.lookup( name );
219 if ( evaluatedAnchor )
220 {
221 return *evaluatedAnchor;
222 }
223 else
224 {
225 if ( ok )
226 *ok = false;
228 }
229}
230
232{
233 switch ( anchor )
234 {
236 return u"pole_of_inaccessibility"_s;
237 case PointOnExterior:
238 return u"point_on_exterior"_s;
239 case PointOnSurface:
240 return u"point_on_surface"_s;
241 case Centroid:
242 return u"centroid"_s;
243 }
244 return QString();
245}
246
248{
249 switch ( anchor )
250 {
252 return u"point_on_exterior"_s;
253 case LabelCentroid:
254 return u"centroid"_s;
255 case LabelTopLeft:
256 return u"tl"_s;
257 case LabelTopMiddle:
258 return u"t"_s;
259 case LabelTopRight:
260 return u"tr"_s;
261 case LabelMiddleLeft:
262 return u"l"_s;
263 case LabelMiddleRight:
264 return u"r"_s;
265 case LabelBottomLeft:
266 return u"bl"_s;
268 return u"b"_s;
269 case LabelBottomRight:
270 return u"br"_s;
271 }
272
273 return QString();
274}
275
277{
278 if ( ok )
279 *ok = true;
280
281 static constexpr auto ANCHOR_MAP = makeDataDefinedStringDecoder<QgsCallout::LabelAnchorPoint>( {
300 } );
301
302 auto evaluatedAnchor = ANCHOR_MAP.lookup( name );
303 if ( evaluatedAnchor )
304 {
305 return *evaluatedAnchor;
306 }
307 else
308 {
309 if ( ok )
310 *ok = false;
312 }
313}
314
315QgsGeometry QgsCallout::labelAnchorGeometry( const QRectF &rect, const double angle, LabelAnchorPoint anchor ) const
316{
317 QgsGeometry label;
318 switch ( anchor )
319 {
321 label = QgsGeometry::fromRect( rect );
322 break;
323
324 case LabelCentroid:
325 label = QgsGeometry::fromRect( rect ).centroid();
326 break;
327
328 case LabelTopLeft:
329 label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomLeft() ) );
330 break;
331
332 case LabelTopMiddle:
333 label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.bottom() ) );
334 break;
335
336 case LabelTopRight:
337 label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomRight() ) );
338 break;
339
340 case LabelMiddleLeft:
341 label = QgsGeometry::fromPointXY( QgsPointXY( rect.left(), ( rect.top() + rect.bottom() ) / 2.0 ) );
342 break;
343
344 case LabelMiddleRight:
345 label = QgsGeometry::fromPointXY( QgsPointXY( rect.right(), ( rect.top() + rect.bottom() ) / 2.0 ) );
346 break;
347
348 case LabelBottomLeft:
349 label = QgsGeometry::fromPointXY( QgsPointXY( rect.topLeft() ) );
350 break;
351
353 label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.top() ) );
354 break;
355
356 case LabelBottomRight:
357 label = QgsGeometry::fromPointXY( QgsPointXY( rect.topRight() ) );
358 break;
359 }
360
361 label.rotate( angle, rect.topLeft() );
362 return label;
363}
364
366 const QRectF &rect, const double angle, QgsCallout::LabelAnchorPoint anchor, QgsRenderContext &context, const QgsCallout::QgsCalloutContext &calloutContext, bool &pinned
367) const
368{
369 pinned = false;
371 {
372 bool ok = false;
374 if ( ok )
375 {
377 if ( ok )
378 {
379 pinned = true;
380 // data defined label point, use it directly
381 QgsGeometry labelPoint = QgsGeometry::fromPointXY( QgsPointXY( x, y ) );
382 try
383 {
384 labelPoint.transform( calloutContext.originalFeatureToMapTransform( context ) );
385 labelPoint.transform( context.mapToPixel().transform() );
386 }
387 catch ( QgsCsException & )
388 {
389 return QgsGeometry();
390 }
391 return labelPoint;
392 }
393 }
394 }
395
396 QgsGeometry label;
397 switch ( anchor )
398 {
400 label = QgsGeometry::fromRect( rect );
401 break;
402
403 case LabelCentroid:
404 label = QgsGeometry::fromRect( rect ).centroid();
405 break;
406
407 case LabelTopLeft:
408 label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomLeft() ) );
409 break;
410
411 case LabelTopMiddle:
412 label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.bottom() ) );
413 break;
414
415 case LabelTopRight:
416 label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomRight() ) );
417 break;
418
419 case LabelMiddleLeft:
420 label = QgsGeometry::fromPointXY( QgsPointXY( rect.left(), ( rect.top() + rect.bottom() ) / 2.0 ) );
421 break;
422
423 case LabelMiddleRight:
424 label = QgsGeometry::fromPointXY( QgsPointXY( rect.right(), ( rect.top() + rect.bottom() ) / 2.0 ) );
425 break;
426
427 case LabelBottomLeft:
428 label = QgsGeometry::fromPointXY( QgsPointXY( rect.topLeft() ) );
429 break;
430
432 label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.top() ) );
433 break;
434
435 case LabelBottomRight:
436 label = QgsGeometry::fromPointXY( QgsPointXY( rect.topRight() ) );
437 break;
438 }
439
440 label.rotate( angle, rect.topLeft() );
441 return label;
442}
443
444QgsGeometry QgsCallout::calloutLineToPart( const QgsGeometry &labelGeometry, const QgsAbstractGeometry *partGeometry, QgsRenderContext &context, const QgsCalloutContext &calloutContext, bool &pinned ) const
445{
446 pinned = false;
447 AnchorPoint anchor = anchorPoint();
448 const QgsAbstractGeometry *evaluatedPartAnchor = partGeometry;
449 std::unique_ptr< QgsAbstractGeometry > tempPartAnchor;
450
452 {
453 bool ok = false;
455 if ( ok )
456 {
458 if ( ok )
459 {
460 pinned = true;
461 tempPartAnchor = std::make_unique< QgsPoint >( Qgis::WkbType::Point, x, y );
462 evaluatedPartAnchor = tempPartAnchor.get();
463 try
464 {
465 tempPartAnchor->transform( calloutContext.originalFeatureToMapTransform( context ) );
466 tempPartAnchor->transform( context.mapToPixel().transform() );
467 }
468 catch ( QgsCsException & )
469 {
470 evaluatedPartAnchor = partGeometry;
471 }
472 }
473 }
474 }
475
477 {
478 const QString encodedAnchor = encodeAnchorPoint( anchor );
479 context.expressionContext().setOriginalValueVariable( encodedAnchor );
480 anchor = decodeAnchorPoint( dataDefinedProperties().valueAsString( QgsCallout::Property::AnchorPointPosition, context.expressionContext(), encodedAnchor ) );
481 }
482
483 QgsGeometry line;
484 const QgsGeos labelGeos( labelGeometry.constGet() );
485
486 switch ( QgsWkbTypes::geometryType( evaluatedPartAnchor->wkbType() ) )
487 {
490 {
491 line = QgsGeometry( labelGeos.shortestLine( evaluatedPartAnchor ) );
492 break;
493 }
494
496 {
497 if ( labelGeos.intersects( evaluatedPartAnchor ) )
498 return QgsGeometry();
499
500 // ideally avoid this unwanted clone in future. For now we need it because poleOfInaccessibility/pointOnSurface are
501 // only available to QgsGeometry objects
502 const QgsGeometry evaluatedPartAnchorGeom( evaluatedPartAnchor->clone() );
503 switch ( anchor )
504 {
506 line = QgsGeometry(
507 labelGeos.shortestLine( evaluatedPartAnchorGeom.poleOfInaccessibility( std::max( evaluatedPartAnchor->boundingBox().width(), evaluatedPartAnchor->boundingBox().height() ) / 20.0 ) )
508 ); // really rough (but quick) pole of inaccessibility
509 break;
511 line = QgsGeometry( labelGeos.shortestLine( evaluatedPartAnchorGeom.pointOnSurface() ) );
512 break;
514 line = QgsGeometry( labelGeos.shortestLine( evaluatedPartAnchor ) );
515 break;
517 line = QgsGeometry( labelGeos.shortestLine( evaluatedPartAnchorGeom.centroid() ) );
518 break;
519 }
520 break;
521 }
522
525 return QgsGeometry(); // shouldn't even get here..
526 }
527 return line;
528}
529
530//
531// QgsCallout::QgsCalloutContext
532//
533
535{
536 if ( !mOriginalFeatureToMapTransform.isValid() )
537 {
538 // lazy initialization, only create if needed...
539 mOriginalFeatureToMapTransform = QgsCoordinateTransform( originalFeatureCrs, renderContext.coordinateTransform().destinationCrs(), renderContext.transformContext() );
540 }
541 return mOriginalFeatureToMapTransform;
542}
543
544
545//
546// QgsSimpleLineCallout
547//
548
550{
551 mLineSymbol = std::make_unique< QgsLineSymbol >( QgsSymbolLayerList() << new QgsSimpleLineSymbolLayer( QColor( 60, 60, 60 ), .3 ) );
552}
553
555
557 : QgsCallout( other )
558 , mLineSymbol( other.mLineSymbol ? other.mLineSymbol->clone() : nullptr )
559 , mMinCalloutLength( other.mMinCalloutLength )
560 , mMinCalloutLengthUnit( other.mMinCalloutLengthUnit )
561 , mMinCalloutLengthScale( other.mMinCalloutLengthScale )
562 , mOffsetFromAnchorDistance( other.mOffsetFromAnchorDistance )
563 , mOffsetFromAnchorUnit( other.mOffsetFromAnchorUnit )
564 , mOffsetFromAnchorScale( other.mOffsetFromAnchorScale )
565 , mOffsetFromLabelDistance( other.mOffsetFromLabelDistance )
566 , mOffsetFromLabelUnit( other.mOffsetFromLabelUnit )
567 , mOffsetFromLabelScale( other.mOffsetFromLabelScale )
568 , mDrawCalloutToAllParts( other.mDrawCalloutToAllParts )
569{}
570
571std::unique_ptr<QgsCallout> QgsSimpleLineCallout::create( const QVariantMap &properties, const QgsReadWriteContext &context )
572{
573 auto callout = std::make_unique< QgsSimpleLineCallout >();
574 callout->readProperties( properties, context );
575 return callout;
576}
577
579{
580 return u"simple"_s;
581}
582
587
588QVariantMap QgsSimpleLineCallout::properties( const QgsReadWriteContext &context ) const
589{
590 QVariantMap props = QgsCallout::properties( context );
591
592 if ( mLineSymbol )
593 {
594 props[u"lineSymbol"_s] = QgsSymbolLayerUtils::symbolProperties( mLineSymbol.get() );
595 }
596 props[u"minLength"_s] = mMinCalloutLength;
597 props[u"minLengthUnit"_s] = QgsUnitTypes::encodeUnit( mMinCalloutLengthUnit );
598 props[u"minLengthMapUnitScale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mMinCalloutLengthScale );
599
600 props[u"offsetFromAnchor"_s] = mOffsetFromAnchorDistance;
601 props[u"offsetFromAnchorUnit"_s] = QgsUnitTypes::encodeUnit( mOffsetFromAnchorUnit );
602 props[u"offsetFromAnchorMapUnitScale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetFromAnchorScale );
603 props[u"offsetFromLabel"_s] = mOffsetFromLabelDistance;
604 props[u"offsetFromLabelUnit"_s] = QgsUnitTypes::encodeUnit( mOffsetFromLabelUnit );
605 props[u"offsetFromLabelMapUnitScale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetFromLabelScale );
606
607 props[u"drawToAllParts"_s] = mDrawCalloutToAllParts;
608
609 return props;
610}
611
612void QgsSimpleLineCallout::readProperties( const QVariantMap &props, const QgsReadWriteContext &context )
613{
614 QgsCallout::readProperties( props, context );
615
616 const QString lineSymbolDef = props.value( u"lineSymbol"_s ).toString();
617 QDomDocument doc( u"symbol"_s );
618 doc.setContent( lineSymbolDef );
619 const QDomElement symbolElem = doc.firstChildElement( u"symbol"_s );
620 std::unique_ptr< QgsLineSymbol > lineSymbol( QgsSymbolLayerUtils::loadSymbol< QgsLineSymbol >( symbolElem, context ) );
621 if ( lineSymbol )
622 mLineSymbol = std::move( lineSymbol );
623
624 mMinCalloutLength = props.value( u"minLength"_s, 0 ).toDouble();
625 mMinCalloutLengthUnit = QgsUnitTypes::decodeRenderUnit( props.value( u"minLengthUnit"_s ).toString() );
626 mMinCalloutLengthScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( u"minLengthMapUnitScale"_s ).toString() );
627
628 mOffsetFromAnchorDistance = props.value( u"offsetFromAnchor"_s, 0 ).toDouble();
629 mOffsetFromAnchorUnit = QgsUnitTypes::decodeRenderUnit( props.value( u"offsetFromAnchorUnit"_s ).toString() );
630 mOffsetFromAnchorScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( u"offsetFromAnchorMapUnitScale"_s ).toString() );
631 mOffsetFromLabelDistance = props.value( u"offsetFromLabel"_s, 0 ).toDouble();
632 mOffsetFromLabelUnit = QgsUnitTypes::decodeRenderUnit( props.value( u"offsetFromLabelUnit"_s ).toString() );
633 mOffsetFromLabelScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( u"offsetFromLabelMapUnitScale"_s ).toString() );
634
635 mDrawCalloutToAllParts = props.value( u"drawToAllParts"_s, false ).toBool();
636}
637
639{
640 QgsCallout::startRender( context );
641 if ( mLineSymbol )
642 mLineSymbol->startRender( context );
643}
644
646{
647 QgsCallout::stopRender( context );
648 if ( mLineSymbol )
649 mLineSymbol->stopRender( context );
650}
651
652QSet<QString> QgsSimpleLineCallout::referencedFields( const QgsRenderContext &context ) const
653{
654 QSet<QString> fields = QgsCallout::referencedFields( context );
655 if ( mLineSymbol )
656 fields.unite( mLineSymbol->usedAttributes( context ) );
657 return fields;
658}
659
661{
662 return mLineSymbol.get();
663}
664
666{
667 mLineSymbol.reset( symbol );
668}
669
670void QgsSimpleLineCallout::draw( QgsRenderContext &context, const QRectF &rect, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext )
671{
672 LabelAnchorPoint labelAnchor = labelAnchorPoint();
674 {
675 const QString encodedAnchor = encodeLabelAnchorPoint( labelAnchor );
676 context.expressionContext().setOriginalValueVariable( encodedAnchor );
677 labelAnchor = decodeLabelAnchorPoint( dataDefinedProperties().valueAsString( QgsCallout::Property::LabelAnchorPointPosition, context.expressionContext(), encodedAnchor ) );
678 }
679
680 bool originPinned = false;
681 const QgsGeometry label = calloutLabelPoint( rect, angle, labelAnchor, context, calloutContext, originPinned );
682 if ( label.isNull() )
683 return;
684
685 auto drawCalloutLine = [this, &context, &calloutContext, &label, &rect, angle, &anchor, originPinned]( const QgsAbstractGeometry *partAnchor ) {
686 bool destinationPinned = false;
687 const QgsGeometry line = calloutLineToPart( label, partAnchor, context, calloutContext, destinationPinned );
688 if ( line.isEmpty() )
689 return;
690
691 const double lineLength = line.length();
692 if ( qgsDoubleNear( lineLength, 0 ) )
693 return;
694
695 double minLength = mMinCalloutLength;
697 {
698 context.expressionContext().setOriginalValueVariable( minLength );
700 }
701 const double minLengthPixels = context.convertToPainterUnits( minLength, mMinCalloutLengthUnit, mMinCalloutLengthScale );
702 if ( minLengthPixels > 0 && lineLength < minLengthPixels )
703 return; // too small!
704
705 std::unique_ptr< QgsCurve > calloutCurve(
706 createCalloutLine( qgsgeometry_cast< const QgsLineString * >( line.constGet() )->startPoint(), qgsgeometry_cast< const QgsLineString * >( line.constGet() )->endPoint(), context, rect, angle, anchor, calloutContext )
707 );
708
709 double offsetFromAnchor = mOffsetFromAnchorDistance;
711 {
714 }
715 const double offsetFromAnchorPixels = context.convertToPainterUnits( offsetFromAnchor, mOffsetFromAnchorUnit, mOffsetFromAnchorScale );
716
717 double offsetFromLabel = mOffsetFromLabelDistance;
719 {
722 }
723 const double offsetFromLabelPixels = context.convertToPainterUnits( offsetFromLabel, mOffsetFromLabelUnit, mOffsetFromLabelScale );
724 if ( offsetFromAnchorPixels > 0 || offsetFromLabelPixels > 0 )
725 {
726 calloutCurve.reset( calloutCurve->curveSubstring( offsetFromLabelPixels, calloutCurve->length() - offsetFromAnchorPixels ) );
727 }
728
729 const QPolygonF points = calloutCurve->asQPolygonF();
730
731 if ( points.empty() )
732 return;
733
734 QgsCalloutPosition position;
735 position.setOrigin( context.mapToPixel().toMapCoordinates( points.at( 0 ).x(), points.at( 0 ).y() ).toQPointF() );
736 position.setOriginIsPinned( originPinned );
737 position.setDestination( context.mapToPixel().toMapCoordinates( points.constLast().x(), points.constLast().y() ).toQPointF() );
738 position.setDestinationIsPinned( destinationPinned );
739 calloutContext.addCalloutPosition( position );
740
741 mLineSymbol->renderPolyline( points, nullptr, context );
742 };
743
744 bool toAllParts = mDrawCalloutToAllParts;
746 {
747 context.expressionContext().setOriginalValueVariable( toAllParts );
749 }
750
751 if ( calloutContext.allFeaturePartsLabeled || !toAllParts )
752 drawCalloutLine( anchor.constGet() );
753 else
754 {
755 for ( auto it = anchor.const_parts_begin(); it != anchor.const_parts_end(); ++it )
756 drawCalloutLine( *it );
757 }
758}
759
760QgsCurve *QgsSimpleLineCallout::createCalloutLine( const QgsPoint &start, const QgsPoint &end, QgsRenderContext &, const QRectF &, const double, const QgsGeometry &, QgsCallout::QgsCalloutContext & ) const
761{
762 return new QgsLineString( start, end );
763}
764
765//
766// QgsManhattanLineCallout
767//
768
771
775
776
777std::unique_ptr<QgsCallout> QgsManhattanLineCallout::create( const QVariantMap &properties, const QgsReadWriteContext &context ) // cppcheck-suppress duplInheritedMember
778{
779 auto callout = std::make_unique< QgsManhattanLineCallout >();
780 callout->readProperties( properties, context );
781 return callout;
782}
783
785{
786 return u"manhattan"_s;
787}
788
793
794QgsCurve *QgsManhattanLineCallout::createCalloutLine( const QgsPoint &start, const QgsPoint &end, QgsRenderContext &, const QRectF &, const double, const QgsGeometry &, QgsCallout::QgsCalloutContext & ) const
795{
796 const QgsPoint mid1 = QgsPoint( start.x(), end.y() );
797 return new QgsLineString( QVector< QgsPoint >() << start << mid1 << end );
798}
799
800
801//
802// QgsCurvedLineCallout
803//
804
807
809 : QgsSimpleLineCallout( other )
810 , mOrientation( other.mOrientation )
811 , mCurvature( other.mCurvature )
812{}
813
814std::unique_ptr<QgsCallout> QgsCurvedLineCallout::create( const QVariantMap &properties, const QgsReadWriteContext &context ) // cppcheck-suppress duplInheritedMember
815{
816 auto callout = std::make_unique< QgsCurvedLineCallout >();
817 callout->readProperties( properties, context );
818
819 callout->setCurvature( properties.value( u"curvature"_s, 0.1 ).toDouble() );
820 callout->setOrientation( decodeOrientation( properties.value( u"orientation"_s, u"auto"_s ).toString() ) );
821
822 return callout;
823}
824
826{
827 return u"curved"_s;
828}
829
834
835QVariantMap QgsCurvedLineCallout::properties( const QgsReadWriteContext &context ) const
836{
837 QVariantMap props = QgsSimpleLineCallout::properties( context );
838 props.insert( u"curvature"_s, mCurvature );
839 props.insert( u"orientation"_s, encodeOrientation( mOrientation ) );
840 return props;
841}
842
844 const QgsPoint &start, const QgsPoint &end, QgsRenderContext &context, const QRectF &rect, const double, const QgsGeometry &, QgsCallout::QgsCalloutContext &
845) const
846{
847 double curvature = mCurvature * 100;
849 {
852 }
853
854 Orientation orientation = mOrientation;
856 {
857 bool ok = false;
858 const QString orientationString = dataDefinedProperties().property( QgsCallout::Property::Orientation ).valueAsString( context.expressionContext(), QString(), &ok );
859 if ( ok )
860 {
861 orientation = decodeOrientation( orientationString );
862 }
863 }
864
865 if ( orientation == Automatic )
866 {
867 // to calculate automatically the best curve orientation, we first check which side of the label bounding box
868 // the callout origin is nearest to
869 switch ( QgsGeometryUtilsBase::closestSideOfRectangle( rect.right(), rect.bottom(), rect.left(), rect.top(), start.x(), start.y() ) )
870 {
871 case 1:
872 // closest to bottom
873 if ( qgsDoubleNear( end.x(), start.x() ) )
874 {
875 // if vertical line, we bend depending on whether the line sits towards the left or right side of the label
876 if ( start.x() < ( rect.left() + 0.5 * rect.width() ) )
878 else
880 }
881 else if ( end.x() > start.x() )
883 else
885 break;
886
887 case 2:
888 // closest to bottom-right
889 if ( end.x() < start.x() )
891 else if ( end.y() < start.y() )
893 else if ( end.x() - start.x() < end.y() - start.y() )
895 else
897 break;
898
899 case 3:
900 // closest to right
901 if ( qgsDoubleNear( end.y(), start.y() ) )
902 {
903 // if horizontal line, we bend depending on whether the line sits towards the top or bottom side of the label
904 if ( start.y() < ( rect.top() + 0.5 * rect.height() ) )
906 else
908 }
909 else if ( end.y() < start.y() )
911 else
913 break;
914
915 case 4:
916 // closest to top-right
917 if ( end.x() < start.x() )
919 else if ( end.y() > start.y() )
921 else if ( end.x() - start.x() < start.y() - end.y() )
923 else
925 break;
926
927 case 5:
928 // closest to top
929 if ( qgsDoubleNear( end.x(), start.x() ) )
930 {
931 // if vertical line, we bend depending on whether the line sits towards the left or right side of the label
932 if ( start.x() < ( rect.left() + 0.5 * rect.width() ) )
934 else
936 }
937 else if ( end.x() < start.x() )
939 else
941 break;
942
943 case 6:
944 // closest to top-left
945 if ( end.x() > start.x() )
947 else if ( end.y() > start.y() )
949 else if ( start.x() - end.x() < start.y() - end.y() )
951 else
953 break;
954
955 case 7:
956 //closest to left
957 if ( qgsDoubleNear( end.y(), start.y() ) )
958 {
959 // if horizontal line, we bend depending on whether the line sits towards the top or bottom side of the label
960 if ( start.y() < ( rect.top() + 0.5 * rect.height() ) )
962 else
964 }
965 else if ( end.y() > start.y() )
967 else
969 break;
970
971 case 8:
972 //closest to bottom-left
973 if ( end.x() > start.x() )
975 else if ( end.y() < start.y() )
977 else if ( start.x() - end.x() < end.y() - start.y() )
979 else
981 break;
982 }
983 }
984
985 // turn the line into a curved line. We do this by creating a circular string from the callout line's
986 // start to end point, where the curve point is in the middle of the callout line and perpendicularly offset
987 // by a proportion of the overall callout line length
988 const double distance = ( orientation == Clockwise ? 1 : -1 ) * start.distance( end ) * curvature / 100.0;
989 double midX, midY;
990 QgsGeometryUtilsBase::perpendicularOffsetPointAlongSegment( start.x(), start.y(), end.x(), end.y(), 0.5, distance, &midX, &midY );
991
992 return new QgsCircularString( start, QgsPoint( midX, midY ), end );
993}
994
995QgsCurvedLineCallout::Orientation QgsCurvedLineCallout::decodeOrientation( const QString &string )
996{
997 static constexpr auto ORIENTATION_MAP = makeDataDefinedStringDecoder<QgsCurvedLineCallout::Orientation>( {
998 { u"auto", Automatic },
999 { u"clockwise", Clockwise },
1000 { u"counterclockwise", CounterClockwise },
1001 } );
1002
1003 auto evaluatedAnchor = ORIENTATION_MAP.lookup( string );
1004 if ( evaluatedAnchor )
1005 {
1006 return *evaluatedAnchor;
1007 }
1008 else
1009 {
1010 return Automatic;
1011 }
1012}
1013
1014QString QgsCurvedLineCallout::encodeOrientation( QgsCurvedLineCallout::Orientation orientation )
1015{
1016 switch ( orientation )
1017 {
1019 return u"auto"_s;
1021 return u"clockwise"_s;
1023 return u"counterclockwise"_s;
1024 }
1025 return QString();
1026}
1027
1029{
1030 return mOrientation;
1031}
1032
1037
1039{
1040 return mCurvature;
1041}
1042
1044{
1045 mCurvature = curvature;
1046}
1047
1048
1049//
1050// QgsBalloonCallout
1051//
1052
1054{
1055 mFillSymbol = std::make_unique< QgsFillSymbol >( QgsSymbolLayerList() << new QgsSimpleFillSymbolLayer( QColor( 255, 200, 60 ) ) );
1056}
1057
1059
1061 : QgsCallout( other )
1062 , mFillSymbol( other.mFillSymbol ? other.mFillSymbol->clone() : nullptr )
1063 , mMarkerSymbol( other.mMarkerSymbol ? other.mMarkerSymbol->clone() : nullptr )
1064 , mOffsetFromAnchorDistance( other.mOffsetFromAnchorDistance )
1065 , mOffsetFromAnchorUnit( other.mOffsetFromAnchorUnit )
1066 , mOffsetFromAnchorScale( other.mOffsetFromAnchorScale )
1067 , mMargins( other.mMargins )
1068 , mMarginUnit( other.mMarginUnit )
1069 , mWedgeWidth( other.mWedgeWidth )
1070 , mWedgeWidthUnit( other.mWedgeWidthUnit )
1071 , mWedgeWidthScale( other.mWedgeWidthScale )
1072 , mCornerRadius( other.mCornerRadius )
1073 , mCornerRadiusUnit( other.mCornerRadiusUnit )
1074 , mCornerRadiusScale( other.mCornerRadiusScale )
1075{}
1076
1077std::unique_ptr<QgsCallout> QgsBalloonCallout::create( const QVariantMap &properties, const QgsReadWriteContext &context )
1078{
1079 auto callout = std::make_unique< QgsBalloonCallout >();
1080 callout->readProperties( properties, context );
1081 return callout;
1082}
1083
1085{
1086 return u"balloon"_s;
1087}
1088
1090{
1091 return new QgsBalloonCallout( *this );
1092}
1093
1094QVariantMap QgsBalloonCallout::properties( const QgsReadWriteContext &context ) const
1095{
1096 QVariantMap props = QgsCallout::properties( context );
1097
1098 if ( mFillSymbol )
1099 {
1100 props[u"fillSymbol"_s] = QgsSymbolLayerUtils::symbolProperties( mFillSymbol.get() );
1101 }
1102
1103 if ( mMarkerSymbol )
1104 {
1105 props[u"markerSymbol"_s] = QgsSymbolLayerUtils::symbolProperties( mMarkerSymbol.get() );
1106 }
1107
1108 props[u"offsetFromAnchor"_s] = mOffsetFromAnchorDistance;
1109 props[u"offsetFromAnchorUnit"_s] = QgsUnitTypes::encodeUnit( mOffsetFromAnchorUnit );
1110 props[u"offsetFromAnchorMapUnitScale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetFromAnchorScale );
1111
1112 props[u"margins"_s] = mMargins.toString();
1113 props[u"marginsUnit"_s] = QgsUnitTypes::encodeUnit( mMarginUnit );
1114
1115 props[u"wedgeWidth"_s] = mWedgeWidth;
1116 props[u"wedgeWidthUnit"_s] = QgsUnitTypes::encodeUnit( mWedgeWidthUnit );
1117 props[u"wedgeWidthMapUnitScale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mWedgeWidthScale );
1118
1119 props[u"cornerRadius"_s] = mCornerRadius;
1120 props[u"cornerRadiusUnit"_s] = QgsUnitTypes::encodeUnit( mCornerRadiusUnit );
1121 props[u"cornerRadiusMapUnitScale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mCornerRadiusScale );
1122
1123 return props;
1124}
1125
1126void QgsBalloonCallout::readProperties( const QVariantMap &props, const QgsReadWriteContext &context )
1127{
1128 QgsCallout::readProperties( props, context );
1129
1130 {
1131 const QString fillSymbolDef = props.value( u"fillSymbol"_s ).toString();
1132 QDomDocument doc( u"symbol"_s );
1133 doc.setContent( fillSymbolDef );
1134 const QDomElement symbolElem = doc.firstChildElement( u"symbol"_s );
1135 std::unique_ptr< QgsFillSymbol > fillSymbol( QgsSymbolLayerUtils::loadSymbol< QgsFillSymbol >( symbolElem, context ) );
1136 if ( fillSymbol )
1137 mFillSymbol = std::move( fillSymbol );
1138 }
1139
1140 {
1141 const QString markerSymbolDef = props.value( u"markerSymbol"_s ).toString();
1142 QDomDocument doc( u"symbol"_s );
1143 doc.setContent( markerSymbolDef );
1144 const QDomElement symbolElem = doc.firstChildElement( u"symbol"_s );
1145 std::unique_ptr< QgsMarkerSymbol > markerSymbol( QgsSymbolLayerUtils::loadSymbol< QgsMarkerSymbol >( symbolElem, context ) );
1146 if ( markerSymbol )
1147 mMarkerSymbol = std::move( markerSymbol );
1148 }
1149
1150 mOffsetFromAnchorDistance = props.value( u"offsetFromAnchor"_s, 0 ).toDouble();
1151 mOffsetFromAnchorUnit = QgsUnitTypes::decodeRenderUnit( props.value( u"offsetFromAnchorUnit"_s ).toString() );
1152 mOffsetFromAnchorScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( u"offsetFromAnchorMapUnitScale"_s ).toString() );
1153
1154 mMargins = QgsMargins::fromString( props.value( u"margins"_s ).toString() );
1155 mMarginUnit = QgsUnitTypes::decodeRenderUnit( props.value( u"marginsUnit"_s ).toString() );
1156
1157 mWedgeWidth = props.value( u"wedgeWidth"_s, 2.64 ).toDouble();
1158 mWedgeWidthUnit = QgsUnitTypes::decodeRenderUnit( props.value( u"wedgeWidthUnit"_s ).toString() );
1159 mWedgeWidthScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( u"wedgeWidthMapUnitScale"_s ).toString() );
1160
1161 mCornerRadius = props.value( u"cornerRadius"_s, 0 ).toDouble();
1162 mCornerRadiusUnit = QgsUnitTypes::decodeRenderUnit( props.value( u"cornerRadiusUnit"_s ).toString() );
1163 mCornerRadiusScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( u"cornerRadiusMapUnitScale"_s ).toString() );
1164}
1165
1167{
1168 QgsCallout::startRender( context );
1169 if ( mFillSymbol )
1170 mFillSymbol->startRender( context );
1171 if ( mMarkerSymbol )
1172 mMarkerSymbol->startRender( context );
1173}
1174
1176{
1177 QgsCallout::stopRender( context );
1178 if ( mFillSymbol )
1179 mFillSymbol->stopRender( context );
1180 if ( mMarkerSymbol )
1181 mMarkerSymbol->stopRender( context );
1182}
1183
1184QSet<QString> QgsBalloonCallout::referencedFields( const QgsRenderContext &context ) const
1185{
1186 QSet<QString> fields = QgsCallout::referencedFields( context );
1187 if ( mFillSymbol )
1188 fields.unite( mFillSymbol->usedAttributes( context ) );
1189 if ( mMarkerSymbol )
1190 fields.unite( mMarkerSymbol->usedAttributes( context ) );
1191 return fields;
1192}
1193
1195{
1196 return mFillSymbol.get();
1197}
1198
1200{
1201 mFillSymbol.reset( symbol );
1202}
1203
1205{
1206 return mMarkerSymbol.get();
1207}
1208
1210{
1211 mMarkerSymbol.reset( symbol );
1212}
1213
1214void QgsBalloonCallout::draw( QgsRenderContext &context, const QRectF &rect, const double, const QgsGeometry &anchor, QgsCalloutContext &calloutContext )
1215{
1216 bool destinationIsPinned = false;
1217 QgsGeometry line = calloutLineToPart( QgsGeometry::fromRect( rect ), anchor.constGet(), context, calloutContext, destinationIsPinned );
1218
1219 if ( mMarkerSymbol )
1220 {
1222 {
1223 QgsPoint anchorPoint = ls->endPoint();
1224 mMarkerSymbol->renderPoint( anchorPoint.toQPointF(), nullptr, context );
1225 }
1226 }
1227
1228 double offsetFromAnchor = mOffsetFromAnchorDistance;
1230 {
1233 }
1234 const double offsetFromAnchorPixels = context.convertToPainterUnits( offsetFromAnchor, mOffsetFromAnchorUnit, mOffsetFromAnchorScale );
1235
1236 if ( offsetFromAnchorPixels > 0 )
1237 {
1239 {
1240 line = QgsGeometry( ls->curveSubstring( 0, ls->length() - offsetFromAnchorPixels ) );
1241 }
1242 }
1243
1244 QgsPointXY destination;
1245 QgsPointXY origin;
1247 {
1248 origin = ls->startPoint();
1249 destination = ls->endPoint();
1250 }
1251 else
1252 {
1253 destination = QgsPointXY( rect.center() );
1254 }
1255
1256 const QPolygonF points = getPoints( context, destination, rect );
1257 if ( points.empty() )
1258 return;
1259
1260 if ( !origin.isEmpty() )
1261 {
1262 QgsCalloutPosition position;
1263 position.setOrigin( context.mapToPixel().toMapCoordinates( origin.x(), origin.y() ).toQPointF() );
1264 position.setOriginIsPinned( false );
1265 position.setDestination( context.mapToPixel().toMapCoordinates( destination.x(), destination.y() ).toQPointF() );
1266 position.setDestinationIsPinned( destinationIsPinned );
1267 calloutContext.addCalloutPosition( position );
1268 }
1269
1270 mFillSymbol->renderPolygon( points, nullptr, nullptr, context );
1271}
1272
1273QPolygonF QgsBalloonCallout::getPoints( QgsRenderContext &context, QgsPointXY origin, QRectF rect ) const
1274{
1275 double segmentPointWidth = mWedgeWidth;
1277 {
1278 context.expressionContext().setOriginalValueVariable( segmentPointWidth );
1279 segmentPointWidth = dataDefinedProperties().valueAsDouble( QgsCallout::Property::WedgeWidth, context.expressionContext(), segmentPointWidth );
1280 }
1281 segmentPointWidth = context.convertToPainterUnits( segmentPointWidth, mWedgeWidthUnit, mWedgeWidthScale );
1282
1283 double cornerRadius = mCornerRadius;
1285 {
1288 }
1289 cornerRadius = context.convertToPainterUnits( cornerRadius, mCornerRadiusUnit, mCornerRadiusScale );
1290
1291 double left = mMargins.left();
1292 double right = mMargins.right();
1293 double top = mMargins.top();
1294 double bottom = mMargins.bottom();
1295
1297 {
1298 const QVariant value = dataDefinedProperties().value( QgsCallout::Property::Margins, context.expressionContext() );
1299 if ( !QgsVariantUtils::isNull( value ) )
1300 {
1301 if ( value.userType() == QMetaType::Type::QVariantList )
1302 {
1303 const QVariantList list = value.toList();
1304 if ( list.size() == 4 )
1305 {
1306 bool topOk = false;
1307 bool rightOk = false;
1308 bool bottomOk = false;
1309 bool leftOk = false;
1310 const double evaluatedTop = list.at( 0 ).toDouble( &topOk );
1311 const double evaluatedRight = list.at( 1 ).toDouble( &rightOk );
1312 const double evaluatedBottom = list.at( 2 ).toDouble( &bottomOk );
1313 const double evaluatedLeft = list.at( 3 ).toDouble( &leftOk );
1314 if ( topOk && rightOk && bottomOk && leftOk )
1315 {
1316 left = evaluatedLeft;
1317 top = evaluatedTop;
1318 right = evaluatedRight;
1319 bottom = evaluatedBottom;
1320 }
1321 }
1322 }
1323 else
1324 {
1325 const QStringList list = value.toString().trimmed().split( ',' );
1326 if ( list.count() == 4 )
1327 {
1328 bool topOk = false;
1329 bool rightOk = false;
1330 bool bottomOk = false;
1331 bool leftOk = false;
1332 const double evaluatedTop = list.at( 0 ).toDouble( &topOk );
1333 const double evaluatedRight = list.at( 1 ).toDouble( &rightOk );
1334 const double evaluatedBottom = list.at( 2 ).toDouble( &bottomOk );
1335 const double evaluatedLeft = list.at( 3 ).toDouble( &leftOk );
1336 if ( topOk && rightOk && bottomOk && leftOk )
1337 {
1338 left = evaluatedLeft;
1339 top = evaluatedTop;
1340 right = evaluatedRight;
1341 bottom = evaluatedBottom;
1342 }
1343 }
1344 }
1345 }
1346 }
1347
1348 const double marginLeft = context.convertToPainterUnits( left, mMarginUnit );
1349 const double marginRight = context.convertToPainterUnits( right, mMarginUnit );
1350 const double marginTop = context.convertToPainterUnits( top, mMarginUnit );
1351 const double marginBottom = context.convertToPainterUnits( bottom, mMarginUnit );
1352
1353 const QRectF expandedRect = rect.height() < 0 ? QRectF( rect.left() - marginLeft, rect.top() + marginBottom, rect.width() + marginLeft + marginRight, rect.height() - marginTop - marginBottom )
1354 : QRectF( rect.left() - marginLeft, rect.top() - marginTop, rect.width() + marginLeft + marginRight, rect.height() + marginTop + marginBottom );
1355
1356 // IMPORTANT -- check for degenerate height is sometimes >=0, because QRectF are not normalized and we are using painter
1357 // coordinates with descending vertical axis!
1358 if ( expandedRect.width() <= 0 || ( rect.height() < 0 && expandedRect.height() >= 0 ) || ( rect.height() > 0 && expandedRect.height() <= 0 ) )
1359 return QPolygonF();
1360
1361 const QPainterPath path = QgsShapeGenerator::createBalloon( origin, expandedRect, segmentPointWidth, cornerRadius );
1362 const QTransform t = QTransform::fromScale( 100, 100 );
1363 const QTransform ti = t.inverted();
1364 const QPolygonF poly = path.toFillPolygon( t );
1365 return ti.map( poly );
1366}
@ ForceVector
Always force vector-based rendering, even when the result will be visually different to a raster-base...
Definition qgis.h:2919
BlendMode
Blending modes defining the available composition modes that can be used when painting.
Definition qgis.h:5497
@ Normal
Normal.
Definition qgis.h:5498
@ Point
Points.
Definition qgis.h:400
@ Line
Lines.
Definition qgis.h:401
@ Polygon
Polygons.
Definition qgis.h:402
@ Unknown
Unknown types.
Definition qgis.h:403
@ Null
No geometry.
Definition qgis.h:404
@ Point
Point.
Definition qgis.h:316
Abstract base class for all geometries.
virtual QgsRectangle boundingBox() const
Returns the minimal bounding box for the geometry.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
bool valueAsBool(int key, const QgsExpressionContext &context, bool defaultValue=false, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as an boolean.
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.
QgsFillSymbol * fillSymbol()
Returns the fill symbol used to render the callout.
double cornerRadius() const
Returns the corner radius of the balloon shapes.
QString type() const override
Returns a unique string representing the callout type.
QgsMarkerSymbol * markerSymbol()
Returns the marker symbol used to render the callout endpoint.
void startRender(QgsRenderContext &context) override
Prepares the callout for rendering on the specified render context.
QSet< QString > referencedFields(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the callout.
void stopRender(QgsRenderContext &context) override
Finalises the callout after a set of rendering operations on the specified render context.
QVariantMap properties(const QgsReadWriteContext &context) const override
Returns the properties describing the callout encoded in a string format.
void setFillSymbol(QgsFillSymbol *symbol)
Sets the fill symbol used to render the callout.
static std::unique_ptr< QgsCallout > create(const QVariantMap &properties=QVariantMap(), const QgsReadWriteContext &context=QgsReadWriteContext())
Creates a new QgsBalloonCallout, using the settings serialized in the properties map (corresponding t...
void draw(QgsRenderContext &context, const QRectF &bodyBoundingBox, const double angle, const QgsGeometry &anchor, QgsCallout::QgsCalloutContext &calloutContext) override
Performs the actual rendering of the callout implementation onto the specified render context.
void setMarkerSymbol(QgsMarkerSymbol *symbol)
Sets the marker symbol used to render the callout endpoint.
~QgsBalloonCallout() override
QgsBalloonCallout * clone() const override
Duplicates a callout by creating a deep copy of the callout.
double offsetFromAnchor() const
Returns the offset distance from the anchor point at which to start the line.
Definition qgscallout.h:953
void readProperties(const QVariantMap &props, const QgsReadWriteContext &context) override
Reads a string map of an callout's properties and restores the callout to the state described by the ...
Represents the calculated placement of a map label callout line.
void setDestination(const QPointF &destination)
Sets the destination of the callout line, in map coordinates.
void setOrigin(const QPointF &origin)
Sets the origin of the callout line, in map coordinates.
void setDestinationIsPinned(bool pinned)
Sets whether the destination of the callout has pinned (manually placed).
void setOriginIsPinned(bool pinned)
Sets whether the origin of the callout has pinned (manually placed).
Contains additional contextual information about the context in which a callout is being rendered.
Definition qgscallout.h:248
QgsCoordinateTransform originalFeatureToMapTransform(const QgsRenderContext &renderContext) const
Returns the coordinate transform to convert from the original layer associated with the callout to th...
void addCalloutPosition(const QgsCalloutPosition &position)
Adds a rendered callout position.
Definition qgscallout.h:278
bool allFeaturePartsLabeled
true if all parts of associated feature were labeled
Definition qgscallout.h:251
QgsCoordinateReferenceSystem originalFeatureCrs
Contains the CRS of the original feature associated with this callout.
Definition qgscallout.h:258
static QString encodeAnchorPoint(AnchorPoint anchor)
Encodes an anchor point to its string representation.
bool containsAdvancedEffects() const
Returns true if the callout requires advanced effects such as blend modes, which require output in ra...
virtual bool saveProperties(QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context) const
Saves the current state of the callout to a DOM element.
virtual void stopRender(QgsRenderContext &context)
Finalises the callout after a set of rendering operations on the specified render context.
QgsGeometry calloutLineToPart(const QgsGeometry &labelGeometry, const QgsAbstractGeometry *partGeometry, QgsRenderContext &context, const QgsCalloutContext &calloutContext, bool &pinned) const
Calculates the direct line from a label geometry to an anchor geometry part, respecting the various c...
void setEnabled(bool enabled)
Sets whether the callout is enabled.
virtual QString type() const =0
Returns a unique string representing the callout type.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the callout's property collection, used for data defined overrides.
Definition qgscallout.h:333
QgsGeometry calloutLabelPoint(const QRectF &bodyBoundingBox, double angle, LabelAnchorPoint anchor, QgsRenderContext &context, const QgsCalloutContext &calloutContext, bool &pinned) const
Returns the anchor point geometry for a label with the given bounding box and anchor point mode.
static QgsPropertiesDefinition propertyDefinitions()
Returns the definitions for data defined properties available for use in callouts.
AnchorPoint anchorPoint() const
Returns the feature's anchor point position.
Definition qgscallout.h:363
LabelAnchorPoint labelAnchorPoint() const
Returns the label's anchor point position.
Definition qgscallout.h:396
AnchorPoint
Feature's anchor point position.
Definition qgscallout.h:114
@ PointOnExterior
A point on the surface's outline closest to the label is used as anchor for polygon geometries.
Definition qgscallout.h:116
@ Centroid
The surface's centroid is used as anchor for polygon geometries.
Definition qgscallout.h:118
@ PointOnSurface
A point guaranteed to be on the surface is used as anchor for polygon geometries.
Definition qgscallout.h:117
@ PoleOfInaccessibility
The surface's pole of inaccessibility used as anchor for polygon geometries.
Definition qgscallout.h:115
void render(QgsRenderContext &context, const QRectF &rect, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext)
Renders the callout onto the specified render context.
virtual void restoreProperties(const QDomElement &element, const QgsReadWriteContext &context)
Restores the callout's properties from a DOM element.
virtual void draw(QgsRenderContext &context, const QRectF &bodyBoundingBox, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext)=0
Performs the actual rendering of the callout implementation onto the specified render context.
static QString encodeLabelAnchorPoint(LabelAnchorPoint anchor)
Encodes a label anchor point to its string representation.
virtual void startRender(QgsRenderContext &context)
Prepares the callout for rendering on the specified render context.
static QgsCallout::LabelAnchorPoint decodeLabelAnchorPoint(const QString &name, bool *ok=nullptr)
Attempts to decode a string representation of a label anchor point name to the corresponding anchor p...
Q_DECL_DEPRECATED QgsGeometry labelAnchorGeometry(const QRectF &bodyBoundingBox, const double angle, LabelAnchorPoint anchor) const
Returns the anchor point geometry for a label with the given bounding box and anchor point mode.
QgsCallout()
Constructor for QgsCallout.
DrawOrder
Options for draw order (stacking) of callouts.
Definition qgscallout.h:107
@ OrderBelowAllLabels
Render callouts below all labels.
Definition qgscallout.h:108
virtual DrawOrder drawOrder() const
Returns the desired drawing order (stacking) to use while rendering this callout.
QPainter::CompositionMode blendMode() const
Returns the blending mode used for drawing callouts.
Definition qgscallout.h:430
LabelAnchorPoint
Label's anchor point position.
Definition qgscallout.h:126
@ LabelPointOnExterior
The point on the label's boundary closest to the feature.
Definition qgscallout.h:127
@ LabelBottomLeft
Bottom left corner of the label's boundary.
Definition qgscallout.h:134
@ LabelBottomMiddle
Bottom middle of the label's boundary.
Definition qgscallout.h:135
@ LabelMiddleLeft
Middle left of the label's boundary.
Definition qgscallout.h:132
@ LabelBottomRight
Bottom right corner of the label's boundary.
Definition qgscallout.h:136
@ LabelMiddleRight
Middle right of the label's boundary.
Definition qgscallout.h:133
@ LabelTopMiddle
Top middle of the label's boundary.
Definition qgscallout.h:130
@ LabelTopLeft
Top left corner of the label's boundary.
Definition qgscallout.h:129
@ LabelCentroid
The labe's centroid.
Definition qgscallout.h:128
@ LabelTopRight
Top right corner of the label's boundary.
Definition qgscallout.h:131
static QgsCallout::AnchorPoint decodeAnchorPoint(const QString &name, bool *ok=nullptr)
Attempts to decode a string representation of an anchor point name to the corresponding anchor point.
virtual void readProperties(const QVariantMap &props, const QgsReadWriteContext &context)
Reads a string map of an callout's properties and restores the callout to the state described by the ...
virtual QSet< QString > referencedFields(const QgsRenderContext &context) const
Returns the set of attributes referenced by the callout.
@ MinimumCalloutLength
Minimum length of callouts.
Definition qgscallout.h:86
@ LabelAnchorPointPosition
Label's anchor point position.
Definition qgscallout.h:91
@ CornerRadius
Balloon callout corner radius.
Definition qgscallout.h:100
@ OffsetFromAnchor
Distance to offset lines from anchor points.
Definition qgscallout.h:87
@ OffsetFromLabel
Distance to offset lines from label area.
Definition qgscallout.h:88
@ DestinationX
X-coordinate of callout destination (feature anchor).
Definition qgscallout.h:94
@ DestinationY
Y-coordinate of callout destination (feature anchor).
Definition qgscallout.h:95
@ WedgeWidth
Balloon callout wedge width.
Definition qgscallout.h:99
@ AnchorPointPosition
Feature's anchor point position.
Definition qgscallout.h:90
@ OriginX
X-coordinate of callout origin (label anchor).
Definition qgscallout.h:92
@ OriginY
Y-coordinate of callout origin (label anchor).
Definition qgscallout.h:93
@ Curvature
Curvature of curved line callouts.
Definition qgscallout.h:96
@ BlendMode
Callout blend mode.
Definition qgscallout.h:101
@ Orientation
Orientation of curved line callouts.
Definition qgscallout.h:97
@ Margins
Margin from text.
Definition qgscallout.h:98
@ DrawCalloutToAllParts
Whether callout lines should be drawn to all feature parts.
Definition qgscallout.h:89
virtual QVariantMap properties(const QgsReadWriteContext &context) const
Returns the properties describing the callout encoded in a string format.
bool enabled() const
Returns true if the callout is enabled.
Definition qgscallout.h:321
Circular string geometry type.
Handles coordinate transforms between two coordinate systems.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system, which the transform will transform coordinates t...
Custom exception class for Coordinate Reference System related exceptions.
Abstract base class for curved geometry type.
Definition qgscurve.h:36
QgsCurvedLineCallout * clone() const override
Duplicates a callout by creating a deep copy of the callout.
QgsCurve * createCalloutLine(const QgsPoint &start, const QgsPoint &end, QgsRenderContext &context, const QRectF &bodyBoundingBox, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext) const override
Creates a callout line between start and end in the desired style.
QVariantMap properties(const QgsReadWriteContext &context) const override
Returns the properties describing the callout encoded in a string format.
void setOrientation(Orientation orientation)
Sets the callout line's curve orientation.
static std::unique_ptr< QgsCallout > create(const QVariantMap &properties=QVariantMap(), const QgsReadWriteContext &context=QgsReadWriteContext())
Creates a new QgsCurvedLineCallout, using the settings serialized in the properties map (correspondin...
void setCurvature(double curvature)
Sets the callout line's curvature.
QString type() const override
Returns a unique string representing the callout type.
double curvature() const
Returns the callout line's curvature.
Orientation
Curve orientation.
Definition qgscallout.h:789
@ Clockwise
Curve lines in a clockwise direction.
Definition qgscallout.h:791
@ CounterClockwise
Curve lines in a counter-clockwise direction.
Definition qgscallout.h:792
@ Automatic
Automatically choose most cartographically pleasing orientation based on label and callout arrangemen...
Definition qgscallout.h:790
Orientation orientation() const
Returns the callout line's curve orientation.
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for the context.
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
static int closestSideOfRectangle(double right, double bottom, double left, double top, double x, double y)
Returns a number representing the closest side of a rectangle defined by /a right,...
static void perpendicularOffsetPointAlongSegment(double x1, double y1, double x2, double y2, double proportion, double offset, double *x, double *y)
Calculates a point a certain proportion of the way along the segment from (x1, y1) to (x2,...
A geometry is the spatial representation of a feature.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
double length() const
Returns the planar, 2-dimensional length of geometry.
QgsGeometry poleOfInaccessibility(double precision, double *distanceToBoundary=nullptr) const
Calculates the approximate pole of inaccessibility for a surface, which is the most distant internal ...
QgsAbstractGeometry::const_part_iterator const_parts_begin() const
Returns STL-style const iterator pointing to the first part of the geometry.
QgsGeometry pointOnSurface() const
Returns a point guaranteed to lie on the surface of a geometry.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static QgsGeometry fromPointXY(const QgsPointXY &point)
Creates a new geometry from a QgsPointXY object.
QgsGeometry centroid() const
Returns the center of mass of a geometry.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
QgsAbstractGeometry::const_part_iterator const_parts_end() const
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Qgis::GeometryOperationResult rotate(double rotation, const QgsPointXY &center)
Rotate this geometry around the Z axis.
Does vector analysis using the GEOS library and handles import, export, and exception handling.
Definition qgsgeos.h:192
bool intersects(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const override
Checks if geom intersects this.
Definition qgsgeos.cpp:905
std::unique_ptr< QgsAbstractGeometry > shortestLine(const QgsGeometry &other, QString *errorMsg=nullptr, QgsFeedback *feedback=nullptr) const
Returns the shortest line joining this geometry to the other geometry.
Definition qgsgeos.cpp:3773
Line string geometry type, with support for z-dimension and m-values.
A line symbol type, for rendering LineString and MultiLineString geometries.
QString type() const override
Returns a unique string representing the callout type.
static std::unique_ptr< QgsCallout > create(const QVariantMap &properties=QVariantMap(), const QgsReadWriteContext &context=QgsReadWriteContext())
Creates a new QgsManhattanLineCallout, using the settings serialized in the properties map (correspon...
QgsCurve * createCalloutLine(const QgsPoint &start, const QgsPoint &end, QgsRenderContext &context, const QRectF &bodyBoundingBox, const double angle, const QgsGeometry &anchor, QgsCallout::QgsCalloutContext &calloutContext) const override
Creates a callout line between start and end in the desired style.
QgsManhattanLineCallout * clone() const override
Duplicates a callout by creating a deep copy of the callout.
QgsPointXY toMapCoordinates(int x, int y) const
Transforms device coordinates to map (world) coordinates.
QgsPointXY transform(const QgsPointXY &p) const
Transforms a point p from map (world) coordinates to device coordinates.
static QgsMargins fromString(const QString &string)
Returns a QgsMargins object decoded from a string, or a null QgsMargins if the string could not be in...
A marker symbol type, for rendering Point and MultiPoint geometries.
static Qgis::BlendMode getBlendModeEnum(QPainter::CompositionMode blendMode)
Returns a Qgis::BlendMode corresponding to a QPainter::CompositionMode.
static QPainter::CompositionMode getCompositionMode(Qgis::BlendMode blendMode)
Returns a QPainter::CompositionMode corresponding to a Qgis::BlendMode.
Represents a 2D point.
Definition qgspointxy.h:62
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
bool isEmpty() const
Returns true if the geometry is empty.
Definition qgspointxy.h:245
QPointF toQPointF() const
Converts a point to a QPointF.
Definition qgspointxy.h:168
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
double x
Definition qgspoint.h:56
double distance(double x, double y) const
Returns the Cartesian 2D distance between this point and a specified x, y coordinate.
Definition qgspoint.h:466
double y
Definition qgspoint.h:57
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const final
Returns the calculated value of the property with the specified key from within the collection.
bool isActive(int key) const final
Returns true if the collection contains an active property with the specified key.
QgsProperty property(int key) const final
Returns a matching property from the collection, if one exists.
@ Double
Double value (including negative values).
Definition qgsproperty.h:56
@ BlendMode
Blend mode.
Definition qgsproperty.h:66
@ Boolean
Boolean value.
Definition qgsproperty.h:52
@ DoublePositive
Positive double value (including 0).
Definition qgsproperty.h:57
@ DataTypeString
Property requires a string value.
Definition qgsproperty.h:91
QString valueAsString(const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property and interprets it as a string.
A container for the context for various read/write operations on objects.
QRectF toRectF() const
Returns a QRectF with same coordinates as the rectangle.
QgsRectangle buffered(double width) const
Gets rectangle enlarged by buffer.
Contains information about the context of a rendering operation.
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.
QgsExpressionContext & expressionContext()
Gets the expression context.
QgsCoordinateTransformContext transformContext() const
Returns the context's coordinate transform context, which stores various information regarding which ...
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
Qgis::RasterizedRenderingPolicy rasterizedRenderingPolicy() const
Returns the policy controlling when rasterisation of content during renders is permitted.
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
static QPolygonF createBalloon(const QgsPointXY &origin, const QRectF &rect, double wedgeWidth)
Generates a "balloon"/"talking bubble" style shape (as a QPolygonF).
Renders polygons using a single fill and stroke color.
void startRender(QgsRenderContext &context) override
Prepares the callout for rendering on the specified render context.
void setLineSymbol(QgsLineSymbol *symbol)
Sets the line symbol used to render the callout line.
void stopRender(QgsRenderContext &context) override
Finalises the callout after a set of rendering operations on the specified render context.
QSet< QString > referencedFields(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the callout.
double offsetFromLabel() const
Returns the offset distance from label area at which to end the line.
Definition qgscallout.h:647
void readProperties(const QVariantMap &props, const QgsReadWriteContext &context) override
Reads a string map of an callout's properties and restores the callout to the state described by the ...
static std::unique_ptr< QgsCallout > create(const QVariantMap &properties=QVariantMap(), const QgsReadWriteContext &context=QgsReadWriteContext())
Creates a new QgsSimpleLineCallout, using the settings serialized in the properties map (correspondin...
void draw(QgsRenderContext &context, const QRectF &bodyBoundingBox, const double angle, const QgsGeometry &anchor, QgsCallout::QgsCalloutContext &calloutContext) override
Performs the actual rendering of the callout implementation onto the specified render context.
QString type() const override
Returns a unique string representing the callout type.
double offsetFromAnchor() const
Returns the offset distance from the anchor point at which to start the line.
Definition qgscallout.h:603
~QgsSimpleLineCallout() override
virtual QgsCurve * createCalloutLine(const QgsPoint &start, const QgsPoint &end, QgsRenderContext &context, const QRectF &bodyBoundingBox, const double angle, const QgsGeometry &anchor, QgsCallout::QgsCalloutContext &calloutContext) const
Creates a callout line between start and end in the desired style.
QVariantMap properties(const QgsReadWriteContext &context) const override
Returns the properties describing the callout encoded in a string format.
QgsSimpleLineCallout * clone() const override
Duplicates a callout by creating a deep copy of the callout.
QgsLineSymbol * lineSymbol()
Returns the line symbol used to render the callout line.
A simple line symbol layer, which renders lines using a line in a variety of styles (e....
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
static std::unique_ptr< QgsSymbol > loadSymbol(const QDomElement &element, const QgsReadWriteContext &context)
Attempts to load a symbol from a DOM element.
static QPainter::CompositionMode decodeBlendMode(const QString &s)
static QString symbolProperties(QgsSymbol *symbol)
Returns a string representing the symbol.
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.
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
static QDomElement writeVariant(const QVariant &value, QDomDocument &doc)
Write a QVariant to a QDomElement.
static QVariant readVariant(const QDomElement &element)
Read a QVariant from a QDomElement.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7795
T qgsgeometry_cast(QgsAbstractGeometry *geom)
constexpr QgsDataDefinedStringDecoder< V, N > makeDataDefinedStringDecoder(const DataDefinedDecoderEntry< V >(&values)[N])
Creates a QgsDataDefinedStringDecoder from a hardcoded lookup table.
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition qgssymbol.h:30