QGIS API Documentation 3.99.0-Master (d270888f95f)
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 "qgsfillsymbol.h"
24#include "qgsfillsymbollayer.h"
25#include "qgsgeometryutils.h"
26#include "qgsgeos.h"
27#include "qgslinestring.h"
28#include "qgslinesymbol.h"
29#include "qgslinesymbollayer.h"
30#include "qgsmarkersymbol.h"
31#include "qgspainting.h"
32#include "qgsrendercontext.h"
33#include "qgsshapegenerator.h"
34#include "qgssymbol.h"
35#include "qgssymbollayerutils.h"
36#include "qgsunittypes.h"
37#include "qgsvariantutils.h"
38#include "qgsxmlutils.h"
39
40#include <QString>
41
42using namespace Qt::StringLiterals;
43
44QgsPropertiesDefinition QgsCallout::sPropertyDefinitions;
45
46void QgsCallout::initPropertyDefinitions()
47{
48 const QString origin = u"callouts"_s;
49
50 sPropertyDefinitions = QgsPropertiesDefinition
51 {
52 { static_cast< int >( QgsCallout::Property::MinimumCalloutLength ), QgsPropertyDefinition( "MinimumCalloutLength", QObject::tr( "Minimum callout length" ), QgsPropertyDefinition::DoublePositive, origin ) },
53 { static_cast< int >( QgsCallout::Property::OffsetFromAnchor ), QgsPropertyDefinition( "OffsetFromAnchor", QObject::tr( "Offset from feature" ), QgsPropertyDefinition::DoublePositive, origin ) },
54 { static_cast< int >( QgsCallout::Property::OffsetFromLabel ), QgsPropertyDefinition( "OffsetFromLabel", QObject::tr( "Offset from label" ), QgsPropertyDefinition::DoublePositive, origin ) },
55 { static_cast< int >( QgsCallout::Property::DrawCalloutToAllParts ), QgsPropertyDefinition( "DrawCalloutToAllParts", QObject::tr( "Draw lines to all feature parts" ), QgsPropertyDefinition::Boolean, origin ) },
56 { static_cast< int >( QgsCallout::Property::AnchorPointPosition ), 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 ) },
57 {
58 static_cast< int >( QgsCallout::Property::LabelAnchorPointPosition ), QgsPropertyDefinition( "LabelAnchorPointPosition", QgsPropertyDefinition::DataTypeString, QObject::tr( "Label's anchor point position" ), QObject::tr( "string " ) + "[<b>point_on_exterior</b>|<b>centroid</b>|<b>TL</b>=Top left|<b>T</b>=Top middle|"
59 "<b>TR</b>=Top right|<br>"
60 "<b>L</b>=Left|<b>R</b>=Right|<br>"
61 "<b>BL</b>=Bottom left|<b>B</b>=Bottom middle|"
62 "<b>BR</b>=Bottom right]", origin )
63 },
64 { static_cast< int >( QgsCallout::Property::OriginX ), QgsPropertyDefinition( "OriginX", QObject::tr( "Callout origin (X)" ), QgsPropertyDefinition::Double, origin ) },
65 { static_cast< int >( QgsCallout::Property::OriginY ), QgsPropertyDefinition( "OriginY", QObject::tr( "Callout origin (Y)" ), QgsPropertyDefinition::Double, origin ) },
66 { static_cast< int >( QgsCallout::Property::DestinationX ), QgsPropertyDefinition( "DestinationX", QObject::tr( "Callout destination (X)" ), QgsPropertyDefinition::Double, origin ) },
67 { static_cast< int >( QgsCallout::Property::DestinationY ), QgsPropertyDefinition( "DestinationY", QObject::tr( "Callout destination (Y)" ), QgsPropertyDefinition::Double, origin ) },
68 { static_cast< int >( QgsCallout::Property::Curvature ), QgsPropertyDefinition( "Curvature", QObject::tr( "Callout line curvature" ), QgsPropertyDefinition::Double, origin ) },
69 {
70 static_cast< int >( QgsCallout::Property::Orientation ), QgsPropertyDefinition( "Orientation", QgsPropertyDefinition::DataTypeString, QObject::tr( "Callout curve orientation" ), QObject::tr( "string " ) + "[<b>auto</b>|<b>clockwise</b>|<b>counterclockwise</b>]", origin )
71 },
72 {
73 static_cast< int >( QgsCallout::Property::Margins ), 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>" ) )
74 },
75 { static_cast< int >( QgsCallout::Property::WedgeWidth ), QgsPropertyDefinition( "WedgeWidth", QObject::tr( "Wedge width" ), QgsPropertyDefinition::DoublePositive, origin ) },
76 { static_cast< int >( QgsCallout::Property::CornerRadius ), QgsPropertyDefinition( "CornerRadius", QObject::tr( "Corner radius" ), QgsPropertyDefinition::DoublePositive, origin ) },
77 { static_cast< int >( QgsCallout::Property::BlendMode ), QgsPropertyDefinition( "BlendMode", QObject::tr( "Callout blend mode" ), QgsPropertyDefinition::BlendMode, origin ) },
78 };
79}
80
81
85
86QVariantMap QgsCallout::properties( const QgsReadWriteContext & ) const
87{
88 QVariantMap props;
89 props.insert( u"enabled"_s, mEnabled ? "1" : "0" );
90 props.insert( u"anchorPoint"_s, encodeAnchorPoint( mAnchorPoint ) );
91 props.insert( u"labelAnchorPoint"_s, encodeLabelAnchorPoint( mLabelAnchorPoint ) );
92 props.insert( u"blendMode"_s, static_cast< int >( QgsPainting::getBlendModeEnum( mBlendMode ) ) );
93 props.insert( u"ddProperties"_s, mDataDefinedProperties.toVariant( propertyDefinitions() ) );
94 return props;
95}
96
97void QgsCallout::readProperties( const QVariantMap &props, const QgsReadWriteContext & )
98{
99 mEnabled = props.value( u"enabled"_s, u"0"_s ).toInt();
100 mAnchorPoint = decodeAnchorPoint( props.value( u"anchorPoint"_s, QString() ).toString() );
101 mLabelAnchorPoint = decodeLabelAnchorPoint( props.value( u"labelAnchorPoint"_s, QString() ).toString() );
103 static_cast< Qgis::BlendMode >( props.value( u"blendMode"_s, QString::number( static_cast< int >( Qgis::BlendMode::Normal ) ) ).toUInt() ) );
104 mDataDefinedProperties.loadVariant( props.value( u"ddProperties"_s ), propertyDefinitions() );
105}
106
107bool QgsCallout::saveProperties( QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context ) const
108{
109 if ( element.isNull() )
110 {
111 return false;
112 }
113
114 const QDomElement calloutPropsElement = QgsXmlUtils::writeVariant( properties( context ), doc );
115
116 QDomElement calloutElement = doc.createElement( u"callout"_s );
117 calloutElement.setAttribute( u"type"_s, type() );
118 calloutElement.appendChild( calloutPropsElement );
119
120 element.appendChild( calloutElement );
121 return true;
122}
123
124void QgsCallout::restoreProperties( const QDomElement &element, const QgsReadWriteContext &context )
125{
126 const QVariantMap props = QgsXmlUtils::readVariant( element.firstChildElement() ).toMap();
127 readProperties( props, context );
128}
129
138
140{
141 return mBlendMode != QPainter::CompositionMode_SourceOver || dataDefinedProperties().isActive( QgsCallout::Property::BlendMode );
142}
143
144QSet<QString> QgsCallout::referencedFields( const QgsRenderContext &context ) const
145{
146 mDataDefinedProperties.prepare( context.expressionContext() );
147 return mDataDefinedProperties.referencedFields( context.expressionContext() );
148}
149
154
155void QgsCallout::render( QgsRenderContext &context, const QRectF &rect, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext )
156{
157 QPainter *painter = context.painter();
159 {
160 const QPainter::CompositionMode blendMode = mBlendMode;
162 {
163 context.expressionContext().setOriginalValueVariable( QString() );
165 }
166
167 painter->setCompositionMode( blendMode );
168 }
169
170#if 0 // for debugging
171 painter->save();
172 painter->setRenderHint( QPainter::Antialiasing, false );
173 painter->translate( rect.center() );
174 painter->rotate( -angle );
175
176 painter->setBrush( QColor( 255, 0, 0, 100 ) );
177 painter->setPen( QColor( 255, 0, 0, 150 ) );
178
179 painter->drawRect( rect.width() * -0.5, rect.height() * -0.5, rect.width(), rect.height() );
180 painter->restore();
181
182 painter->setBrush( QColor( 0, 255, 0, 100 ) );
183 painter->setPen( QColor( 0, 255, 0, 150 ) );
184
185 painter->drawRect( anchor.boundingBox( ).buffered( 30 ).toRectF() );
186#endif
187
188 draw( context, rect, angle, anchor, calloutContext );
189
190 painter->setCompositionMode( QPainter::CompositionMode_SourceOver ); // just to be sure
191}
192
194{
195 mEnabled = enabled;
196}
197
199{
200 static std::once_flag initialized;
201 std::call_once( initialized, initPropertyDefinitions );
202 return sPropertyDefinitions;
203}
204
206{
207 if ( ok )
208 *ok = true;
209 const QString cleaned = name.toLower().trimmed();
210
211 if ( cleaned == "pole_of_inaccessibility"_L1 )
213 else if ( cleaned == "point_on_exterior"_L1 )
214 return PointOnExterior;
215 else if ( cleaned == "point_on_surface"_L1 )
216 return PointOnSurface;
217 else if ( cleaned == "centroid"_L1 )
218 return Centroid;
219
220 if ( ok )
221 *ok = false;
223}
224
226{
227 switch ( anchor )
228 {
230 return u"pole_of_inaccessibility"_s;
231 case PointOnExterior:
232 return u"point_on_exterior"_s;
233 case PointOnSurface:
234 return u"point_on_surface"_s;
235 case Centroid:
236 return u"centroid"_s;
237 }
238 return QString();
239}
240
242{
243 switch ( anchor )
244 {
246 return u"point_on_exterior"_s;
247 case LabelCentroid:
248 return u"centroid"_s;
249 case LabelTopLeft:
250 return u"tl"_s;
251 case LabelTopMiddle:
252 return u"t"_s;
253 case LabelTopRight:
254 return u"tr"_s;
255 case LabelMiddleLeft:
256 return u"l"_s;
257 case LabelMiddleRight:
258 return u"r"_s;
259 case LabelBottomLeft:
260 return u"bl"_s;
262 return u"b"_s;
263 case LabelBottomRight:
264 return u"br"_s;
265 }
266
267 return QString();
268}
269
271{
272 if ( ok )
273 *ok = true;
274 const QString cleaned = name.toLower().trimmed();
275
276 if ( cleaned == "point_on_exterior"_L1 )
278 else if ( cleaned == "centroid"_L1 )
279 return LabelCentroid;
280 else if ( cleaned == "tl"_L1 )
281 return LabelTopLeft;
282 else if ( cleaned == "t"_L1 )
283 return LabelTopMiddle;
284 else if ( cleaned == "tr"_L1 )
285 return LabelTopRight;
286 else if ( cleaned == "l"_L1 )
287 return LabelMiddleLeft;
288 else if ( cleaned == "r"_L1 )
289 return LabelMiddleRight;
290 else if ( cleaned == "bl"_L1 )
291 return LabelBottomLeft;
292 else if ( cleaned == "b"_L1 )
293 return LabelBottomMiddle;
294 else if ( cleaned == "br"_L1 )
295 return LabelBottomRight;
296
297 if ( ok )
298 *ok = false;
300}
301
302QgsGeometry QgsCallout::labelAnchorGeometry( const QRectF &rect, const double angle, LabelAnchorPoint anchor ) const
303{
304 QgsGeometry label;
305 switch ( anchor )
306 {
308 label = QgsGeometry::fromRect( rect );
309 break;
310
311 case LabelCentroid:
312 label = QgsGeometry::fromRect( rect ).centroid();
313 break;
314
315 case LabelTopLeft:
316 label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomLeft() ) );
317 break;
318
319 case LabelTopMiddle:
320 label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.bottom() ) );
321 break;
322
323 case LabelTopRight:
324 label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomRight() ) );
325 break;
326
327 case LabelMiddleLeft:
328 label = QgsGeometry::fromPointXY( QgsPointXY( rect.left(), ( rect.top() + rect.bottom() ) / 2.0 ) );
329 break;
330
331 case LabelMiddleRight:
332 label = QgsGeometry::fromPointXY( QgsPointXY( rect.right(), ( rect.top() + rect.bottom() ) / 2.0 ) );
333 break;
334
335 case LabelBottomLeft:
336 label = QgsGeometry::fromPointXY( QgsPointXY( rect.topLeft() ) );
337 break;
338
340 label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.top() ) );
341 break;
342
343 case LabelBottomRight:
344 label = QgsGeometry::fromPointXY( QgsPointXY( rect.topRight() ) );
345 break;
346 }
347
348 label.rotate( angle, rect.topLeft() );
349 return label;
350}
351
352QgsGeometry QgsCallout::calloutLabelPoint( const QRectF &rect, const double angle, QgsCallout::LabelAnchorPoint anchor, QgsRenderContext &context, const QgsCallout::QgsCalloutContext &calloutContext, bool &pinned ) const
353{
354 pinned = false;
356 {
357 bool ok = false;
359 if ( ok )
360 {
362 if ( ok )
363 {
364 pinned = true;
365 // data defined label point, use it directly
366 QgsGeometry labelPoint = QgsGeometry::fromPointXY( QgsPointXY( x, y ) );
367 try
368 {
369 labelPoint.transform( calloutContext.originalFeatureToMapTransform( context ) );
370 labelPoint.transform( context.mapToPixel().transform() );
371 }
372 catch ( QgsCsException & )
373 {
374 return QgsGeometry();
375 }
376 return labelPoint;
377 }
378 }
379 }
380
381 QgsGeometry label;
382 switch ( anchor )
383 {
385 label = QgsGeometry::fromRect( rect );
386 break;
387
388 case LabelCentroid:
389 label = QgsGeometry::fromRect( rect ).centroid();
390 break;
391
392 case LabelTopLeft:
393 label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomLeft() ) );
394 break;
395
396 case LabelTopMiddle:
397 label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.bottom() ) );
398 break;
399
400 case LabelTopRight:
401 label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomRight() ) );
402 break;
403
404 case LabelMiddleLeft:
405 label = QgsGeometry::fromPointXY( QgsPointXY( rect.left(), ( rect.top() + rect.bottom() ) / 2.0 ) );
406 break;
407
408 case LabelMiddleRight:
409 label = QgsGeometry::fromPointXY( QgsPointXY( rect.right(), ( rect.top() + rect.bottom() ) / 2.0 ) );
410 break;
411
412 case LabelBottomLeft:
413 label = QgsGeometry::fromPointXY( QgsPointXY( rect.topLeft() ) );
414 break;
415
417 label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.top() ) );
418 break;
419
420 case LabelBottomRight:
421 label = QgsGeometry::fromPointXY( QgsPointXY( rect.topRight() ) );
422 break;
423 }
424
425 label.rotate( angle, rect.topLeft() );
426 return label;
427}
428
429QgsGeometry QgsCallout::calloutLineToPart( const QgsGeometry &labelGeometry, const QgsAbstractGeometry *partGeometry, QgsRenderContext &context, const QgsCalloutContext &calloutContext, bool &pinned ) const
430{
431 pinned = false;
432 AnchorPoint anchor = anchorPoint();
433 const QgsAbstractGeometry *evaluatedPartAnchor = partGeometry;
434 std::unique_ptr< QgsAbstractGeometry > tempPartAnchor;
435
437 {
438 bool ok = false;
440 if ( ok )
441 {
443 if ( ok )
444 {
445 pinned = true;
446 tempPartAnchor = std::make_unique< QgsPoint >( Qgis::WkbType::Point, x, y );
447 evaluatedPartAnchor = tempPartAnchor.get();
448 try
449 {
450 tempPartAnchor->transform( calloutContext.originalFeatureToMapTransform( context ) );
451 tempPartAnchor->transform( context.mapToPixel().transform() );
452 }
453 catch ( QgsCsException & )
454 {
455 evaluatedPartAnchor = partGeometry;
456 }
457 }
458 }
459 }
460
462 {
463 const QString encodedAnchor = encodeAnchorPoint( anchor );
464 context.expressionContext().setOriginalValueVariable( encodedAnchor );
465 anchor = decodeAnchorPoint( dataDefinedProperties().valueAsString( QgsCallout::Property::AnchorPointPosition, context.expressionContext(), encodedAnchor ) );
466 }
467
468 QgsGeometry line;
469 const QgsGeos labelGeos( labelGeometry.constGet() );
470
471 switch ( QgsWkbTypes::geometryType( evaluatedPartAnchor->wkbType() ) )
472 {
475 {
476 line = QgsGeometry( labelGeos.shortestLine( evaluatedPartAnchor ) );
477 break;
478 }
479
481 {
482 if ( labelGeos.intersects( evaluatedPartAnchor ) )
483 return QgsGeometry();
484
485 // ideally avoid this unwanted clone in future. For now we need it because poleOfInaccessibility/pointOnSurface are
486 // only available to QgsGeometry objects
487 const QgsGeometry evaluatedPartAnchorGeom( evaluatedPartAnchor->clone() );
488 switch ( anchor )
489 {
491 line = QgsGeometry( labelGeos.shortestLine( evaluatedPartAnchorGeom.poleOfInaccessibility( std::max( evaluatedPartAnchor->boundingBox().width(), evaluatedPartAnchor->boundingBox().height() ) / 20.0 ) ) ); // really rough (but quick) pole of inaccessibility
492 break;
494 line = QgsGeometry( labelGeos.shortestLine( evaluatedPartAnchorGeom.pointOnSurface() ) );
495 break;
497 line = QgsGeometry( labelGeos.shortestLine( evaluatedPartAnchor ) );
498 break;
500 line = QgsGeometry( labelGeos.shortestLine( evaluatedPartAnchorGeom.centroid() ) );
501 break;
502 }
503 break;
504 }
505
508 return QgsGeometry(); // shouldn't even get here..
509 }
510 return line;
511}
512
513//
514// QgsCallout::QgsCalloutContext
515//
516
518{
519 if ( !mOriginalFeatureToMapTransform.isValid() )
520 {
521 // lazy initialization, only create if needed...
522 mOriginalFeatureToMapTransform = QgsCoordinateTransform( originalFeatureCrs, renderContext.coordinateTransform().destinationCrs(), renderContext.transformContext() );
523 }
524 return mOriginalFeatureToMapTransform;
525}
526
527
528//
529// QgsSimpleLineCallout
530//
531
533{
534 mLineSymbol = std::make_unique< QgsLineSymbol >( QgsSymbolLayerList() << new QgsSimpleLineSymbolLayer( QColor( 60, 60, 60 ), .3 ) );
535
536}
537
539
541 : QgsCallout( other )
542 , mLineSymbol( other.mLineSymbol ? other.mLineSymbol->clone() : nullptr )
543 , mMinCalloutLength( other.mMinCalloutLength )
544 , mMinCalloutLengthUnit( other.mMinCalloutLengthUnit )
545 , mMinCalloutLengthScale( other.mMinCalloutLengthScale )
546 , mOffsetFromAnchorDistance( other.mOffsetFromAnchorDistance )
547 , mOffsetFromAnchorUnit( other.mOffsetFromAnchorUnit )
548 , mOffsetFromAnchorScale( other.mOffsetFromAnchorScale )
549 , mOffsetFromLabelDistance( other.mOffsetFromLabelDistance )
550 , mOffsetFromLabelUnit( other.mOffsetFromLabelUnit )
551 , mOffsetFromLabelScale( other.mOffsetFromLabelScale )
552 , mDrawCalloutToAllParts( other.mDrawCalloutToAllParts )
553{
554
555}
556
558{
559 auto callout = std::make_unique< QgsSimpleLineCallout >();
560 callout->readProperties( properties, context );
561 return callout.release();
562}
563
565{
566 return u"simple"_s;
567}
568
573
574QVariantMap QgsSimpleLineCallout::properties( const QgsReadWriteContext &context ) const
575{
576 QVariantMap props = QgsCallout::properties( context );
577
578 if ( mLineSymbol )
579 {
580 props[ u"lineSymbol"_s ] = QgsSymbolLayerUtils::symbolProperties( mLineSymbol.get() );
581 }
582 props[ u"minLength"_s ] = mMinCalloutLength;
583 props[ u"minLengthUnit"_s ] = QgsUnitTypes::encodeUnit( mMinCalloutLengthUnit );
584 props[ u"minLengthMapUnitScale"_s ] = QgsSymbolLayerUtils::encodeMapUnitScale( mMinCalloutLengthScale );
585
586 props[ u"offsetFromAnchor"_s ] = mOffsetFromAnchorDistance;
587 props[ u"offsetFromAnchorUnit"_s ] = QgsUnitTypes::encodeUnit( mOffsetFromAnchorUnit );
588 props[ u"offsetFromAnchorMapUnitScale"_s ] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetFromAnchorScale );
589 props[ u"offsetFromLabel"_s ] = mOffsetFromLabelDistance;
590 props[ u"offsetFromLabelUnit"_s ] = QgsUnitTypes::encodeUnit( mOffsetFromLabelUnit );
591 props[ u"offsetFromLabelMapUnitScale"_s ] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetFromLabelScale );
592
593 props[ u"drawToAllParts"_s ] = mDrawCalloutToAllParts;
594
595 return props;
596}
597
598void QgsSimpleLineCallout::readProperties( const QVariantMap &props, const QgsReadWriteContext &context )
599{
600 QgsCallout::readProperties( props, context );
601
602 const QString lineSymbolDef = props.value( u"lineSymbol"_s ).toString();
603 QDomDocument doc( u"symbol"_s );
604 doc.setContent( lineSymbolDef );
605 const QDomElement symbolElem = doc.firstChildElement( u"symbol"_s );
606 std::unique_ptr< QgsLineSymbol > lineSymbol( QgsSymbolLayerUtils::loadSymbol< QgsLineSymbol >( symbolElem, context ) );
607 if ( lineSymbol )
608 mLineSymbol = std::move( lineSymbol );
609
610 mMinCalloutLength = props.value( u"minLength"_s, 0 ).toDouble();
611 mMinCalloutLengthUnit = QgsUnitTypes::decodeRenderUnit( props.value( u"minLengthUnit"_s ).toString() );
612 mMinCalloutLengthScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( u"minLengthMapUnitScale"_s ).toString() );
613
614 mOffsetFromAnchorDistance = props.value( u"offsetFromAnchor"_s, 0 ).toDouble();
615 mOffsetFromAnchorUnit = QgsUnitTypes::decodeRenderUnit( props.value( u"offsetFromAnchorUnit"_s ).toString() );
616 mOffsetFromAnchorScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( u"offsetFromAnchorMapUnitScale"_s ).toString() );
617 mOffsetFromLabelDistance = props.value( u"offsetFromLabel"_s, 0 ).toDouble();
618 mOffsetFromLabelUnit = QgsUnitTypes::decodeRenderUnit( props.value( u"offsetFromLabelUnit"_s ).toString() );
619 mOffsetFromLabelScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( u"offsetFromLabelMapUnitScale"_s ).toString() );
620
621 mDrawCalloutToAllParts = props.value( u"drawToAllParts"_s, false ).toBool();
622}
623
625{
626 QgsCallout::startRender( context );
627 if ( mLineSymbol )
628 mLineSymbol->startRender( context );
629}
630
632{
633 QgsCallout::stopRender( context );
634 if ( mLineSymbol )
635 mLineSymbol->stopRender( context );
636}
637
638QSet<QString> QgsSimpleLineCallout::referencedFields( const QgsRenderContext &context ) const
639{
640 QSet<QString> fields = QgsCallout::referencedFields( context );
641 if ( mLineSymbol )
642 fields.unite( mLineSymbol->usedAttributes( context ) );
643 return fields;
644}
645
647{
648 return mLineSymbol.get();
649}
650
652{
653 mLineSymbol.reset( symbol );
654}
655
656void QgsSimpleLineCallout::draw( QgsRenderContext &context, const QRectF &rect, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext )
657{
658 LabelAnchorPoint labelAnchor = labelAnchorPoint();
660 {
661 const QString encodedAnchor = encodeLabelAnchorPoint( labelAnchor );
662 context.expressionContext().setOriginalValueVariable( encodedAnchor );
663 labelAnchor = decodeLabelAnchorPoint( dataDefinedProperties().valueAsString( QgsCallout::Property::LabelAnchorPointPosition, context.expressionContext(), encodedAnchor ) );
664 }
665
666 bool originPinned = false;
667 const QgsGeometry label = calloutLabelPoint( rect, angle, labelAnchor, context, calloutContext, originPinned );
668 if ( label.isNull() )
669 return;
670
671 auto drawCalloutLine = [this, &context, &calloutContext, &label, &rect, angle, &anchor, originPinned]( const QgsAbstractGeometry * partAnchor )
672 {
673 bool destinationPinned = false;
674 const QgsGeometry line = calloutLineToPart( label, partAnchor, context, calloutContext, destinationPinned );
675 if ( line.isEmpty() )
676 return;
677
678 const double lineLength = line.length();
679 if ( qgsDoubleNear( lineLength, 0 ) )
680 return;
681
682 double minLength = mMinCalloutLength;
684 {
685 context.expressionContext().setOriginalValueVariable( minLength );
687 }
688 const double minLengthPixels = context.convertToPainterUnits( minLength, mMinCalloutLengthUnit, mMinCalloutLengthScale );
689 if ( minLengthPixels > 0 && lineLength < minLengthPixels )
690 return; // too small!
691
692 std::unique_ptr< QgsCurve > calloutCurve( createCalloutLine( qgsgeometry_cast< const QgsLineString * >( line.constGet() )->startPoint(),
693 qgsgeometry_cast< const QgsLineString * >( line.constGet() )->endPoint(), context, rect, angle, anchor, calloutContext ) );
694
695 double offsetFromAnchor = mOffsetFromAnchorDistance;
697 {
700 }
701 const double offsetFromAnchorPixels = context.convertToPainterUnits( offsetFromAnchor, mOffsetFromAnchorUnit, mOffsetFromAnchorScale );
702
703 double offsetFromLabel = mOffsetFromLabelDistance;
705 {
708 }
709 const double offsetFromLabelPixels = context.convertToPainterUnits( offsetFromLabel, mOffsetFromLabelUnit, mOffsetFromLabelScale );
710 if ( offsetFromAnchorPixels > 0 || offsetFromLabelPixels > 0 )
711 {
712 calloutCurve.reset( calloutCurve->curveSubstring( offsetFromLabelPixels, calloutCurve->length() - offsetFromAnchorPixels ) );
713 }
714
715 const QPolygonF points = calloutCurve->asQPolygonF();
716
717 if ( points.empty() )
718 return;
719
720 QgsCalloutPosition position;
721 position.setOrigin( context.mapToPixel().toMapCoordinates( points.at( 0 ).x(), points.at( 0 ).y() ).toQPointF() );
722 position.setOriginIsPinned( originPinned );
723 position.setDestination( context.mapToPixel().toMapCoordinates( points.constLast().x(), points.constLast().y() ).toQPointF() );
724 position.setDestinationIsPinned( destinationPinned );
725 calloutContext.addCalloutPosition( position );
726
727 mLineSymbol->renderPolyline( points, nullptr, context );
728 };
729
730 bool toAllParts = mDrawCalloutToAllParts;
732 {
733 context.expressionContext().setOriginalValueVariable( toAllParts );
735 }
736
737 if ( calloutContext.allFeaturePartsLabeled || !toAllParts )
738 drawCalloutLine( anchor.constGet() );
739 else
740 {
741 for ( auto it = anchor.const_parts_begin(); it != anchor.const_parts_end(); ++it )
742 drawCalloutLine( *it );
743 }
744}
745
746QgsCurve *QgsSimpleLineCallout::createCalloutLine( const QgsPoint &start, const QgsPoint &end, QgsRenderContext &, const QRectF &, const double, const QgsGeometry &, QgsCallout::QgsCalloutContext & ) const
747{
748 return new QgsLineString( start, end );
749}
750
751//
752// QgsManhattanLineCallout
753//
754
758
764
765
766QgsCallout *QgsManhattanLineCallout::create( const QVariantMap &properties, const QgsReadWriteContext &context ) // cppcheck-suppress duplInheritedMember
767{
768 auto callout = std::make_unique< QgsManhattanLineCallout >();
769 callout->readProperties( properties, context );
770 return callout.release();
771}
772
774{
775 return u"manhattan"_s;
776}
777
782
783QgsCurve *QgsManhattanLineCallout::createCalloutLine( const QgsPoint &start, const QgsPoint &end, QgsRenderContext &, const QRectF &, const double, const QgsGeometry &, QgsCallout::QgsCalloutContext & ) const
784{
785 const QgsPoint mid1 = QgsPoint( start.x(), end.y() );
786 return new QgsLineString( QVector< QgsPoint >() << start << mid1 << end );
787}
788
789
790//
791// QgsCurvedLineCallout
792//
793
797
799 : QgsSimpleLineCallout( other )
800 , mOrientation( other.mOrientation )
801 , mCurvature( other.mCurvature )
802{
803
804}
805
806QgsCallout *QgsCurvedLineCallout::create( const QVariantMap &properties, const QgsReadWriteContext &context ) // cppcheck-suppress duplInheritedMember
807{
808 auto callout = std::make_unique< QgsCurvedLineCallout >();
809 callout->readProperties( properties, context );
810
811 callout->setCurvature( properties.value( u"curvature"_s, 0.1 ).toDouble() );
812 callout->setOrientation( decodeOrientation( properties.value( u"orientation"_s, u"auto"_s ).toString() ) );
813
814 return callout.release();
815}
816
818{
819 return u"curved"_s;
820}
821
826
827QVariantMap QgsCurvedLineCallout::properties( const QgsReadWriteContext &context ) const
828{
829 QVariantMap props = QgsSimpleLineCallout::properties( context );
830 props.insert( u"curvature"_s, mCurvature );
831 props.insert( u"orientation"_s, encodeOrientation( mOrientation ) );
832 return props;
833}
834
835QgsCurve *QgsCurvedLineCallout::createCalloutLine( const QgsPoint &start, const QgsPoint &end, QgsRenderContext &context, const QRectF &rect, const double, const QgsGeometry &, QgsCallout::QgsCalloutContext & ) const
836{
837 double curvature = mCurvature * 100;
839 {
842 }
843
844 Orientation orientation = mOrientation;
846 {
847 bool ok = false;
848 const QString orientationString = dataDefinedProperties().property( QgsCallout::Property::Orientation ).valueAsString( context.expressionContext(), QString(), &ok );
849 if ( ok )
850 {
851 orientation = decodeOrientation( orientationString );
852 }
853 }
854
855 if ( orientation == Automatic )
856 {
857 // to calculate automatically the best curve orientation, we first check which side of the label bounding box
858 // the callout origin is nearest to
859 switch ( QgsGeometryUtilsBase::closestSideOfRectangle( rect.right(), rect.bottom(), rect.left(), rect.top(), start.x(), start.y() ) )
860 {
861 case 1:
862 // closest to bottom
863 if ( qgsDoubleNear( end.x(), start.x() ) )
864 {
865 // if vertical line, we bend depending on whether the line sits towards the left or right side of the label
866 if ( start.x() < ( rect.left() + 0.5 * rect.width() ) )
868 else
870 }
871 else if ( end.x() > start.x() )
873 else
875 break;
876
877 case 2:
878 // closest to bottom-right
879 if ( end.x() < start.x() )
881 else if ( end.y() < start.y() )
883 else if ( end.x() - start.x() < end.y() - start.y() )
885 else
887 break;
888
889 case 3:
890 // closest to right
891 if ( qgsDoubleNear( end.y(), start.y() ) )
892 {
893 // if horizontal line, we bend depending on whether the line sits towards the top or bottom side of the label
894 if ( start.y() < ( rect.top() + 0.5 * rect.height() ) )
896 else
898 }
899 else if ( end.y() < start.y() )
901 else
903 break;
904
905 case 4:
906 // closest to top-right
907 if ( end.x() < start.x() )
909 else if ( end.y() > start.y() )
911 else if ( end.x() - start.x() < start.y() - end.y() )
913 else
915 break;
916
917 case 5:
918 // closest to top
919 if ( qgsDoubleNear( end.x(), start.x() ) )
920 {
921 // if vertical line, we bend depending on whether the line sits towards the left or right side of the label
922 if ( start.x() < ( rect.left() + 0.5 * rect.width() ) )
924 else
926 }
927 else if ( end.x() < start.x() )
929 else
931 break;
932
933 case 6:
934 // closest to top-left
935 if ( end.x() > start.x() )
937 else if ( end.y() > start.y() )
939 else if ( start.x() - end.x() < start.y() - end.y() )
941 else
943 break;
944
945 case 7:
946 //closest to left
947 if ( qgsDoubleNear( end.y(), start.y() ) )
948 {
949 // if horizontal line, we bend depending on whether the line sits towards the top or bottom side of the label
950 if ( start.y() < ( rect.top() + 0.5 * rect.height() ) )
952 else
954 }
955 else if ( end.y() > start.y() )
957 else
959 break;
960
961 case 8:
962 //closest to bottom-left
963 if ( end.x() > start.x() )
965 else if ( end.y() < start.y() )
967 else if ( start.x() - end.x() < end.y() - start.y() )
969 else
971 break;
972 }
973 }
974
975 // turn the line into a curved line. We do this by creating a circular string from the callout line's
976 // start to end point, where the curve point is in the middle of the callout line and perpendicularly offset
977 // by a proportion of the overall callout line length
978 const double distance = ( orientation == Clockwise ? 1 : -1 ) * start.distance( end ) * curvature / 100.0;
979 double midX, midY;
980 QgsGeometryUtilsBase::perpendicularOffsetPointAlongSegment( start.x(), start.y(), end.x(), end.y(), 0.5, distance, &midX, &midY );
981
982 return new QgsCircularString( start, QgsPoint( midX, midY ), end );
983}
984
985QgsCurvedLineCallout::Orientation QgsCurvedLineCallout::decodeOrientation( const QString &string )
986{
987 const QString cleaned = string.toLower().trimmed();
988 if ( cleaned == "auto"_L1 )
989 return Automatic;
990 if ( cleaned == "clockwise"_L1 )
991 return Clockwise;
992 if ( cleaned == "counterclockwise"_L1 )
993 return CounterClockwise;
994 return Automatic;
995}
996
997QString QgsCurvedLineCallout::encodeOrientation( QgsCurvedLineCallout::Orientation orientation )
998{
999 switch ( orientation )
1000 {
1002 return u"auto"_s;
1004 return u"clockwise"_s;
1006 return u"counterclockwise"_s;
1007 }
1008 return QString();
1009}
1010
1012{
1013 return mOrientation;
1014}
1015
1020
1022{
1023 return mCurvature;
1024}
1025
1027{
1028 mCurvature = curvature;
1029}
1030
1031
1032
1033//
1034// QgsBalloonCallout
1035//
1036
1038{
1039 mFillSymbol = std::make_unique< QgsFillSymbol >( QgsSymbolLayerList() << new QgsSimpleFillSymbolLayer( QColor( 255, 200, 60 ) ) );
1040}
1041
1043
1045 : QgsCallout( other )
1046 , mFillSymbol( other.mFillSymbol ? other.mFillSymbol->clone() : nullptr )
1047 , mMarkerSymbol( other.mMarkerSymbol ? other.mMarkerSymbol->clone() : nullptr )
1048 , mOffsetFromAnchorDistance( other.mOffsetFromAnchorDistance )
1049 , mOffsetFromAnchorUnit( other.mOffsetFromAnchorUnit )
1050 , mOffsetFromAnchorScale( other.mOffsetFromAnchorScale )
1051 , mMargins( other.mMargins )
1052 , mMarginUnit( other.mMarginUnit )
1053 , mWedgeWidth( other.mWedgeWidth )
1054 , mWedgeWidthUnit( other.mWedgeWidthUnit )
1055 , mWedgeWidthScale( other.mWedgeWidthScale )
1056 , mCornerRadius( other.mCornerRadius )
1057 , mCornerRadiusUnit( other.mCornerRadiusUnit )
1058 , mCornerRadiusScale( other.mCornerRadiusScale )
1059{
1060
1061}
1062
1064{
1065 auto callout = std::make_unique< QgsBalloonCallout >();
1066 callout->readProperties( properties, context );
1067 return callout.release();
1068}
1069
1071{
1072 return u"balloon"_s;
1073}
1074
1076{
1077 return new QgsBalloonCallout( *this );
1078}
1079
1080QVariantMap QgsBalloonCallout::properties( const QgsReadWriteContext &context ) const
1081{
1082 QVariantMap props = QgsCallout::properties( context );
1083
1084 if ( mFillSymbol )
1085 {
1086 props[ u"fillSymbol"_s ] = QgsSymbolLayerUtils::symbolProperties( mFillSymbol.get() );
1087 }
1088
1089 if ( mMarkerSymbol )
1090 {
1091 props[ u"markerSymbol"_s ] = QgsSymbolLayerUtils::symbolProperties( mMarkerSymbol.get() );
1092 }
1093
1094 props[ u"offsetFromAnchor"_s ] = mOffsetFromAnchorDistance;
1095 props[ u"offsetFromAnchorUnit"_s ] = QgsUnitTypes::encodeUnit( mOffsetFromAnchorUnit );
1096 props[ u"offsetFromAnchorMapUnitScale"_s ] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetFromAnchorScale );
1097
1098 props[ u"margins"_s ] = mMargins.toString();
1099 props[ u"marginsUnit"_s ] = QgsUnitTypes::encodeUnit( mMarginUnit );
1100
1101 props[ u"wedgeWidth"_s ] = mWedgeWidth;
1102 props[ u"wedgeWidthUnit"_s ] = QgsUnitTypes::encodeUnit( mWedgeWidthUnit );
1103 props[ u"wedgeWidthMapUnitScale"_s ] = QgsSymbolLayerUtils::encodeMapUnitScale( mWedgeWidthScale );
1104
1105 props[ u"cornerRadius"_s ] = mCornerRadius;
1106 props[ u"cornerRadiusUnit"_s ] = QgsUnitTypes::encodeUnit( mCornerRadiusUnit );
1107 props[ u"cornerRadiusMapUnitScale"_s ] = QgsSymbolLayerUtils::encodeMapUnitScale( mCornerRadiusScale );
1108
1109 return props;
1110}
1111
1112void QgsBalloonCallout::readProperties( const QVariantMap &props, const QgsReadWriteContext &context )
1113{
1114 QgsCallout::readProperties( props, context );
1115
1116 {
1117 const QString fillSymbolDef = props.value( u"fillSymbol"_s ).toString();
1118 QDomDocument doc( u"symbol"_s );
1119 doc.setContent( fillSymbolDef );
1120 const QDomElement symbolElem = doc.firstChildElement( u"symbol"_s );
1121 std::unique_ptr< QgsFillSymbol > fillSymbol( QgsSymbolLayerUtils::loadSymbol< QgsFillSymbol >( symbolElem, context ) );
1122 if ( fillSymbol )
1123 mFillSymbol = std::move( fillSymbol );
1124 }
1125
1126 {
1127 const QString markerSymbolDef = props.value( u"markerSymbol"_s ).toString();
1128 QDomDocument doc( u"symbol"_s );
1129 doc.setContent( markerSymbolDef );
1130 const QDomElement symbolElem = doc.firstChildElement( u"symbol"_s );
1131 std::unique_ptr< QgsMarkerSymbol > markerSymbol( QgsSymbolLayerUtils::loadSymbol< QgsMarkerSymbol >( symbolElem, context ) );
1132 if ( markerSymbol )
1133 mMarkerSymbol = std::move( markerSymbol );
1134 }
1135
1136 mOffsetFromAnchorDistance = props.value( u"offsetFromAnchor"_s, 0 ).toDouble();
1137 mOffsetFromAnchorUnit = QgsUnitTypes::decodeRenderUnit( props.value( u"offsetFromAnchorUnit"_s ).toString() );
1138 mOffsetFromAnchorScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( u"offsetFromAnchorMapUnitScale"_s ).toString() );
1139
1140 mMargins = QgsMargins::fromString( props.value( u"margins"_s ).toString() );
1141 mMarginUnit = QgsUnitTypes::decodeRenderUnit( props.value( u"marginsUnit"_s ).toString() );
1142
1143 mWedgeWidth = props.value( u"wedgeWidth"_s, 2.64 ).toDouble();
1144 mWedgeWidthUnit = QgsUnitTypes::decodeRenderUnit( props.value( u"wedgeWidthUnit"_s ).toString() );
1145 mWedgeWidthScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( u"wedgeWidthMapUnitScale"_s ).toString() );
1146
1147 mCornerRadius = props.value( u"cornerRadius"_s, 0 ).toDouble();
1148 mCornerRadiusUnit = QgsUnitTypes::decodeRenderUnit( props.value( u"cornerRadiusUnit"_s ).toString() );
1149 mCornerRadiusScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( u"cornerRadiusMapUnitScale"_s ).toString() );
1150}
1151
1153{
1154 QgsCallout::startRender( context );
1155 if ( mFillSymbol )
1156 mFillSymbol->startRender( context );
1157 if ( mMarkerSymbol )
1158 mMarkerSymbol->startRender( context );
1159}
1160
1162{
1163 QgsCallout::stopRender( context );
1164 if ( mFillSymbol )
1165 mFillSymbol->stopRender( context );
1166 if ( mMarkerSymbol )
1167 mMarkerSymbol->stopRender( context );
1168}
1169
1170QSet<QString> QgsBalloonCallout::referencedFields( const QgsRenderContext &context ) const
1171{
1172 QSet<QString> fields = QgsCallout::referencedFields( context );
1173 if ( mFillSymbol )
1174 fields.unite( mFillSymbol->usedAttributes( context ) );
1175 if ( mMarkerSymbol )
1176 fields.unite( mMarkerSymbol->usedAttributes( context ) );
1177 return fields;
1178}
1179
1181{
1182 return mFillSymbol.get();
1183}
1184
1186{
1187 mFillSymbol.reset( symbol );
1188}
1189
1191{
1192 return mMarkerSymbol.get();
1193}
1194
1196{
1197 mMarkerSymbol.reset( symbol );
1198}
1199
1200void QgsBalloonCallout::draw( QgsRenderContext &context, const QRectF &rect, const double, const QgsGeometry &anchor, QgsCalloutContext &calloutContext )
1201{
1202 bool destinationIsPinned = false;
1203 QgsGeometry line = calloutLineToPart( QgsGeometry::fromRect( rect ), anchor.constGet(), context, calloutContext, destinationIsPinned );
1204
1205 if ( mMarkerSymbol )
1206 {
1208 {
1209 QgsPoint anchorPoint = ls->endPoint();
1210 mMarkerSymbol->renderPoint( anchorPoint.toQPointF(), nullptr, context );
1211 }
1212 }
1213
1214 double offsetFromAnchor = mOffsetFromAnchorDistance;
1216 {
1219 }
1220 const double offsetFromAnchorPixels = context.convertToPainterUnits( offsetFromAnchor, mOffsetFromAnchorUnit, mOffsetFromAnchorScale );
1221
1222 if ( offsetFromAnchorPixels > 0 )
1223 {
1225 {
1226 line = QgsGeometry( ls->curveSubstring( 0, ls->length() - offsetFromAnchorPixels ) );
1227 }
1228 }
1229
1230 QgsPointXY destination;
1231 QgsPointXY origin;
1233 {
1234 origin = ls->startPoint();
1235 destination = ls->endPoint();
1236 }
1237 else
1238 {
1239 destination = QgsPointXY( rect.center() );
1240 }
1241
1242 const QPolygonF points = getPoints( context, destination, rect );
1243 if ( points.empty() )
1244 return;
1245
1246 if ( !origin.isEmpty() )
1247 {
1248 QgsCalloutPosition position;
1249 position.setOrigin( context.mapToPixel().toMapCoordinates( origin.x(), origin.y() ).toQPointF() );
1250 position.setOriginIsPinned( false );
1251 position.setDestination( context.mapToPixel().toMapCoordinates( destination.x(), destination.y() ).toQPointF() );
1252 position.setDestinationIsPinned( destinationIsPinned );
1253 calloutContext.addCalloutPosition( position );
1254 }
1255
1256 mFillSymbol->renderPolygon( points, nullptr, nullptr, context );
1257}
1258
1259QPolygonF QgsBalloonCallout::getPoints( QgsRenderContext &context, QgsPointXY origin, QRectF rect ) const
1260{
1261 double segmentPointWidth = mWedgeWidth;
1263 {
1264 context.expressionContext().setOriginalValueVariable( segmentPointWidth );
1265 segmentPointWidth = dataDefinedProperties().valueAsDouble( QgsCallout::Property::WedgeWidth, context.expressionContext(), segmentPointWidth );
1266 }
1267 segmentPointWidth = context.convertToPainterUnits( segmentPointWidth, mWedgeWidthUnit, mWedgeWidthScale );
1268
1269 double cornerRadius = mCornerRadius;
1271 {
1274 }
1275 cornerRadius = context.convertToPainterUnits( cornerRadius, mCornerRadiusUnit, mCornerRadiusScale );
1276
1277 double left = mMargins.left();
1278 double right = mMargins.right();
1279 double top = mMargins.top();
1280 double bottom = mMargins.bottom();
1281
1283 {
1284 const QVariant value = dataDefinedProperties().value( QgsCallout::Property::Margins, context.expressionContext() );
1285 if ( !QgsVariantUtils::isNull( value ) )
1286 {
1287 if ( value.userType() == QMetaType::Type::QVariantList )
1288 {
1289 const QVariantList list = value.toList();
1290 if ( list.size() == 4 )
1291 {
1292 bool topOk = false;
1293 bool rightOk = false;
1294 bool bottomOk = false;
1295 bool leftOk = false;
1296 const double evaluatedTop = list.at( 0 ).toDouble( &topOk );
1297 const double evaluatedRight = list.at( 1 ).toDouble( &rightOk );
1298 const double evaluatedBottom = list.at( 2 ).toDouble( &bottomOk );
1299 const double evaluatedLeft = list.at( 3 ).toDouble( &leftOk );
1300 if ( topOk && rightOk && bottomOk && leftOk )
1301 {
1302 left = evaluatedLeft;
1303 top = evaluatedTop;
1304 right = evaluatedRight;
1305 bottom = evaluatedBottom;
1306 }
1307 }
1308 }
1309 else
1310 {
1311 const QStringList list = value.toString().trimmed().split( ',' );
1312 if ( list.count() == 4 )
1313 {
1314 bool topOk = false;
1315 bool rightOk = false;
1316 bool bottomOk = false;
1317 bool leftOk = false;
1318 const double evaluatedTop = list.at( 0 ).toDouble( &topOk );
1319 const double evaluatedRight = list.at( 1 ).toDouble( &rightOk );
1320 const double evaluatedBottom = list.at( 2 ).toDouble( &bottomOk );
1321 const double evaluatedLeft = list.at( 3 ).toDouble( &leftOk );
1322 if ( topOk && rightOk && bottomOk && leftOk )
1323 {
1324 left = evaluatedLeft;
1325 top = evaluatedTop;
1326 right = evaluatedRight;
1327 bottom = evaluatedBottom;
1328 }
1329 }
1330 }
1331 }
1332 }
1333
1334 const double marginLeft = context.convertToPainterUnits( left, mMarginUnit );
1335 const double marginRight = context.convertToPainterUnits( right, mMarginUnit );
1336 const double marginTop = context.convertToPainterUnits( top, mMarginUnit );
1337 const double marginBottom = context.convertToPainterUnits( bottom, mMarginUnit );
1338
1339 const QRectF expandedRect = rect.height() < 0 ?
1340 QRectF( rect.left() - marginLeft, rect.top() + marginBottom,
1341 rect.width() + marginLeft + marginRight,
1342 rect.height() - marginTop - marginBottom ) :
1343 QRectF( rect.left() - marginLeft, rect.top() - marginTop,
1344 rect.width() + marginLeft + marginRight,
1345 rect.height() + marginTop + marginBottom );
1346
1347 // IMPORTANT -- check for degenerate height is sometimes >=0, because QRectF are not normalized and we are using painter
1348 // coordinates with descending vertical axis!
1349 if ( expandedRect.width() <= 0 || ( rect.height() < 0 && expandedRect.height() >= 0 ) || ( rect.height() > 0 && expandedRect.height() <= 0 ) )
1350 return QPolygonF();
1351
1352 const QPainterPath path = QgsShapeGenerator::createBalloon( origin, expandedRect, segmentPointWidth, cornerRadius );
1353 const QTransform t = QTransform::fromScale( 100, 100 );
1354 const QTransform ti = t.inverted();
1355 const QPolygonF poly = path.toFillPolygon( t );
1356 return ti.map( poly );
1357}
@ ForceVector
Always force vector-based rendering, even when the result will be visually different to a raster-base...
Definition qgis.h:2764
BlendMode
Blending modes defining the available composition modes that can be used when painting.
Definition qgis.h:5002
@ Normal
Normal.
Definition qgis.h:5003
@ Point
Points.
Definition qgis.h:366
@ Line
Lines.
Definition qgis.h:367
@ Polygon
Polygons.
Definition qgis.h:368
@ Unknown
Unknown types.
Definition qgis.h:369
@ Null
No geometry.
Definition qgis.h:370
@ Point
Point.
Definition qgis.h:282
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.
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.
static QgsCallout * create(const QVariantMap &properties=QVariantMap(), const QgsReadWriteContext &context=QgsReadWriteContext())
Creates a new QgsBalloonCallout, using the settings serialized in the properties map (corresponding t...
double offsetFromAnchor() const
Returns the offset distance from the anchor point at which to start the line.
Definition qgscallout.h:956
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:250
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:280
bool allFeaturePartsLabeled
true if all parts of associated feature were labeled
Definition qgscallout.h:253
QgsCoordinateReferenceSystem originalFeatureCrs
Contains the CRS of the original feature associated with this callout.
Definition qgscallout.h:260
Abstract base class for callout renderers.
Definition qgscallout.h:55
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:335
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:365
LabelAnchorPoint labelAnchorPoint() const
Returns the label's anchor point position.
Definition qgscallout.h:398
AnchorPoint
Feature's anchor point position.
Definition qgscallout.h:116
@ PointOnExterior
A point on the surface's outline closest to the label is used as anchor for polygon geometries.
Definition qgscallout.h:118
@ Centroid
The surface's centroid is used as anchor for polygon geometries.
Definition qgscallout.h:120
@ PointOnSurface
A point guaranteed to be on the surface is used as anchor for polygon geometries.
Definition qgscallout.h:119
@ PoleOfInaccessibility
The surface's pole of inaccessibility used as anchor for polygon geometries.
Definition qgscallout.h:117
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:109
@ OrderBelowAllLabels
Render callouts below all labels.
Definition qgscallout.h:110
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:432
LabelAnchorPoint
Label's anchor point position.
Definition qgscallout.h:128
@ LabelPointOnExterior
The point on the label's boundary closest to the feature.
Definition qgscallout.h:129
@ LabelBottomLeft
Bottom left corner of the label's boundary.
Definition qgscallout.h:136
@ LabelBottomMiddle
Bottom middle of the label's boundary.
Definition qgscallout.h:137
@ LabelMiddleLeft
Middle left of the label's boundary.
Definition qgscallout.h:134
@ LabelBottomRight
Bottom right corner of the label's boundary.
Definition qgscallout.h:138
@ LabelMiddleRight
Middle right of the label's boundary.
Definition qgscallout.h:135
@ LabelTopMiddle
Top middle of the label's boundary.
Definition qgscallout.h:132
@ LabelTopLeft
Top left corner of the label's boundary.
Definition qgscallout.h:131
@ LabelCentroid
The labe's centroid.
Definition qgscallout.h:130
@ LabelTopRight
Top right corner of the label's boundary.
Definition qgscallout.h:133
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:88
@ LabelAnchorPointPosition
Label's anchor point position.
Definition qgscallout.h:93
@ CornerRadius
Balloon callout corner radius.
Definition qgscallout.h:102
@ OffsetFromAnchor
Distance to offset lines from anchor points.
Definition qgscallout.h:89
@ OffsetFromLabel
Distance to offset lines from label area.
Definition qgscallout.h:90
@ DestinationX
X-coordinate of callout destination (feature anchor).
Definition qgscallout.h:96
@ DestinationY
Y-coordinate of callout destination (feature anchor).
Definition qgscallout.h:97
@ WedgeWidth
Balloon callout wedge width.
Definition qgscallout.h:101
@ AnchorPointPosition
Feature's anchor point position.
Definition qgscallout.h:92
@ OriginX
X-coordinate of callout origin (label anchor).
Definition qgscallout.h:94
@ OriginY
Y-coordinate of callout origin (label anchor).
Definition qgscallout.h:95
@ Curvature
Curvature of curved line callouts.
Definition qgscallout.h:98
@ BlendMode
Callout blend mode.
Definition qgscallout.h:103
@ Orientation
Orientation of curved line callouts.
Definition qgscallout.h:99
@ Margins
Margin from text.
Definition qgscallout.h:100
@ DrawCalloutToAllParts
Whether callout lines should be drawn to all feature parts.
Definition qgscallout.h:91
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:323
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.
void setCurvature(double curvature)
Sets the callout line's curvature.
static QgsCallout * create(const QVariantMap &properties=QVariantMap(), const QgsReadWriteContext &context=QgsReadWriteContext())
Creates a new QgsCurvedLineCallout, using the settings serialized in the properties map (correspondin...
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:793
@ Clockwise
Curve lines in a clockwise direction.
Definition qgscallout.h:795
@ CounterClockwise
Curve lines in a counter-clockwise direction.
Definition qgscallout.h:796
@ Automatic
Automatically choose most cartographically pleasing orientation based on label and callout arrangemen...
Definition qgscallout.h:794
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:141
std::unique_ptr< QgsAbstractGeometry > shortestLine(const QgsGeometry &other, QString *errorMsg=nullptr) const
Returns the shortest line joining this geometry to the other geometry.
Definition qgsgeos.cpp:3123
bool intersects(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom intersects this.
Definition qgsgeos.cpp:848
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.
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.
static QgsCallout * create(const QVariantMap &properties=QVariantMap(), const QgsReadWriteContext &context=QgsReadWriteContext())
Creates a new QgsManhattanLineCallout, using the settings serialized in the properties map (correspon...
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:244
QPointF toQPointF() const
Converts a point to a QPointF.
Definition qgspointxy.h:167
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:391
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:57
@ BlendMode
Blend mode.
Definition qgsproperty.h:67
@ Boolean
Boolean value.
Definition qgsproperty.h:53
@ DoublePositive
Positive double value (including 0).
Definition qgsproperty.h:58
@ DataTypeString
Property requires a string value.
Definition qgsproperty.h:92
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:652
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 ...
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:608
~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.
static QgsCallout * create(const QVariantMap &properties=QVariantMap(), const QgsReadWriteContext &context=QgsReadWriteContext())
Creates a new QgsSimpleLineCallout, using the settings serialized in the properties map (correspondin...
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:6900
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition qgssymbol.h:30