QGIS API Documentation 3.43.0-Master (0cdc48caa8d)
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, initPropertyDefinitions );
198 return sPropertyDefinitions;
199}
200
202{
203 if ( ok )
204 *ok = true;
205 const QString cleaned = name.toLower().trimmed();
206
207 if ( cleaned == QLatin1String( "pole_of_inaccessibility" ) )
209 else if ( cleaned == QLatin1String( "point_on_exterior" ) )
210 return PointOnExterior;
211 else if ( cleaned == QLatin1String( "point_on_surface" ) )
212 return PointOnSurface;
213 else if ( cleaned == QLatin1String( "centroid" ) )
214 return Centroid;
215
216 if ( ok )
217 *ok = false;
219}
220
222{
223 switch ( anchor )
224 {
226 return QStringLiteral( "pole_of_inaccessibility" );
227 case PointOnExterior:
228 return QStringLiteral( "point_on_exterior" );
229 case PointOnSurface:
230 return QStringLiteral( "point_on_surface" );
231 case Centroid:
232 return QStringLiteral( "centroid" );
233 }
234 return QString();
235}
236
238{
239 switch ( anchor )
240 {
242 return QStringLiteral( "point_on_exterior" );
243 case LabelCentroid:
244 return QStringLiteral( "centroid" );
245 case LabelTopLeft:
246 return QStringLiteral( "tl" );
247 case LabelTopMiddle:
248 return QStringLiteral( "t" );
249 case LabelTopRight:
250 return QStringLiteral( "tr" );
251 case LabelMiddleLeft:
252 return QStringLiteral( "l" );
253 case LabelMiddleRight:
254 return QStringLiteral( "r" );
255 case LabelBottomLeft:
256 return QStringLiteral( "bl" );
258 return QStringLiteral( "b" );
259 case LabelBottomRight:
260 return QStringLiteral( "br" );
261 }
262
263 return QString();
264}
265
267{
268 if ( ok )
269 *ok = true;
270 const QString cleaned = name.toLower().trimmed();
271
272 if ( cleaned == QLatin1String( "point_on_exterior" ) )
274 else if ( cleaned == QLatin1String( "centroid" ) )
275 return LabelCentroid;
276 else if ( cleaned == QLatin1String( "tl" ) )
277 return LabelTopLeft;
278 else if ( cleaned == QLatin1String( "t" ) )
279 return LabelTopMiddle;
280 else if ( cleaned == QLatin1String( "tr" ) )
281 return LabelTopRight;
282 else if ( cleaned == QLatin1String( "l" ) )
283 return LabelMiddleLeft;
284 else if ( cleaned == QLatin1String( "r" ) )
285 return LabelMiddleRight;
286 else if ( cleaned == QLatin1String( "bl" ) )
287 return LabelBottomLeft;
288 else if ( cleaned == QLatin1String( "b" ) )
289 return LabelBottomMiddle;
290 else if ( cleaned == QLatin1String( "br" ) )
291 return LabelBottomRight;
292
293 if ( ok )
294 *ok = false;
296}
297
298QgsGeometry QgsCallout::labelAnchorGeometry( const QRectF &rect, const double angle, LabelAnchorPoint anchor ) const
299{
300 QgsGeometry label;
301 switch ( anchor )
302 {
304 label = QgsGeometry::fromRect( rect );
305 break;
306
307 case LabelCentroid:
308 label = QgsGeometry::fromRect( rect ).centroid();
309 break;
310
311 case LabelTopLeft:
312 label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomLeft() ) );
313 break;
314
315 case LabelTopMiddle:
316 label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.bottom() ) );
317 break;
318
319 case LabelTopRight:
320 label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomRight() ) );
321 break;
322
323 case LabelMiddleLeft:
324 label = QgsGeometry::fromPointXY( QgsPointXY( rect.left(), ( rect.top() + rect.bottom() ) / 2.0 ) );
325 break;
326
327 case LabelMiddleRight:
328 label = QgsGeometry::fromPointXY( QgsPointXY( rect.right(), ( rect.top() + rect.bottom() ) / 2.0 ) );
329 break;
330
331 case LabelBottomLeft:
332 label = QgsGeometry::fromPointXY( QgsPointXY( rect.topLeft() ) );
333 break;
334
336 label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.top() ) );
337 break;
338
339 case LabelBottomRight:
340 label = QgsGeometry::fromPointXY( QgsPointXY( rect.topRight() ) );
341 break;
342 }
343
344 label.rotate( angle, rect.topLeft() );
345 return label;
346}
347
348QgsGeometry QgsCallout::calloutLabelPoint( const QRectF &rect, const double angle, QgsCallout::LabelAnchorPoint anchor, QgsRenderContext &context, const QgsCallout::QgsCalloutContext &calloutContext, bool &pinned ) const
349{
350 pinned = false;
352 {
353 bool ok = false;
355 if ( ok )
356 {
358 if ( ok )
359 {
360 pinned = true;
361 // data defined label point, use it directly
362 QgsGeometry labelPoint = QgsGeometry::fromPointXY( QgsPointXY( x, y ) );
363 try
364 {
365 labelPoint.transform( calloutContext.originalFeatureToMapTransform( context ) );
366 labelPoint.transform( context.mapToPixel().transform() );
367 }
368 catch ( QgsCsException & )
369 {
370 return QgsGeometry();
371 }
372 return labelPoint;
373 }
374 }
375 }
376
377 QgsGeometry label;
378 switch ( anchor )
379 {
381 label = QgsGeometry::fromRect( rect );
382 break;
383
384 case LabelCentroid:
385 label = QgsGeometry::fromRect( rect ).centroid();
386 break;
387
388 case LabelTopLeft:
389 label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomLeft() ) );
390 break;
391
392 case LabelTopMiddle:
393 label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.bottom() ) );
394 break;
395
396 case LabelTopRight:
397 label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomRight() ) );
398 break;
399
400 case LabelMiddleLeft:
401 label = QgsGeometry::fromPointXY( QgsPointXY( rect.left(), ( rect.top() + rect.bottom() ) / 2.0 ) );
402 break;
403
404 case LabelMiddleRight:
405 label = QgsGeometry::fromPointXY( QgsPointXY( rect.right(), ( rect.top() + rect.bottom() ) / 2.0 ) );
406 break;
407
408 case LabelBottomLeft:
409 label = QgsGeometry::fromPointXY( QgsPointXY( rect.topLeft() ) );
410 break;
411
413 label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.top() ) );
414 break;
415
416 case LabelBottomRight:
417 label = QgsGeometry::fromPointXY( QgsPointXY( rect.topRight() ) );
418 break;
419 }
420
421 label.rotate( angle, rect.topLeft() );
422 return label;
423}
424
425QgsGeometry QgsCallout::calloutLineToPart( const QgsGeometry &labelGeometry, const QgsAbstractGeometry *partGeometry, QgsRenderContext &context, const QgsCalloutContext &calloutContext, bool &pinned ) const
426{
427 pinned = false;
428 AnchorPoint anchor = anchorPoint();
429 const QgsAbstractGeometry *evaluatedPartAnchor = partGeometry;
430 std::unique_ptr< QgsAbstractGeometry > tempPartAnchor;
431
433 {
434 bool ok = false;
436 if ( ok )
437 {
439 if ( ok )
440 {
441 pinned = true;
442 tempPartAnchor = std::make_unique< QgsPoint >( Qgis::WkbType::Point, x, y );
443 evaluatedPartAnchor = tempPartAnchor.get();
444 try
445 {
446 tempPartAnchor->transform( calloutContext.originalFeatureToMapTransform( context ) );
447 tempPartAnchor->transform( context.mapToPixel().transform() );
448 }
449 catch ( QgsCsException & )
450 {
451 evaluatedPartAnchor = partGeometry;
452 }
453 }
454 }
455 }
456
458 {
459 const QString encodedAnchor = encodeAnchorPoint( anchor );
460 context.expressionContext().setOriginalValueVariable( encodedAnchor );
461 anchor = decodeAnchorPoint( dataDefinedProperties().valueAsString( QgsCallout::Property::AnchorPointPosition, context.expressionContext(), encodedAnchor ) );
462 }
463
464 QgsGeometry line;
465 const QgsGeos labelGeos( labelGeometry.constGet() );
466
467 switch ( QgsWkbTypes::geometryType( evaluatedPartAnchor->wkbType() ) )
468 {
471 {
472 line = QgsGeometry( labelGeos.shortestLine( evaluatedPartAnchor ) );
473 break;
474 }
475
477 {
478 if ( labelGeos.intersects( evaluatedPartAnchor ) )
479 return QgsGeometry();
480
481 // ideally avoid this unwanted clone in future. For now we need it because poleOfInaccessibility/pointOnSurface are
482 // only available to QgsGeometry objects
483 const QgsGeometry evaluatedPartAnchorGeom( evaluatedPartAnchor->clone() );
484 switch ( anchor )
485 {
487 line = QgsGeometry( labelGeos.shortestLine( evaluatedPartAnchorGeom.poleOfInaccessibility( std::max( evaluatedPartAnchor->boundingBox().width(), evaluatedPartAnchor->boundingBox().height() ) / 20.0 ) ) ); // really rough (but quick) pole of inaccessibility
488 break;
490 line = QgsGeometry( labelGeos.shortestLine( evaluatedPartAnchorGeom.pointOnSurface() ) );
491 break;
493 line = QgsGeometry( labelGeos.shortestLine( evaluatedPartAnchor ) );
494 break;
496 line = QgsGeometry( labelGeos.shortestLine( evaluatedPartAnchorGeom.centroid() ) );
497 break;
498 }
499 break;
500 }
501
504 return QgsGeometry(); // shouldn't even get here..
505 }
506 return line;
507}
508
509//
510// QgsCallout::QgsCalloutContext
511//
512
514{
515 if ( !mOriginalFeatureToMapTransform.isValid() )
516 {
517 // lazy initialization, only create if needed...
518 mOriginalFeatureToMapTransform = QgsCoordinateTransform( originalFeatureCrs, renderContext.coordinateTransform().destinationCrs(), renderContext.transformContext() );
519 }
520 return mOriginalFeatureToMapTransform;
521}
522
523
524//
525// QgsSimpleLineCallout
526//
527
529{
530 mLineSymbol = std::make_unique< QgsLineSymbol >( QgsSymbolLayerList() << new QgsSimpleLineSymbolLayer( QColor( 60, 60, 60 ), .3 ) );
531
532}
533
535
537 : QgsCallout( other )
538 , mLineSymbol( other.mLineSymbol ? other.mLineSymbol->clone() : nullptr )
539 , mMinCalloutLength( other.mMinCalloutLength )
540 , mMinCalloutLengthUnit( other.mMinCalloutLengthUnit )
541 , mMinCalloutLengthScale( other.mMinCalloutLengthScale )
542 , mOffsetFromAnchorDistance( other.mOffsetFromAnchorDistance )
543 , mOffsetFromAnchorUnit( other.mOffsetFromAnchorUnit )
544 , mOffsetFromAnchorScale( other.mOffsetFromAnchorScale )
545 , mOffsetFromLabelDistance( other.mOffsetFromLabelDistance )
546 , mOffsetFromLabelUnit( other.mOffsetFromLabelUnit )
547 , mOffsetFromLabelScale( other.mOffsetFromLabelScale )
548 , mDrawCalloutToAllParts( other.mDrawCalloutToAllParts )
549{
550
551}
552
553QgsCallout *QgsSimpleLineCallout::create( const QVariantMap &properties, const QgsReadWriteContext &context )
554{
555 auto callout = std::make_unique< QgsSimpleLineCallout >();
556 callout->readProperties( properties, context );
557 return callout.release();
558}
559
561{
562 return QStringLiteral( "simple" );
563}
564
569
570QVariantMap QgsSimpleLineCallout::properties( const QgsReadWriteContext &context ) const
571{
572 QVariantMap props = QgsCallout::properties( context );
573
574 if ( mLineSymbol )
575 {
576 props[ QStringLiteral( "lineSymbol" ) ] = QgsSymbolLayerUtils::symbolProperties( mLineSymbol.get() );
577 }
578 props[ QStringLiteral( "minLength" ) ] = mMinCalloutLength;
579 props[ QStringLiteral( "minLengthUnit" ) ] = QgsUnitTypes::encodeUnit( mMinCalloutLengthUnit );
580 props[ QStringLiteral( "minLengthMapUnitScale" ) ] = QgsSymbolLayerUtils::encodeMapUnitScale( mMinCalloutLengthScale );
581
582 props[ QStringLiteral( "offsetFromAnchor" ) ] = mOffsetFromAnchorDistance;
583 props[ QStringLiteral( "offsetFromAnchorUnit" ) ] = QgsUnitTypes::encodeUnit( mOffsetFromAnchorUnit );
584 props[ QStringLiteral( "offsetFromAnchorMapUnitScale" ) ] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetFromAnchorScale );
585 props[ QStringLiteral( "offsetFromLabel" ) ] = mOffsetFromLabelDistance;
586 props[ QStringLiteral( "offsetFromLabelUnit" ) ] = QgsUnitTypes::encodeUnit( mOffsetFromLabelUnit );
587 props[ QStringLiteral( "offsetFromLabelMapUnitScale" ) ] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetFromLabelScale );
588
589 props[ QStringLiteral( "drawToAllParts" ) ] = mDrawCalloutToAllParts;
590
591 return props;
592}
593
594void QgsSimpleLineCallout::readProperties( const QVariantMap &props, const QgsReadWriteContext &context )
595{
596 QgsCallout::readProperties( props, context );
597
598 const QString lineSymbolDef = props.value( QStringLiteral( "lineSymbol" ) ).toString();
599 QDomDocument doc( QStringLiteral( "symbol" ) );
600 doc.setContent( lineSymbolDef );
601 const QDomElement symbolElem = doc.firstChildElement( QStringLiteral( "symbol" ) );
602 std::unique_ptr< QgsLineSymbol > lineSymbol( QgsSymbolLayerUtils::loadSymbol< QgsLineSymbol >( symbolElem, context ) );
603 if ( lineSymbol )
604 mLineSymbol = std::move( lineSymbol );
605
606 mMinCalloutLength = props.value( QStringLiteral( "minLength" ), 0 ).toDouble();
607 mMinCalloutLengthUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "minLengthUnit" ) ).toString() );
608 mMinCalloutLengthScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "minLengthMapUnitScale" ) ).toString() );
609
610 mOffsetFromAnchorDistance = props.value( QStringLiteral( "offsetFromAnchor" ), 0 ).toDouble();
611 mOffsetFromAnchorUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "offsetFromAnchorUnit" ) ).toString() );
612 mOffsetFromAnchorScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "offsetFromAnchorMapUnitScale" ) ).toString() );
613 mOffsetFromLabelDistance = props.value( QStringLiteral( "offsetFromLabel" ), 0 ).toDouble();
614 mOffsetFromLabelUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "offsetFromLabelUnit" ) ).toString() );
615 mOffsetFromLabelScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "offsetFromLabelMapUnitScale" ) ).toString() );
616
617 mDrawCalloutToAllParts = props.value( QStringLiteral( "drawToAllParts" ), false ).toBool();
618}
619
621{
622 QgsCallout::startRender( context );
623 if ( mLineSymbol )
624 mLineSymbol->startRender( context );
625}
626
628{
629 QgsCallout::stopRender( context );
630 if ( mLineSymbol )
631 mLineSymbol->stopRender( context );
632}
633
634QSet<QString> QgsSimpleLineCallout::referencedFields( const QgsRenderContext &context ) const
635{
636 QSet<QString> fields = QgsCallout::referencedFields( context );
637 if ( mLineSymbol )
638 fields.unite( mLineSymbol->usedAttributes( context ) );
639 return fields;
640}
641
643{
644 return mLineSymbol.get();
645}
646
648{
649 mLineSymbol.reset( symbol );
650}
651
652void QgsSimpleLineCallout::draw( QgsRenderContext &context, const QRectF &rect, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext )
653{
654 LabelAnchorPoint labelAnchor = labelAnchorPoint();
656 {
657 const QString encodedAnchor = encodeLabelAnchorPoint( labelAnchor );
658 context.expressionContext().setOriginalValueVariable( encodedAnchor );
659 labelAnchor = decodeLabelAnchorPoint( dataDefinedProperties().valueAsString( QgsCallout::Property::LabelAnchorPointPosition, context.expressionContext(), encodedAnchor ) );
660 }
661
662 bool originPinned = false;
663 const QgsGeometry label = calloutLabelPoint( rect, angle, labelAnchor, context, calloutContext, originPinned );
664 if ( label.isNull() )
665 return;
666
667 auto drawCalloutLine = [this, &context, &calloutContext, &label, &rect, angle, &anchor, originPinned]( const QgsAbstractGeometry * partAnchor )
668 {
669 bool destinationPinned = false;
670 const QgsGeometry line = calloutLineToPart( label, partAnchor, context, calloutContext, destinationPinned );
671 if ( line.isEmpty() )
672 return;
673
674 const double lineLength = line.length();
675 if ( qgsDoubleNear( lineLength, 0 ) )
676 return;
677
678 double minLength = mMinCalloutLength;
680 {
681 context.expressionContext().setOriginalValueVariable( minLength );
683 }
684 const double minLengthPixels = context.convertToPainterUnits( minLength, mMinCalloutLengthUnit, mMinCalloutLengthScale );
685 if ( minLengthPixels > 0 && lineLength < minLengthPixels )
686 return; // too small!
687
688 std::unique_ptr< QgsCurve > calloutCurve( createCalloutLine( qgsgeometry_cast< const QgsLineString * >( line.constGet() )->startPoint(),
689 qgsgeometry_cast< const QgsLineString * >( line.constGet() )->endPoint(), context, rect, angle, anchor, calloutContext ) );
690
691 double offsetFromAnchor = mOffsetFromAnchorDistance;
693 {
696 }
697 const double offsetFromAnchorPixels = context.convertToPainterUnits( offsetFromAnchor, mOffsetFromAnchorUnit, mOffsetFromAnchorScale );
698
699 double offsetFromLabel = mOffsetFromLabelDistance;
701 {
704 }
705 const double offsetFromLabelPixels = context.convertToPainterUnits( offsetFromLabel, mOffsetFromLabelUnit, mOffsetFromLabelScale );
706 if ( offsetFromAnchorPixels > 0 || offsetFromLabelPixels > 0 )
707 {
708 calloutCurve.reset( calloutCurve->curveSubstring( offsetFromLabelPixels, calloutCurve->length() - offsetFromAnchorPixels ) );
709 }
710
711 const QPolygonF points = calloutCurve->asQPolygonF();
712
713 if ( points.empty() )
714 return;
715
716 QgsCalloutPosition position;
717 position.setOrigin( context.mapToPixel().toMapCoordinates( points.at( 0 ).x(), points.at( 0 ).y() ).toQPointF() );
718 position.setOriginIsPinned( originPinned );
719 position.setDestination( context.mapToPixel().toMapCoordinates( points.constLast().x(), points.constLast().y() ).toQPointF() );
720 position.setDestinationIsPinned( destinationPinned );
721 calloutContext.addCalloutPosition( position );
722
723 mLineSymbol->renderPolyline( points, nullptr, context );
724 };
725
726 bool toAllParts = mDrawCalloutToAllParts;
728 {
729 context.expressionContext().setOriginalValueVariable( toAllParts );
731 }
732
733 if ( calloutContext.allFeaturePartsLabeled || !toAllParts )
734 drawCalloutLine( anchor.constGet() );
735 else
736 {
737 for ( auto it = anchor.const_parts_begin(); it != anchor.const_parts_end(); ++it )
738 drawCalloutLine( *it );
739 }
740}
741
742QgsCurve *QgsSimpleLineCallout::createCalloutLine( const QgsPoint &start, const QgsPoint &end, QgsRenderContext &, const QRectF &, const double, const QgsGeometry &, QgsCallout::QgsCalloutContext & ) const
743{
744 return new QgsLineString( start, end );
745}
746
747//
748// QgsManhattanLineCallout
749//
750
754
760
761
762QgsCallout *QgsManhattanLineCallout::create( const QVariantMap &properties, const QgsReadWriteContext &context ) // cppcheck-suppress duplInheritedMember
763{
764 auto callout = std::make_unique< QgsManhattanLineCallout >();
765 callout->readProperties( properties, context );
766 return callout.release();
767}
768
770{
771 return QStringLiteral( "manhattan" );
772}
773
778
779QgsCurve *QgsManhattanLineCallout::createCalloutLine( const QgsPoint &start, const QgsPoint &end, QgsRenderContext &, const QRectF &, const double, const QgsGeometry &, QgsCallout::QgsCalloutContext & ) const
780{
781 const QgsPoint mid1 = QgsPoint( start.x(), end.y() );
782 return new QgsLineString( QVector< QgsPoint >() << start << mid1 << end );
783}
784
785
786//
787// QgsCurvedLineCallout
788//
789
793
795 : QgsSimpleLineCallout( other )
796 , mOrientation( other.mOrientation )
797 , mCurvature( other.mCurvature )
798{
799
800}
801
802QgsCallout *QgsCurvedLineCallout::create( const QVariantMap &properties, const QgsReadWriteContext &context ) // cppcheck-suppress duplInheritedMember
803{
804 auto callout = std::make_unique< QgsCurvedLineCallout >();
805 callout->readProperties( properties, context );
806
807 callout->setCurvature( properties.value( QStringLiteral( "curvature" ), 0.1 ).toDouble() );
808 callout->setOrientation( decodeOrientation( properties.value( QStringLiteral( "orientation" ), QStringLiteral( "auto" ) ).toString() ) );
809
810 return callout.release();
811}
812
814{
815 return QStringLiteral( "curved" );
816}
817
822
823QVariantMap QgsCurvedLineCallout::properties( const QgsReadWriteContext &context ) const
824{
825 QVariantMap props = QgsSimpleLineCallout::properties( context );
826 props.insert( QStringLiteral( "curvature" ), mCurvature );
827 props.insert( QStringLiteral( "orientation" ), encodeOrientation( mOrientation ) );
828 return props;
829}
830
831QgsCurve *QgsCurvedLineCallout::createCalloutLine( const QgsPoint &start, const QgsPoint &end, QgsRenderContext &context, const QRectF &rect, const double, const QgsGeometry &, QgsCallout::QgsCalloutContext & ) const
832{
833 double curvature = mCurvature * 100;
835 {
838 }
839
840 Orientation orientation = mOrientation;
842 {
843 bool ok = false;
844 const QString orientationString = dataDefinedProperties().property( QgsCallout::Property::Orientation ).valueAsString( context.expressionContext(), QString(), &ok );
845 if ( ok )
846 {
847 orientation = decodeOrientation( orientationString );
848 }
849 }
850
851 if ( orientation == Automatic )
852 {
853 // to calculate automatically the best curve orientation, we first check which side of the label bounding box
854 // the callout origin is nearest to
855 switch ( QgsGeometryUtilsBase::closestSideOfRectangle( rect.right(), rect.bottom(), rect.left(), rect.top(), start.x(), start.y() ) )
856 {
857 case 1:
858 // closest to bottom
859 if ( qgsDoubleNear( end.x(), start.x() ) )
860 {
861 // if vertical line, we bend depending on whether the line sits towards the left or right side of the label
862 if ( start.x() < ( rect.left() + 0.5 * rect.width() ) )
864 else
866 }
867 else if ( end.x() > start.x() )
869 else
871 break;
872
873 case 2:
874 // closest to bottom-right
875 if ( end.x() < start.x() )
877 else if ( end.y() < start.y() )
879 else if ( end.x() - start.x() < end.y() - start.y() )
881 else
883 break;
884
885 case 3:
886 // closest to right
887 if ( qgsDoubleNear( end.y(), start.y() ) )
888 {
889 // if horizontal line, we bend depending on whether the line sits towards the top or bottom side of the label
890 if ( start.y() < ( rect.top() + 0.5 * rect.height() ) )
892 else
894 }
895 else if ( end.y() < start.y() )
897 else
899 break;
900
901 case 4:
902 // closest to top-right
903 if ( end.x() < start.x() )
905 else if ( end.y() > start.y() )
907 else if ( end.x() - start.x() < start.y() - end.y() )
909 else
911 break;
912
913 case 5:
914 // closest to top
915 if ( qgsDoubleNear( end.x(), start.x() ) )
916 {
917 // if vertical line, we bend depending on whether the line sits towards the left or right side of the label
918 if ( start.x() < ( rect.left() + 0.5 * rect.width() ) )
920 else
922 }
923 else if ( end.x() < start.x() )
925 else
927 break;
928
929 case 6:
930 // closest to top-left
931 if ( end.x() > start.x() )
933 else if ( end.y() > start.y() )
935 else if ( start.x() - end.x() < start.y() - end.y() )
937 else
939 break;
940
941 case 7:
942 //closest to left
943 if ( qgsDoubleNear( end.y(), start.y() ) )
944 {
945 // if horizontal line, we bend depending on whether the line sits towards the top or bottom side of the label
946 if ( start.y() < ( rect.top() + 0.5 * rect.height() ) )
948 else
950 }
951 else if ( end.y() > start.y() )
953 else
955 break;
956
957 case 8:
958 //closest to bottom-left
959 if ( end.x() > start.x() )
961 else if ( end.y() < start.y() )
963 else if ( start.x() - end.x() < end.y() - start.y() )
965 else
967 break;
968 }
969 }
970
971 // turn the line into a curved line. We do this by creating a circular string from the callout line's
972 // start to end point, where the curve point is in the middle of the callout line and perpendicularly offset
973 // by a proportion of the overall callout line length
974 const double distance = ( orientation == Clockwise ? 1 : -1 ) * start.distance( end ) * curvature / 100.0;
975 double midX, midY;
976 QgsGeometryUtilsBase::perpendicularOffsetPointAlongSegment( start.x(), start.y(), end.x(), end.y(), 0.5, distance, &midX, &midY );
977
978 return new QgsCircularString( start, QgsPoint( midX, midY ), end );
979}
980
981QgsCurvedLineCallout::Orientation QgsCurvedLineCallout::decodeOrientation( const QString &string )
982{
983 const QString cleaned = string.toLower().trimmed();
984 if ( cleaned == QLatin1String( "auto" ) )
985 return Automatic;
986 if ( cleaned == QLatin1String( "clockwise" ) )
987 return Clockwise;
988 if ( cleaned == QLatin1String( "counterclockwise" ) )
989 return CounterClockwise;
990 return Automatic;
991}
992
993QString QgsCurvedLineCallout::encodeOrientation( QgsCurvedLineCallout::Orientation orientation )
994{
995 switch ( orientation )
996 {
998 return QStringLiteral( "auto" );
1000 return QStringLiteral( "clockwise" );
1002 return QStringLiteral( "counterclockwise" );
1003 }
1004 return QString();
1005}
1006
1008{
1009 return mOrientation;
1010}
1011
1013{
1014 mOrientation = orientation;
1015}
1016
1018{
1019 return mCurvature;
1020}
1021
1023{
1024 mCurvature = curvature;
1025}
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
1057}
1058
1059QgsCallout *QgsBalloonCallout::create( const QVariantMap &properties, const QgsReadWriteContext &context )
1060{
1061 auto callout = std::make_unique< QgsBalloonCallout >();
1062 callout->readProperties( properties, context );
1063 return callout.release();
1064}
1065
1067{
1068 return QStringLiteral( "balloon" );
1069}
1070
1072{
1073 return new QgsBalloonCallout( *this );
1074}
1075
1076QVariantMap QgsBalloonCallout::properties( const QgsReadWriteContext &context ) const
1077{
1078 QVariantMap props = QgsCallout::properties( context );
1079
1080 if ( mFillSymbol )
1081 {
1082 props[ QStringLiteral( "fillSymbol" ) ] = QgsSymbolLayerUtils::symbolProperties( mFillSymbol.get() );
1083 }
1084
1085 if ( mMarkerSymbol )
1086 {
1087 props[ QStringLiteral( "markerSymbol" ) ] = QgsSymbolLayerUtils::symbolProperties( mMarkerSymbol.get() );
1088 }
1089
1090 props[ QStringLiteral( "offsetFromAnchor" ) ] = mOffsetFromAnchorDistance;
1091 props[ QStringLiteral( "offsetFromAnchorUnit" ) ] = QgsUnitTypes::encodeUnit( mOffsetFromAnchorUnit );
1092 props[ QStringLiteral( "offsetFromAnchorMapUnitScale" ) ] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetFromAnchorScale );
1093
1094 props[ QStringLiteral( "margins" ) ] = mMargins.toString();
1095 props[ QStringLiteral( "marginsUnit" ) ] = QgsUnitTypes::encodeUnit( mMarginUnit );
1096
1097 props[ QStringLiteral( "wedgeWidth" ) ] = mWedgeWidth;
1098 props[ QStringLiteral( "wedgeWidthUnit" ) ] = QgsUnitTypes::encodeUnit( mWedgeWidthUnit );
1099 props[ QStringLiteral( "wedgeWidthMapUnitScale" ) ] = QgsSymbolLayerUtils::encodeMapUnitScale( mWedgeWidthScale );
1100
1101 props[ QStringLiteral( "cornerRadius" ) ] = mCornerRadius;
1102 props[ QStringLiteral( "cornerRadiusUnit" ) ] = QgsUnitTypes::encodeUnit( mCornerRadiusUnit );
1103 props[ QStringLiteral( "cornerRadiusMapUnitScale" ) ] = QgsSymbolLayerUtils::encodeMapUnitScale( mCornerRadiusScale );
1104
1105 return props;
1106}
1107
1108void QgsBalloonCallout::readProperties( const QVariantMap &props, const QgsReadWriteContext &context )
1109{
1110 QgsCallout::readProperties( props, context );
1111
1112 {
1113 const QString fillSymbolDef = props.value( QStringLiteral( "fillSymbol" ) ).toString();
1114 QDomDocument doc( QStringLiteral( "symbol" ) );
1115 doc.setContent( fillSymbolDef );
1116 const QDomElement symbolElem = doc.firstChildElement( QStringLiteral( "symbol" ) );
1117 std::unique_ptr< QgsFillSymbol > fillSymbol( QgsSymbolLayerUtils::loadSymbol< QgsFillSymbol >( symbolElem, context ) );
1118 if ( fillSymbol )
1119 mFillSymbol = std::move( fillSymbol );
1120 }
1121
1122 {
1123 const QString markerSymbolDef = props.value( QStringLiteral( "markerSymbol" ) ).toString();
1124 QDomDocument doc( QStringLiteral( "symbol" ) );
1125 doc.setContent( markerSymbolDef );
1126 const QDomElement symbolElem = doc.firstChildElement( QStringLiteral( "symbol" ) );
1127 std::unique_ptr< QgsMarkerSymbol > markerSymbol( QgsSymbolLayerUtils::loadSymbol< QgsMarkerSymbol >( symbolElem, context ) );
1128 if ( markerSymbol )
1129 mMarkerSymbol = std::move( markerSymbol );
1130 }
1131
1132 mOffsetFromAnchorDistance = props.value( QStringLiteral( "offsetFromAnchor" ), 0 ).toDouble();
1133 mOffsetFromAnchorUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "offsetFromAnchorUnit" ) ).toString() );
1134 mOffsetFromAnchorScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "offsetFromAnchorMapUnitScale" ) ).toString() );
1135
1136 mMargins = QgsMargins::fromString( props.value( QStringLiteral( "margins" ) ).toString() );
1137 mMarginUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "marginsUnit" ) ).toString() );
1138
1139 mWedgeWidth = props.value( QStringLiteral( "wedgeWidth" ), 2.64 ).toDouble();
1140 mWedgeWidthUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "wedgeWidthUnit" ) ).toString() );
1141 mWedgeWidthScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "wedgeWidthMapUnitScale" ) ).toString() );
1142
1143 mCornerRadius = props.value( QStringLiteral( "cornerRadius" ), 0 ).toDouble();
1144 mCornerRadiusUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "cornerRadiusUnit" ) ).toString() );
1145 mCornerRadiusScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "cornerRadiusMapUnitScale" ) ).toString() );
1146}
1147
1149{
1150 QgsCallout::startRender( context );
1151 if ( mFillSymbol )
1152 mFillSymbol->startRender( context );
1153 if ( mMarkerSymbol )
1154 mMarkerSymbol->startRender( context );
1155}
1156
1158{
1159 QgsCallout::stopRender( context );
1160 if ( mFillSymbol )
1161 mFillSymbol->stopRender( context );
1162 if ( mMarkerSymbol )
1163 mMarkerSymbol->stopRender( context );
1164}
1165
1166QSet<QString> QgsBalloonCallout::referencedFields( const QgsRenderContext &context ) const
1167{
1168 QSet<QString> fields = QgsCallout::referencedFields( context );
1169 if ( mFillSymbol )
1170 fields.unite( mFillSymbol->usedAttributes( context ) );
1171 if ( mMarkerSymbol )
1172 fields.unite( mMarkerSymbol->usedAttributes( context ) );
1173 return fields;
1174}
1175
1177{
1178 return mFillSymbol.get();
1179}
1180
1182{
1183 mFillSymbol.reset( symbol );
1184}
1185
1187{
1188 return mMarkerSymbol.get();
1189}
1190
1192{
1193 mMarkerSymbol.reset( symbol );
1194}
1195
1196void QgsBalloonCallout::draw( QgsRenderContext &context, const QRectF &rect, const double, const QgsGeometry &anchor, QgsCalloutContext &calloutContext )
1197{
1198 bool destinationIsPinned = false;
1199 QgsGeometry line = calloutLineToPart( QgsGeometry::fromRect( rect ), anchor.constGet(), context, calloutContext, destinationIsPinned );
1200
1201 if ( mMarkerSymbol )
1202 {
1203 if ( const QgsLineString *ls = qgsgeometry_cast< const QgsLineString * >( line.constGet() ) )
1204 {
1205 QgsPoint anchorPoint = ls->endPoint();
1206 mMarkerSymbol->renderPoint( anchorPoint.toQPointF(), nullptr, context );
1207 }
1208 }
1209
1210 double offsetFromAnchor = mOffsetFromAnchorDistance;
1212 {
1215 }
1216 const double offsetFromAnchorPixels = context.convertToPainterUnits( offsetFromAnchor, mOffsetFromAnchorUnit, mOffsetFromAnchorScale );
1217
1218 if ( offsetFromAnchorPixels > 0 )
1219 {
1220 if ( const QgsLineString *ls = qgsgeometry_cast< const QgsLineString * >( line.constGet() ) )
1221 {
1222 line = QgsGeometry( ls->curveSubstring( 0, ls->length() - offsetFromAnchorPixels ) );
1223 }
1224 }
1225
1226 QgsPointXY destination;
1227 QgsPointXY origin;
1228 if ( const QgsLineString *ls = qgsgeometry_cast< const QgsLineString * >( line.constGet() ) )
1229 {
1230 origin = ls->startPoint();
1231 destination = ls->endPoint();
1232 }
1233 else
1234 {
1235 destination = QgsPointXY( rect.center() );
1236 }
1237
1238 const QPolygonF points = getPoints( context, destination, rect );
1239 if ( points.empty() )
1240 return;
1241
1242 if ( !origin.isEmpty() )
1243 {
1244 QgsCalloutPosition position;
1245 position.setOrigin( context.mapToPixel().toMapCoordinates( origin.x(), origin.y() ).toQPointF() );
1246 position.setOriginIsPinned( false );
1247 position.setDestination( context.mapToPixel().toMapCoordinates( destination.x(), destination.y() ).toQPointF() );
1248 position.setDestinationIsPinned( destinationIsPinned );
1249 calloutContext.addCalloutPosition( position );
1250 }
1251
1252 mFillSymbol->renderPolygon( points, nullptr, nullptr, context );
1253}
1254
1255QPolygonF QgsBalloonCallout::getPoints( QgsRenderContext &context, QgsPointXY origin, QRectF rect ) const
1256{
1257 double segmentPointWidth = mWedgeWidth;
1259 {
1260 context.expressionContext().setOriginalValueVariable( segmentPointWidth );
1261 segmentPointWidth = dataDefinedProperties().valueAsDouble( QgsCallout::Property::WedgeWidth, context.expressionContext(), segmentPointWidth );
1262 }
1263 segmentPointWidth = context.convertToPainterUnits( segmentPointWidth, mWedgeWidthUnit, mWedgeWidthScale );
1264
1265 double cornerRadius = mCornerRadius;
1267 {
1270 }
1271 cornerRadius = context.convertToPainterUnits( cornerRadius, mCornerRadiusUnit, mCornerRadiusScale );
1272
1273 double left = mMargins.left();
1274 double right = mMargins.right();
1275 double top = mMargins.top();
1276 double bottom = mMargins.bottom();
1277
1279 {
1280 const QVariant value = dataDefinedProperties().value( QgsCallout::Property::Margins, context.expressionContext() );
1281 if ( !QgsVariantUtils::isNull( value ) )
1282 {
1283 if ( value.userType() == QMetaType::Type::QVariantList )
1284 {
1285 const QVariantList list = value.toList();
1286 if ( list.size() == 4 )
1287 {
1288 bool topOk = false;
1289 bool rightOk = false;
1290 bool bottomOk = false;
1291 bool leftOk = false;
1292 const double evaluatedTop = list.at( 0 ).toDouble( &topOk );
1293 const double evaluatedRight = list.at( 1 ).toDouble( &rightOk );
1294 const double evaluatedBottom = list.at( 2 ).toDouble( &bottomOk );
1295 const double evaluatedLeft = list.at( 3 ).toDouble( &leftOk );
1296 if ( topOk && rightOk && bottomOk && leftOk )
1297 {
1298 left = evaluatedLeft;
1299 top = evaluatedTop;
1300 right = evaluatedRight;
1301 bottom = evaluatedBottom;
1302 }
1303 }
1304 }
1305 else
1306 {
1307 const QStringList list = value.toString().trimmed().split( ',' );
1308 if ( list.count() == 4 )
1309 {
1310 bool topOk = false;
1311 bool rightOk = false;
1312 bool bottomOk = false;
1313 bool leftOk = false;
1314 const double evaluatedTop = list.at( 0 ).toDouble( &topOk );
1315 const double evaluatedRight = list.at( 1 ).toDouble( &rightOk );
1316 const double evaluatedBottom = list.at( 2 ).toDouble( &bottomOk );
1317 const double evaluatedLeft = list.at( 3 ).toDouble( &leftOk );
1318 if ( topOk && rightOk && bottomOk && leftOk )
1319 {
1320 left = evaluatedLeft;
1321 top = evaluatedTop;
1322 right = evaluatedRight;
1323 bottom = evaluatedBottom;
1324 }
1325 }
1326 }
1327 }
1328 }
1329
1330 const double marginLeft = context.convertToPainterUnits( left, mMarginUnit );
1331 const double marginRight = context.convertToPainterUnits( right, mMarginUnit );
1332 const double marginTop = context.convertToPainterUnits( top, mMarginUnit );
1333 const double marginBottom = context.convertToPainterUnits( bottom, mMarginUnit );
1334
1335 const QRectF expandedRect = rect.height() < 0 ?
1336 QRectF( rect.left() - marginLeft, rect.top() + marginBottom,
1337 rect.width() + marginLeft + marginRight,
1338 rect.height() - marginTop - marginBottom ) :
1339 QRectF( rect.left() - marginLeft, rect.top() - marginTop,
1340 rect.width() + marginLeft + marginRight,
1341 rect.height() + marginTop + marginBottom );
1342
1343 // IMPORTANT -- check for degenerate height is sometimes >=0, because QRectF are not normalized and we are using painter
1344 // coordinates with descending vertical axis!
1345 if ( expandedRect.width() <= 0 || ( rect.height() < 0 && expandedRect.height() >= 0 ) || ( rect.height() > 0 && expandedRect.height() <= 0 ) )
1346 return QPolygonF();
1347
1348 const QPainterPath path = QgsShapeGenerator::createBalloon( origin, expandedRect, segmentPointWidth, cornerRadius );
1349 const QTransform t = QTransform::fromScale( 100, 100 );
1350 const QTransform ti = t.inverted();
1351 const QPolygonF poly = path.toFillPolygon( t );
1352 return ti.map( poly );
1353}
BlendMode
Blending modes defining the available composition modes that can be used when painting.
Definition qgis.h:4776
@ 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.
Handles coordinate transforms between two 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:3117
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.
Represents 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:387
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.
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.
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:6302
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