QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
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 { static_cast< int >( QgsCallout::Property::MinimumCalloutLength ),
52 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 ),
56 QgsPropertyDefinition( "DrawCalloutToAllParts", QObject::tr( "Draw lines to all feature parts" ), QgsPropertyDefinition::Boolean, origin ) },
57 { static_cast< int >( QgsCallout::Property::AnchorPointPosition ),
58 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 ) },
60 QgsPropertyDefinition(
61 "LabelAnchorPointPosition",
63 QObject::tr( "Label's anchor point position" ),
64 QObject::tr( "string " )
65 + "[<b>point_on_exterior</b>|<b>centroid</b>|<b>TL</b>=Top left|<b>T</b>=Top middle|"
66 "<b>TR</b>=Top right|<br>"
67 "<b>L</b>=Left|<b>R</b>=Right|<br>"
68 "<b>BL</b>=Bottom left|<b>B</b>=Bottom middle|"
69 "<b>BR</b>=Bottom right]",
70 origin
71 ) },
72 { static_cast< int >( QgsCallout::Property::OriginX ), QgsPropertyDefinition( "OriginX", QObject::tr( "Callout origin (X)" ), QgsPropertyDefinition::Double, origin ) },
73 { static_cast< int >( QgsCallout::Property::OriginY ), QgsPropertyDefinition( "OriginY", QObject::tr( "Callout origin (Y)" ), QgsPropertyDefinition::Double, origin ) },
74 { static_cast< int >( QgsCallout::Property::DestinationX ), QgsPropertyDefinition( "DestinationX", QObject::tr( "Callout destination (X)" ), QgsPropertyDefinition::Double, origin ) },
75 { static_cast< int >( QgsCallout::Property::DestinationY ), QgsPropertyDefinition( "DestinationY", QObject::tr( "Callout destination (Y)" ), QgsPropertyDefinition::Double, origin ) },
76 { static_cast< int >( QgsCallout::Property::Curvature ), QgsPropertyDefinition( "Curvature", QObject::tr( "Callout line curvature" ), QgsPropertyDefinition::Double, origin ) },
77 { static_cast< int >( QgsCallout::Property::Orientation ),
78 QgsPropertyDefinition( "Orientation", QgsPropertyDefinition::DataTypeString, QObject::tr( "Callout curve orientation" ), QObject::tr( "string " ) + "[<b>auto</b>|<b>clockwise</b>|<b>counterclockwise</b>]", origin ) },
79 { static_cast< int >( QgsCallout::Property::Margins ),
80 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>" ) ) },
81 { static_cast< int >( QgsCallout::Property::WedgeWidth ), QgsPropertyDefinition( "WedgeWidth", QObject::tr( "Wedge width" ), QgsPropertyDefinition::DoublePositive, origin ) },
82 { static_cast< int >( QgsCallout::Property::CornerRadius ), QgsPropertyDefinition( "CornerRadius", QObject::tr( "Corner radius" ), QgsPropertyDefinition::DoublePositive, origin ) },
83 { static_cast< int >( QgsCallout::Property::BlendMode ), QgsPropertyDefinition( "BlendMode", QObject::tr( "Callout blend mode" ), QgsPropertyDefinition::BlendMode, origin ) },
84 };
85}
86
87
90
91QVariantMap QgsCallout::properties( const QgsReadWriteContext & ) const
92{
93 QVariantMap props;
94 props.insert( u"enabled"_s, mEnabled ? "1" : "0" );
95 props.insert( u"anchorPoint"_s, encodeAnchorPoint( mAnchorPoint ) );
96 props.insert( u"labelAnchorPoint"_s, encodeLabelAnchorPoint( mLabelAnchorPoint ) );
97 props.insert( u"blendMode"_s, static_cast< int >( QgsPainting::getBlendModeEnum( mBlendMode ) ) );
98 props.insert( u"ddProperties"_s, mDataDefinedProperties.toVariant( propertyDefinitions() ) );
99 return props;
100}
101
102void QgsCallout::readProperties( const QVariantMap &props, const QgsReadWriteContext & )
103{
104 mEnabled = props.value( u"enabled"_s, u"0"_s ).toInt();
105 mAnchorPoint = decodeAnchorPoint( props.value( u"anchorPoint"_s, QString() ).toString() );
106 mLabelAnchorPoint = decodeLabelAnchorPoint( props.value( u"labelAnchorPoint"_s, QString() ).toString() );
107 mBlendMode = QgsPainting::getCompositionMode( static_cast< Qgis::BlendMode >( props.value( u"blendMode"_s, QString::number( static_cast< int >( Qgis::BlendMode::Normal ) ) ).toUInt() ) );
108 mDataDefinedProperties.loadVariant( props.value( u"ddProperties"_s ), propertyDefinitions() );
109}
110
111bool QgsCallout::saveProperties( QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context ) const
112{
113 if ( element.isNull() )
114 {
115 return false;
116 }
117
118 const QDomElement calloutPropsElement = QgsXmlUtils::writeVariant( properties( context ), doc );
119
120 QDomElement calloutElement = doc.createElement( u"callout"_s );
121 calloutElement.setAttribute( u"type"_s, type() );
122 calloutElement.appendChild( calloutPropsElement );
123
124 element.appendChild( calloutElement );
125 return true;
126}
127
128void QgsCallout::restoreProperties( const QDomElement &element, const QgsReadWriteContext &context )
129{
130 const QVariantMap props = QgsXmlUtils::readVariant( element.firstChildElement() ).toMap();
131 readProperties( props, context );
132}
133
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
353 const QRectF &rect, const double angle, QgsCallout::LabelAnchorPoint anchor, QgsRenderContext &context, const QgsCallout::QgsCalloutContext &calloutContext, bool &pinned
354) const
355{
356 pinned = false;
358 {
359 bool ok = false;
361 if ( ok )
362 {
364 if ( ok )
365 {
366 pinned = true;
367 // data defined label point, use it directly
368 QgsGeometry labelPoint = QgsGeometry::fromPointXY( QgsPointXY( x, y ) );
369 try
370 {
371 labelPoint.transform( calloutContext.originalFeatureToMapTransform( context ) );
372 labelPoint.transform( context.mapToPixel().transform() );
373 }
374 catch ( QgsCsException & )
375 {
376 return QgsGeometry();
377 }
378 return labelPoint;
379 }
380 }
381 }
382
383 QgsGeometry label;
384 switch ( anchor )
385 {
387 label = QgsGeometry::fromRect( rect );
388 break;
389
390 case LabelCentroid:
391 label = QgsGeometry::fromRect( rect ).centroid();
392 break;
393
394 case LabelTopLeft:
395 label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomLeft() ) );
396 break;
397
398 case LabelTopMiddle:
399 label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.bottom() ) );
400 break;
401
402 case LabelTopRight:
403 label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomRight() ) );
404 break;
405
406 case LabelMiddleLeft:
407 label = QgsGeometry::fromPointXY( QgsPointXY( rect.left(), ( rect.top() + rect.bottom() ) / 2.0 ) );
408 break;
409
410 case LabelMiddleRight:
411 label = QgsGeometry::fromPointXY( QgsPointXY( rect.right(), ( rect.top() + rect.bottom() ) / 2.0 ) );
412 break;
413
414 case LabelBottomLeft:
415 label = QgsGeometry::fromPointXY( QgsPointXY( rect.topLeft() ) );
416 break;
417
419 label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.top() ) );
420 break;
421
422 case LabelBottomRight:
423 label = QgsGeometry::fromPointXY( QgsPointXY( rect.topRight() ) );
424 break;
425 }
426
427 label.rotate( angle, rect.topLeft() );
428 return label;
429}
430
431QgsGeometry QgsCallout::calloutLineToPart( const QgsGeometry &labelGeometry, const QgsAbstractGeometry *partGeometry, QgsRenderContext &context, const QgsCalloutContext &calloutContext, bool &pinned ) const
432{
433 pinned = false;
434 AnchorPoint anchor = anchorPoint();
435 const QgsAbstractGeometry *evaluatedPartAnchor = partGeometry;
436 std::unique_ptr< QgsAbstractGeometry > tempPartAnchor;
437
439 {
440 bool ok = false;
442 if ( ok )
443 {
445 if ( ok )
446 {
447 pinned = true;
448 tempPartAnchor = std::make_unique< QgsPoint >( Qgis::WkbType::Point, x, y );
449 evaluatedPartAnchor = tempPartAnchor.get();
450 try
451 {
452 tempPartAnchor->transform( calloutContext.originalFeatureToMapTransform( context ) );
453 tempPartAnchor->transform( context.mapToPixel().transform() );
454 }
455 catch ( QgsCsException & )
456 {
457 evaluatedPartAnchor = partGeometry;
458 }
459 }
460 }
461 }
462
464 {
465 const QString encodedAnchor = encodeAnchorPoint( anchor );
466 context.expressionContext().setOriginalValueVariable( encodedAnchor );
467 anchor = decodeAnchorPoint( dataDefinedProperties().valueAsString( QgsCallout::Property::AnchorPointPosition, context.expressionContext(), encodedAnchor ) );
468 }
469
470 QgsGeometry line;
471 const QgsGeos labelGeos( labelGeometry.constGet() );
472
473 switch ( QgsWkbTypes::geometryType( evaluatedPartAnchor->wkbType() ) )
474 {
477 {
478 line = QgsGeometry( labelGeos.shortestLine( evaluatedPartAnchor ) );
479 break;
480 }
481
483 {
484 if ( labelGeos.intersects( evaluatedPartAnchor ) )
485 return QgsGeometry();
486
487 // ideally avoid this unwanted clone in future. For now we need it because poleOfInaccessibility/pointOnSurface are
488 // only available to QgsGeometry objects
489 const QgsGeometry evaluatedPartAnchorGeom( evaluatedPartAnchor->clone() );
490 switch ( anchor )
491 {
493 line = QgsGeometry(
494 labelGeos.shortestLine( evaluatedPartAnchorGeom.poleOfInaccessibility( std::max( evaluatedPartAnchor->boundingBox().width(), evaluatedPartAnchor->boundingBox().height() ) / 20.0 ) )
495 ); // really rough (but quick) pole of inaccessibility
496 break;
498 line = QgsGeometry( labelGeos.shortestLine( evaluatedPartAnchorGeom.pointOnSurface() ) );
499 break;
501 line = QgsGeometry( labelGeos.shortestLine( evaluatedPartAnchor ) );
502 break;
504 line = QgsGeometry( labelGeos.shortestLine( evaluatedPartAnchorGeom.centroid() ) );
505 break;
506 }
507 break;
508 }
509
512 return QgsGeometry(); // shouldn't even get here..
513 }
514 return line;
515}
516
517//
518// QgsCallout::QgsCalloutContext
519//
520
522{
523 if ( !mOriginalFeatureToMapTransform.isValid() )
524 {
525 // lazy initialization, only create if needed...
526 mOriginalFeatureToMapTransform = QgsCoordinateTransform( originalFeatureCrs, renderContext.coordinateTransform().destinationCrs(), renderContext.transformContext() );
527 }
528 return mOriginalFeatureToMapTransform;
529}
530
531
532//
533// QgsSimpleLineCallout
534//
535
537{
538 mLineSymbol = std::make_unique< QgsLineSymbol >( QgsSymbolLayerList() << new QgsSimpleLineSymbolLayer( QColor( 60, 60, 60 ), .3 ) );
539}
540
542
544 : QgsCallout( other )
545 , mLineSymbol( other.mLineSymbol ? other.mLineSymbol->clone() : nullptr )
546 , mMinCalloutLength( other.mMinCalloutLength )
547 , mMinCalloutLengthUnit( other.mMinCalloutLengthUnit )
548 , mMinCalloutLengthScale( other.mMinCalloutLengthScale )
549 , mOffsetFromAnchorDistance( other.mOffsetFromAnchorDistance )
550 , mOffsetFromAnchorUnit( other.mOffsetFromAnchorUnit )
551 , mOffsetFromAnchorScale( other.mOffsetFromAnchorScale )
552 , mOffsetFromLabelDistance( other.mOffsetFromLabelDistance )
553 , mOffsetFromLabelUnit( other.mOffsetFromLabelUnit )
554 , mOffsetFromLabelScale( other.mOffsetFromLabelScale )
555 , mDrawCalloutToAllParts( other.mDrawCalloutToAllParts )
556{}
557
559{
560 auto callout = std::make_unique< QgsSimpleLineCallout >();
561 callout->readProperties( properties, context );
562 return callout.release();
563}
564
566{
567 return u"simple"_s;
568}
569
574
575QVariantMap QgsSimpleLineCallout::properties( const QgsReadWriteContext &context ) const
576{
577 QVariantMap props = QgsCallout::properties( context );
578
579 if ( mLineSymbol )
580 {
581 props[u"lineSymbol"_s] = QgsSymbolLayerUtils::symbolProperties( mLineSymbol.get() );
582 }
583 props[u"minLength"_s] = mMinCalloutLength;
584 props[u"minLengthUnit"_s] = QgsUnitTypes::encodeUnit( mMinCalloutLengthUnit );
585 props[u"minLengthMapUnitScale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mMinCalloutLengthScale );
586
587 props[u"offsetFromAnchor"_s] = mOffsetFromAnchorDistance;
588 props[u"offsetFromAnchorUnit"_s] = QgsUnitTypes::encodeUnit( mOffsetFromAnchorUnit );
589 props[u"offsetFromAnchorMapUnitScale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetFromAnchorScale );
590 props[u"offsetFromLabel"_s] = mOffsetFromLabelDistance;
591 props[u"offsetFromLabelUnit"_s] = QgsUnitTypes::encodeUnit( mOffsetFromLabelUnit );
592 props[u"offsetFromLabelMapUnitScale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetFromLabelScale );
593
594 props[u"drawToAllParts"_s] = mDrawCalloutToAllParts;
595
596 return props;
597}
598
599void QgsSimpleLineCallout::readProperties( const QVariantMap &props, const QgsReadWriteContext &context )
600{
601 QgsCallout::readProperties( props, context );
602
603 const QString lineSymbolDef = props.value( u"lineSymbol"_s ).toString();
604 QDomDocument doc( u"symbol"_s );
605 doc.setContent( lineSymbolDef );
606 const QDomElement symbolElem = doc.firstChildElement( u"symbol"_s );
607 std::unique_ptr< QgsLineSymbol > lineSymbol( QgsSymbolLayerUtils::loadSymbol< QgsLineSymbol >( symbolElem, context ) );
608 if ( lineSymbol )
609 mLineSymbol = std::move( lineSymbol );
610
611 mMinCalloutLength = props.value( u"minLength"_s, 0 ).toDouble();
612 mMinCalloutLengthUnit = QgsUnitTypes::decodeRenderUnit( props.value( u"minLengthUnit"_s ).toString() );
613 mMinCalloutLengthScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( u"minLengthMapUnitScale"_s ).toString() );
614
615 mOffsetFromAnchorDistance = props.value( u"offsetFromAnchor"_s, 0 ).toDouble();
616 mOffsetFromAnchorUnit = QgsUnitTypes::decodeRenderUnit( props.value( u"offsetFromAnchorUnit"_s ).toString() );
617 mOffsetFromAnchorScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( u"offsetFromAnchorMapUnitScale"_s ).toString() );
618 mOffsetFromLabelDistance = props.value( u"offsetFromLabel"_s, 0 ).toDouble();
619 mOffsetFromLabelUnit = QgsUnitTypes::decodeRenderUnit( props.value( u"offsetFromLabelUnit"_s ).toString() );
620 mOffsetFromLabelScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( u"offsetFromLabelMapUnitScale"_s ).toString() );
621
622 mDrawCalloutToAllParts = props.value( u"drawToAllParts"_s, false ).toBool();
623}
624
626{
627 QgsCallout::startRender( context );
628 if ( mLineSymbol )
629 mLineSymbol->startRender( context );
630}
631
633{
634 QgsCallout::stopRender( context );
635 if ( mLineSymbol )
636 mLineSymbol->stopRender( context );
637}
638
639QSet<QString> QgsSimpleLineCallout::referencedFields( const QgsRenderContext &context ) const
640{
641 QSet<QString> fields = QgsCallout::referencedFields( context );
642 if ( mLineSymbol )
643 fields.unite( mLineSymbol->usedAttributes( context ) );
644 return fields;
645}
646
648{
649 return mLineSymbol.get();
650}
651
653{
654 mLineSymbol.reset( symbol );
655}
656
657void QgsSimpleLineCallout::draw( QgsRenderContext &context, const QRectF &rect, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext )
658{
659 LabelAnchorPoint labelAnchor = labelAnchorPoint();
661 {
662 const QString encodedAnchor = encodeLabelAnchorPoint( labelAnchor );
663 context.expressionContext().setOriginalValueVariable( encodedAnchor );
664 labelAnchor = decodeLabelAnchorPoint( dataDefinedProperties().valueAsString( QgsCallout::Property::LabelAnchorPointPosition, context.expressionContext(), encodedAnchor ) );
665 }
666
667 bool originPinned = false;
668 const QgsGeometry label = calloutLabelPoint( rect, angle, labelAnchor, context, calloutContext, originPinned );
669 if ( label.isNull() )
670 return;
671
672 auto drawCalloutLine = [this, &context, &calloutContext, &label, &rect, angle, &anchor, originPinned]( const QgsAbstractGeometry *partAnchor ) {
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(
693 createCalloutLine( qgsgeometry_cast< const QgsLineString * >( line.constGet() )->startPoint(), qgsgeometry_cast< const QgsLineString * >( line.constGet() )->endPoint(), context, rect, angle, anchor, calloutContext )
694 );
695
696 double offsetFromAnchor = mOffsetFromAnchorDistance;
698 {
701 }
702 const double offsetFromAnchorPixels = context.convertToPainterUnits( offsetFromAnchor, mOffsetFromAnchorUnit, mOffsetFromAnchorScale );
703
704 double offsetFromLabel = mOffsetFromLabelDistance;
706 {
709 }
710 const double offsetFromLabelPixels = context.convertToPainterUnits( offsetFromLabel, mOffsetFromLabelUnit, mOffsetFromLabelScale );
711 if ( offsetFromAnchorPixels > 0 || offsetFromLabelPixels > 0 )
712 {
713 calloutCurve.reset( calloutCurve->curveSubstring( offsetFromLabelPixels, calloutCurve->length() - offsetFromAnchorPixels ) );
714 }
715
716 const QPolygonF points = calloutCurve->asQPolygonF();
717
718 if ( points.empty() )
719 return;
720
721 QgsCalloutPosition position;
722 position.setOrigin( context.mapToPixel().toMapCoordinates( points.at( 0 ).x(), points.at( 0 ).y() ).toQPointF() );
723 position.setOriginIsPinned( originPinned );
724 position.setDestination( context.mapToPixel().toMapCoordinates( points.constLast().x(), points.constLast().y() ).toQPointF() );
725 position.setDestinationIsPinned( destinationPinned );
726 calloutContext.addCalloutPosition( position );
727
728 mLineSymbol->renderPolyline( points, nullptr, context );
729 };
730
731 bool toAllParts = mDrawCalloutToAllParts;
733 {
734 context.expressionContext().setOriginalValueVariable( toAllParts );
736 }
737
738 if ( calloutContext.allFeaturePartsLabeled || !toAllParts )
739 drawCalloutLine( anchor.constGet() );
740 else
741 {
742 for ( auto it = anchor.const_parts_begin(); it != anchor.const_parts_end(); ++it )
743 drawCalloutLine( *it );
744 }
745}
746
747QgsCurve *QgsSimpleLineCallout::createCalloutLine( const QgsPoint &start, const QgsPoint &end, QgsRenderContext &, const QRectF &, const double, const QgsGeometry &, QgsCallout::QgsCalloutContext & ) const
748{
749 return new QgsLineString( start, end );
750}
751
752//
753// QgsManhattanLineCallout
754//
755
758
762
763
764QgsCallout *QgsManhattanLineCallout::create( const QVariantMap &properties, const QgsReadWriteContext &context ) // cppcheck-suppress duplInheritedMember
765{
766 auto callout = std::make_unique< QgsManhattanLineCallout >();
767 callout->readProperties( properties, context );
768 return callout.release();
769}
770
772{
773 return u"manhattan"_s;
774}
775
780
781QgsCurve *QgsManhattanLineCallout::createCalloutLine( const QgsPoint &start, const QgsPoint &end, QgsRenderContext &, const QRectF &, const double, const QgsGeometry &, QgsCallout::QgsCalloutContext & ) const
782{
783 const QgsPoint mid1 = QgsPoint( start.x(), end.y() );
784 return new QgsLineString( QVector< QgsPoint >() << start << mid1 << end );
785}
786
787
788//
789// QgsCurvedLineCallout
790//
791
794
796 : QgsSimpleLineCallout( other )
797 , mOrientation( other.mOrientation )
798 , mCurvature( other.mCurvature )
799{}
800
801QgsCallout *QgsCurvedLineCallout::create( const QVariantMap &properties, const QgsReadWriteContext &context ) // cppcheck-suppress duplInheritedMember
802{
803 auto callout = std::make_unique< QgsCurvedLineCallout >();
804 callout->readProperties( properties, context );
805
806 callout->setCurvature( properties.value( u"curvature"_s, 0.1 ).toDouble() );
807 callout->setOrientation( decodeOrientation( properties.value( u"orientation"_s, u"auto"_s ).toString() ) );
808
809 return callout.release();
810}
811
813{
814 return u"curved"_s;
815}
816
821
822QVariantMap QgsCurvedLineCallout::properties( const QgsReadWriteContext &context ) const
823{
824 QVariantMap props = QgsSimpleLineCallout::properties( context );
825 props.insert( u"curvature"_s, mCurvature );
826 props.insert( u"orientation"_s, encodeOrientation( mOrientation ) );
827 return props;
828}
829
831 const QgsPoint &start, const QgsPoint &end, QgsRenderContext &context, const QRectF &rect, const double, const QgsGeometry &, QgsCallout::QgsCalloutContext &
832) const
833{
834 double curvature = mCurvature * 100;
836 {
839 }
840
841 Orientation orientation = mOrientation;
843 {
844 bool ok = false;
845 const QString orientationString = dataDefinedProperties().property( QgsCallout::Property::Orientation ).valueAsString( context.expressionContext(), QString(), &ok );
846 if ( ok )
847 {
848 orientation = decodeOrientation( orientationString );
849 }
850 }
851
852 if ( orientation == Automatic )
853 {
854 // to calculate automatically the best curve orientation, we first check which side of the label bounding box
855 // the callout origin is nearest to
856 switch ( QgsGeometryUtilsBase::closestSideOfRectangle( rect.right(), rect.bottom(), rect.left(), rect.top(), start.x(), start.y() ) )
857 {
858 case 1:
859 // closest to bottom
860 if ( qgsDoubleNear( end.x(), start.x() ) )
861 {
862 // if vertical line, we bend depending on whether the line sits towards the left or right side of the label
863 if ( start.x() < ( rect.left() + 0.5 * rect.width() ) )
865 else
867 }
868 else if ( end.x() > start.x() )
870 else
872 break;
873
874 case 2:
875 // closest to bottom-right
876 if ( end.x() < start.x() )
878 else if ( end.y() < start.y() )
880 else if ( end.x() - start.x() < end.y() - start.y() )
882 else
884 break;
885
886 case 3:
887 // closest to right
888 if ( qgsDoubleNear( end.y(), start.y() ) )
889 {
890 // if horizontal line, we bend depending on whether the line sits towards the top or bottom side of the label
891 if ( start.y() < ( rect.top() + 0.5 * rect.height() ) )
893 else
895 }
896 else if ( end.y() < start.y() )
898 else
900 break;
901
902 case 4:
903 // closest to top-right
904 if ( end.x() < start.x() )
906 else if ( end.y() > start.y() )
908 else if ( end.x() - start.x() < start.y() - end.y() )
910 else
912 break;
913
914 case 5:
915 // closest to top
916 if ( qgsDoubleNear( end.x(), start.x() ) )
917 {
918 // if vertical line, we bend depending on whether the line sits towards the left or right side of the label
919 if ( start.x() < ( rect.left() + 0.5 * rect.width() ) )
921 else
923 }
924 else if ( end.x() < start.x() )
926 else
928 break;
929
930 case 6:
931 // closest to top-left
932 if ( end.x() > start.x() )
934 else if ( end.y() > start.y() )
936 else if ( start.x() - end.x() < start.y() - end.y() )
938 else
940 break;
941
942 case 7:
943 //closest to left
944 if ( qgsDoubleNear( end.y(), start.y() ) )
945 {
946 // if horizontal line, we bend depending on whether the line sits towards the top or bottom side of the label
947 if ( start.y() < ( rect.top() + 0.5 * rect.height() ) )
949 else
951 }
952 else if ( end.y() > start.y() )
954 else
956 break;
957
958 case 8:
959 //closest to bottom-left
960 if ( end.x() > start.x() )
962 else if ( end.y() < start.y() )
964 else if ( start.x() - end.x() < end.y() - start.y() )
966 else
968 break;
969 }
970 }
971
972 // turn the line into a curved line. We do this by creating a circular string from the callout line's
973 // start to end point, where the curve point is in the middle of the callout line and perpendicularly offset
974 // by a proportion of the overall callout line length
975 const double distance = ( orientation == Clockwise ? 1 : -1 ) * start.distance( end ) * curvature / 100.0;
976 double midX, midY;
977 QgsGeometryUtilsBase::perpendicularOffsetPointAlongSegment( start.x(), start.y(), end.x(), end.y(), 0.5, distance, &midX, &midY );
978
979 return new QgsCircularString( start, QgsPoint( midX, midY ), end );
980}
981
982QgsCurvedLineCallout::Orientation QgsCurvedLineCallout::decodeOrientation( const QString &string )
983{
984 const QString cleaned = string.toLower().trimmed();
985 if ( cleaned == "auto"_L1 )
986 return Automatic;
987 if ( cleaned == "clockwise"_L1 )
988 return Clockwise;
989 if ( cleaned == "counterclockwise"_L1 )
990 return CounterClockwise;
991 return Automatic;
992}
993
994QString QgsCurvedLineCallout::encodeOrientation( QgsCurvedLineCallout::Orientation orientation )
995{
996 switch ( orientation )
997 {
999 return u"auto"_s;
1001 return u"clockwise"_s;
1003 return u"counterclockwise"_s;
1004 }
1005 return QString();
1006}
1007
1009{
1010 return mOrientation;
1011}
1012
1017
1019{
1020 return mCurvature;
1021}
1022
1024{
1025 mCurvature = curvature;
1026}
1027
1028
1029//
1030// QgsBalloonCallout
1031//
1032
1034{
1035 mFillSymbol = std::make_unique< QgsFillSymbol >( QgsSymbolLayerList() << new QgsSimpleFillSymbolLayer( QColor( 255, 200, 60 ) ) );
1036}
1037
1039
1041 : QgsCallout( other )
1042 , mFillSymbol( other.mFillSymbol ? other.mFillSymbol->clone() : nullptr )
1043 , mMarkerSymbol( other.mMarkerSymbol ? other.mMarkerSymbol->clone() : nullptr )
1044 , mOffsetFromAnchorDistance( other.mOffsetFromAnchorDistance )
1045 , mOffsetFromAnchorUnit( other.mOffsetFromAnchorUnit )
1046 , mOffsetFromAnchorScale( other.mOffsetFromAnchorScale )
1047 , mMargins( other.mMargins )
1048 , mMarginUnit( other.mMarginUnit )
1049 , mWedgeWidth( other.mWedgeWidth )
1050 , mWedgeWidthUnit( other.mWedgeWidthUnit )
1051 , mWedgeWidthScale( other.mWedgeWidthScale )
1052 , mCornerRadius( other.mCornerRadius )
1053 , mCornerRadiusUnit( other.mCornerRadiusUnit )
1054 , mCornerRadiusScale( other.mCornerRadiusScale )
1055{}
1056
1058{
1059 auto callout = std::make_unique< QgsBalloonCallout >();
1060 callout->readProperties( properties, context );
1061 return callout.release();
1062}
1063
1065{
1066 return u"balloon"_s;
1067}
1068
1070{
1071 return new QgsBalloonCallout( *this );
1072}
1073
1074QVariantMap QgsBalloonCallout::properties( const QgsReadWriteContext &context ) const
1075{
1076 QVariantMap props = QgsCallout::properties( context );
1077
1078 if ( mFillSymbol )
1079 {
1080 props[u"fillSymbol"_s] = QgsSymbolLayerUtils::symbolProperties( mFillSymbol.get() );
1081 }
1082
1083 if ( mMarkerSymbol )
1084 {
1085 props[u"markerSymbol"_s] = QgsSymbolLayerUtils::symbolProperties( mMarkerSymbol.get() );
1086 }
1087
1088 props[u"offsetFromAnchor"_s] = mOffsetFromAnchorDistance;
1089 props[u"offsetFromAnchorUnit"_s] = QgsUnitTypes::encodeUnit( mOffsetFromAnchorUnit );
1090 props[u"offsetFromAnchorMapUnitScale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetFromAnchorScale );
1091
1092 props[u"margins"_s] = mMargins.toString();
1093 props[u"marginsUnit"_s] = QgsUnitTypes::encodeUnit( mMarginUnit );
1094
1095 props[u"wedgeWidth"_s] = mWedgeWidth;
1096 props[u"wedgeWidthUnit"_s] = QgsUnitTypes::encodeUnit( mWedgeWidthUnit );
1097 props[u"wedgeWidthMapUnitScale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mWedgeWidthScale );
1098
1099 props[u"cornerRadius"_s] = mCornerRadius;
1100 props[u"cornerRadiusUnit"_s] = QgsUnitTypes::encodeUnit( mCornerRadiusUnit );
1101 props[u"cornerRadiusMapUnitScale"_s] = QgsSymbolLayerUtils::encodeMapUnitScale( mCornerRadiusScale );
1102
1103 return props;
1104}
1105
1106void QgsBalloonCallout::readProperties( const QVariantMap &props, const QgsReadWriteContext &context )
1107{
1108 QgsCallout::readProperties( props, context );
1109
1110 {
1111 const QString fillSymbolDef = props.value( u"fillSymbol"_s ).toString();
1112 QDomDocument doc( u"symbol"_s );
1113 doc.setContent( fillSymbolDef );
1114 const QDomElement symbolElem = doc.firstChildElement( u"symbol"_s );
1115 std::unique_ptr< QgsFillSymbol > fillSymbol( QgsSymbolLayerUtils::loadSymbol< QgsFillSymbol >( symbolElem, context ) );
1116 if ( fillSymbol )
1117 mFillSymbol = std::move( fillSymbol );
1118 }
1119
1120 {
1121 const QString markerSymbolDef = props.value( u"markerSymbol"_s ).toString();
1122 QDomDocument doc( u"symbol"_s );
1123 doc.setContent( markerSymbolDef );
1124 const QDomElement symbolElem = doc.firstChildElement( u"symbol"_s );
1125 std::unique_ptr< QgsMarkerSymbol > markerSymbol( QgsSymbolLayerUtils::loadSymbol< QgsMarkerSymbol >( symbolElem, context ) );
1126 if ( markerSymbol )
1127 mMarkerSymbol = std::move( markerSymbol );
1128 }
1129
1130 mOffsetFromAnchorDistance = props.value( u"offsetFromAnchor"_s, 0 ).toDouble();
1131 mOffsetFromAnchorUnit = QgsUnitTypes::decodeRenderUnit( props.value( u"offsetFromAnchorUnit"_s ).toString() );
1132 mOffsetFromAnchorScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( u"offsetFromAnchorMapUnitScale"_s ).toString() );
1133
1134 mMargins = QgsMargins::fromString( props.value( u"margins"_s ).toString() );
1135 mMarginUnit = QgsUnitTypes::decodeRenderUnit( props.value( u"marginsUnit"_s ).toString() );
1136
1137 mWedgeWidth = props.value( u"wedgeWidth"_s, 2.64 ).toDouble();
1138 mWedgeWidthUnit = QgsUnitTypes::decodeRenderUnit( props.value( u"wedgeWidthUnit"_s ).toString() );
1139 mWedgeWidthScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( u"wedgeWidthMapUnitScale"_s ).toString() );
1140
1141 mCornerRadius = props.value( u"cornerRadius"_s, 0 ).toDouble();
1142 mCornerRadiusUnit = QgsUnitTypes::decodeRenderUnit( props.value( u"cornerRadiusUnit"_s ).toString() );
1143 mCornerRadiusScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( u"cornerRadiusMapUnitScale"_s ).toString() );
1144}
1145
1147{
1148 QgsCallout::startRender( context );
1149 if ( mFillSymbol )
1150 mFillSymbol->startRender( context );
1151 if ( mMarkerSymbol )
1152 mMarkerSymbol->startRender( context );
1153}
1154
1156{
1157 QgsCallout::stopRender( context );
1158 if ( mFillSymbol )
1159 mFillSymbol->stopRender( context );
1160 if ( mMarkerSymbol )
1161 mMarkerSymbol->stopRender( context );
1162}
1163
1164QSet<QString> QgsBalloonCallout::referencedFields( const QgsRenderContext &context ) const
1165{
1166 QSet<QString> fields = QgsCallout::referencedFields( context );
1167 if ( mFillSymbol )
1168 fields.unite( mFillSymbol->usedAttributes( context ) );
1169 if ( mMarkerSymbol )
1170 fields.unite( mMarkerSymbol->usedAttributes( context ) );
1171 return fields;
1172}
1173
1175{
1176 return mFillSymbol.get();
1177}
1178
1180{
1181 mFillSymbol.reset( symbol );
1182}
1183
1185{
1186 return mMarkerSymbol.get();
1187}
1188
1190{
1191 mMarkerSymbol.reset( symbol );
1192}
1193
1194void QgsBalloonCallout::draw( QgsRenderContext &context, const QRectF &rect, const double, const QgsGeometry &anchor, QgsCalloutContext &calloutContext )
1195{
1196 bool destinationIsPinned = false;
1197 QgsGeometry line = calloutLineToPart( QgsGeometry::fromRect( rect ), anchor.constGet(), context, calloutContext, destinationIsPinned );
1198
1199 if ( mMarkerSymbol )
1200 {
1202 {
1203 QgsPoint anchorPoint = ls->endPoint();
1204 mMarkerSymbol->renderPoint( anchorPoint.toQPointF(), nullptr, context );
1205 }
1206 }
1207
1208 double offsetFromAnchor = mOffsetFromAnchorDistance;
1210 {
1213 }
1214 const double offsetFromAnchorPixels = context.convertToPainterUnits( offsetFromAnchor, mOffsetFromAnchorUnit, mOffsetFromAnchorScale );
1215
1216 if ( offsetFromAnchorPixels > 0 )
1217 {
1219 {
1220 line = QgsGeometry( ls->curveSubstring( 0, ls->length() - offsetFromAnchorPixels ) );
1221 }
1222 }
1223
1224 QgsPointXY destination;
1225 QgsPointXY origin;
1227 {
1228 origin = ls->startPoint();
1229 destination = ls->endPoint();
1230 }
1231 else
1232 {
1233 destination = QgsPointXY( rect.center() );
1234 }
1235
1236 const QPolygonF points = getPoints( context, destination, rect );
1237 if ( points.empty() )
1238 return;
1239
1240 if ( !origin.isEmpty() )
1241 {
1242 QgsCalloutPosition position;
1243 position.setOrigin( context.mapToPixel().toMapCoordinates( origin.x(), origin.y() ).toQPointF() );
1244 position.setOriginIsPinned( false );
1245 position.setDestination( context.mapToPixel().toMapCoordinates( destination.x(), destination.y() ).toQPointF() );
1246 position.setDestinationIsPinned( destinationIsPinned );
1247 calloutContext.addCalloutPosition( position );
1248 }
1249
1250 mFillSymbol->renderPolygon( points, nullptr, nullptr, context );
1251}
1252
1253QPolygonF QgsBalloonCallout::getPoints( QgsRenderContext &context, QgsPointXY origin, QRectF rect ) const
1254{
1255 double segmentPointWidth = mWedgeWidth;
1257 {
1258 context.expressionContext().setOriginalValueVariable( segmentPointWidth );
1259 segmentPointWidth = dataDefinedProperties().valueAsDouble( QgsCallout::Property::WedgeWidth, context.expressionContext(), segmentPointWidth );
1260 }
1261 segmentPointWidth = context.convertToPainterUnits( segmentPointWidth, mWedgeWidthUnit, mWedgeWidthScale );
1262
1263 double cornerRadius = mCornerRadius;
1265 {
1268 }
1269 cornerRadius = context.convertToPainterUnits( cornerRadius, mCornerRadiusUnit, mCornerRadiusScale );
1270
1271 double left = mMargins.left();
1272 double right = mMargins.right();
1273 double top = mMargins.top();
1274 double bottom = mMargins.bottom();
1275
1277 {
1278 const QVariant value = dataDefinedProperties().value( QgsCallout::Property::Margins, context.expressionContext() );
1279 if ( !QgsVariantUtils::isNull( value ) )
1280 {
1281 if ( value.userType() == QMetaType::Type::QVariantList )
1282 {
1283 const QVariantList list = value.toList();
1284 if ( list.size() == 4 )
1285 {
1286 bool topOk = false;
1287 bool rightOk = false;
1288 bool bottomOk = false;
1289 bool leftOk = false;
1290 const double evaluatedTop = list.at( 0 ).toDouble( &topOk );
1291 const double evaluatedRight = list.at( 1 ).toDouble( &rightOk );
1292 const double evaluatedBottom = list.at( 2 ).toDouble( &bottomOk );
1293 const double evaluatedLeft = list.at( 3 ).toDouble( &leftOk );
1294 if ( topOk && rightOk && bottomOk && leftOk )
1295 {
1296 left = evaluatedLeft;
1297 top = evaluatedTop;
1298 right = evaluatedRight;
1299 bottom = evaluatedBottom;
1300 }
1301 }
1302 }
1303 else
1304 {
1305 const QStringList list = value.toString().trimmed().split( ',' );
1306 if ( list.count() == 4 )
1307 {
1308 bool topOk = false;
1309 bool rightOk = false;
1310 bool bottomOk = false;
1311 bool leftOk = false;
1312 const double evaluatedTop = list.at( 0 ).toDouble( &topOk );
1313 const double evaluatedRight = list.at( 1 ).toDouble( &rightOk );
1314 const double evaluatedBottom = list.at( 2 ).toDouble( &bottomOk );
1315 const double evaluatedLeft = list.at( 3 ).toDouble( &leftOk );
1316 if ( topOk && rightOk && bottomOk && leftOk )
1317 {
1318 left = evaluatedLeft;
1319 top = evaluatedTop;
1320 right = evaluatedRight;
1321 bottom = evaluatedBottom;
1322 }
1323 }
1324 }
1325 }
1326 }
1327
1328 const double marginLeft = context.convertToPainterUnits( left, mMarginUnit );
1329 const double marginRight = context.convertToPainterUnits( right, mMarginUnit );
1330 const double marginTop = context.convertToPainterUnits( top, mMarginUnit );
1331 const double marginBottom = context.convertToPainterUnits( bottom, mMarginUnit );
1332
1333 const QRectF expandedRect = rect.height() < 0 ? QRectF( rect.left() - marginLeft, rect.top() + marginBottom, rect.width() + marginLeft + marginRight, rect.height() - marginTop - marginBottom )
1334 : QRectF( rect.left() - marginLeft, rect.top() - marginTop, rect.width() + marginLeft + marginRight, rect.height() + marginTop + marginBottom );
1335
1336 // IMPORTANT -- check for degenerate height is sometimes >=0, because QRectF are not normalized and we are using painter
1337 // coordinates with descending vertical axis!
1338 if ( expandedRect.width() <= 0 || ( rect.height() < 0 && expandedRect.height() >= 0 ) || ( rect.height() > 0 && expandedRect.height() <= 0 ) )
1339 return QPolygonF();
1340
1341 const QPainterPath path = QgsShapeGenerator::createBalloon( origin, expandedRect, segmentPointWidth, cornerRadius );
1342 const QTransform t = QTransform::fromScale( 100, 100 );
1343 const QTransform ti = t.inverted();
1344 const QPolygonF poly = path.toFillPolygon( t );
1345 return ti.map( poly );
1346}
@ ForceVector
Always force vector-based rendering, even when the result will be visually different to a raster-base...
Definition qgis.h:2801
BlendMode
Blending modes defining the available composition modes that can be used when painting.
Definition qgis.h:5087
@ Normal
Normal.
Definition qgis.h:5088
@ Point
Points.
Definition qgis.h:380
@ Line
Lines.
Definition qgis.h:381
@ Polygon
Polygons.
Definition qgis.h:382
@ Unknown
Unknown types.
Definition qgis.h:383
@ Null
No geometry.
Definition qgis.h:384
@ Point
Point.
Definition qgis.h:296
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: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
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: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.
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: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:139
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:3121
bool intersects(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom intersects this.
Definition qgsgeos.cpp:850
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: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 ...
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.
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:6975
T qgsgeometry_cast(QgsAbstractGeometry *geom)
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition qgssymbol.h:30