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