QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
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 "qgsunittypes.h"
35
36#include <mutex>
37
38QgsPropertiesDefinition QgsCallout::sPropertyDefinitions;
39
40void QgsCallout::initPropertyDefinitions()
41{
42 const QString origin = QStringLiteral( "callouts" );
43
44 sPropertyDefinitions = QgsPropertiesDefinition
45 {
46 { QgsCallout::MinimumCalloutLength, QgsPropertyDefinition( "MinimumCalloutLength", QObject::tr( "Minimum callout length" ), QgsPropertyDefinition::DoublePositive, origin ) },
47 { QgsCallout::OffsetFromAnchor, QgsPropertyDefinition( "OffsetFromAnchor", QObject::tr( "Offset from feature" ), QgsPropertyDefinition::DoublePositive, origin ) },
48 { QgsCallout::OffsetFromLabel, QgsPropertyDefinition( "OffsetFromLabel", QObject::tr( "Offset from label" ), QgsPropertyDefinition::DoublePositive, origin ) },
49 { QgsCallout::DrawCalloutToAllParts, QgsPropertyDefinition( "DrawCalloutToAllParts", QObject::tr( "Draw lines to all feature parts" ), QgsPropertyDefinition::Boolean, origin ) },
50 { QgsCallout::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 ) },
51 {
52 QgsCallout::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|"
53 "<b>TR</b>=Top right|<br>"
54 "<b>L</b>=Left|<b>R</b>=Right|<br>"
55 "<b>BL</b>=Bottom left|<b>B</b>=Bottom middle|"
56 "<b>BR</b>=Bottom right]", origin )
57 },
58 { QgsCallout::OriginX, QgsPropertyDefinition( "OriginX", QObject::tr( "Callout origin (X)" ), QgsPropertyDefinition::Double, origin ) },
59 { QgsCallout::OriginY, QgsPropertyDefinition( "OriginY", QObject::tr( "Callout origin (Y)" ), QgsPropertyDefinition::Double, origin ) },
60 { QgsCallout::DestinationX, QgsPropertyDefinition( "DestinationX", QObject::tr( "Callout destination (X)" ), QgsPropertyDefinition::Double, origin ) },
61 { QgsCallout::DestinationY, QgsPropertyDefinition( "DestinationY", QObject::tr( "Callout destination (Y)" ), QgsPropertyDefinition::Double, origin ) },
62 { QgsCallout::Curvature, QgsPropertyDefinition( "Curvature", QObject::tr( "Callout line curvature" ), QgsPropertyDefinition::Double, origin ) },
63 {
64 QgsCallout::Orientation, QgsPropertyDefinition( "Orientation", QgsPropertyDefinition::DataTypeString, QObject::tr( "Callout curve orientation" ), QObject::tr( "string " ) + "[<b>auto</b>|<b>clockwise</b>|<b>counterclockwise</b>]", origin )
65 },
66 {
67 QgsCallout::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>" ) )
68 },
69 { QgsCallout::WedgeWidth, QgsPropertyDefinition( "WedgeWidth", QObject::tr( "Wedge width" ), QgsPropertyDefinition::DoublePositive, origin ) },
70 { QgsCallout::CornerRadius, QgsPropertyDefinition( "CornerRadius", QObject::tr( "Corner radius" ), QgsPropertyDefinition::DoublePositive, origin ) },
71 { QgsCallout::BlendMode, QgsPropertyDefinition( "BlendMode", QObject::tr( "Callout blend mode" ), QgsPropertyDefinition::BlendMode, origin ) },
72 };
73}
74
75
77{
78}
79
80QVariantMap QgsCallout::properties( const QgsReadWriteContext & ) const
81{
82 QVariantMap props;
83 props.insert( QStringLiteral( "enabled" ), mEnabled ? "1" : "0" );
84 props.insert( QStringLiteral( "anchorPoint" ), encodeAnchorPoint( mAnchorPoint ) );
85 props.insert( QStringLiteral( "labelAnchorPoint" ), encodeLabelAnchorPoint( mLabelAnchorPoint ) );
86 props.insert( QStringLiteral( "blendMode" ), static_cast< int >( QgsPainting::getBlendModeEnum( mBlendMode ) ) );
87 props.insert( QStringLiteral( "ddProperties" ), mDataDefinedProperties.toVariant( propertyDefinitions() ) );
88 return props;
89}
90
91void QgsCallout::readProperties( const QVariantMap &props, const QgsReadWriteContext & )
92{
93 mEnabled = props.value( QStringLiteral( "enabled" ), QStringLiteral( "0" ) ).toInt();
94 mAnchorPoint = decodeAnchorPoint( props.value( QStringLiteral( "anchorPoint" ), QString() ).toString() );
95 mLabelAnchorPoint = decodeLabelAnchorPoint( props.value( QStringLiteral( "labelAnchorPoint" ), QString() ).toString() );
97 static_cast< Qgis::BlendMode >( props.value( QStringLiteral( "blendMode" ), QString::number( static_cast< int >( Qgis::BlendMode::Normal ) ) ).toUInt() ) );
98 mDataDefinedProperties.loadVariant( props.value( QStringLiteral( "ddProperties" ) ), propertyDefinitions() );
99}
100
101bool QgsCallout::saveProperties( QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context ) const
102{
103 if ( element.isNull() )
104 {
105 return false;
106 }
107
108 const QDomElement calloutPropsElement = QgsXmlUtils::writeVariant( properties( context ), doc );
109
110 QDomElement calloutElement = doc.createElement( QStringLiteral( "callout" ) );
111 calloutElement.setAttribute( QStringLiteral( "type" ), type() );
112 calloutElement.appendChild( calloutPropsElement );
113
114 element.appendChild( calloutElement );
115 return true;
116}
117
118void QgsCallout::restoreProperties( const QDomElement &element, const QgsReadWriteContext &context )
119{
120 const QVariantMap props = QgsXmlUtils::readVariant( element.firstChildElement() ).toMap();
121 readProperties( props, context );
122}
123
125{
126
127}
129{
130
131}
132
134{
135 return mBlendMode != QPainter::CompositionMode_SourceOver || dataDefinedProperties().isActive( QgsCallout::BlendMode );
136}
137
138QSet<QString> QgsCallout::referencedFields( const QgsRenderContext &context ) const
139{
140 mDataDefinedProperties.prepare( context.expressionContext() );
141 return mDataDefinedProperties.referencedFields( context.expressionContext() );
142}
143
145{
146 return OrderBelowAllLabels;
147}
148
149void QgsCallout::render( QgsRenderContext &context, const QRectF &rect, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext )
150{
151 QPainter *painter = context.painter();
152 if ( context.useAdvancedEffects() )
153 {
154
155 const QPainter::CompositionMode blendMode = mBlendMode;
157 {
158 context.expressionContext().setOriginalValueVariable( QString() );
159 mBlendMode = QgsSymbolLayerUtils::decodeBlendMode( dataDefinedProperties().valueAsString( QgsCallout::BlendMode, context.expressionContext(), QString() ) );
160 }
161
162 painter->setCompositionMode( blendMode );
163 }
164
165#if 0 // for debugging
166 painter->save();
167 painter->setRenderHint( QPainter::Antialiasing, false );
168 painter->translate( rect.center() );
169 painter->rotate( -angle );
170
171 painter->setBrush( QColor( 255, 0, 0, 100 ) );
172 painter->setPen( QColor( 255, 0, 0, 150 ) );
173
174 painter->drawRect( rect.width() * -0.5, rect.height() * -0.5, rect.width(), rect.height() );
175 painter->restore();
176
177 painter->setBrush( QColor( 0, 255, 0, 100 ) );
178 painter->setPen( QColor( 0, 255, 0, 150 ) );
179
180 painter->drawRect( anchor.boundingBox( ).buffered( 30 ).toRectF() );
181#endif
182
183 draw( context, rect, angle, anchor, calloutContext );
184
185 painter->setCompositionMode( QPainter::CompositionMode_SourceOver ); // just to be sure
186}
187
188void QgsCallout::setEnabled( bool enabled )
189{
190 mEnabled = enabled;
191}
192
194{
195 static std::once_flag initialized;
196 std::call_once( initialized, [ = ]( )
197 {
198 initPropertyDefinitions();
199 } );
200 return sPropertyDefinitions;
201}
202
204{
205 if ( ok )
206 *ok = true;
207 const QString cleaned = name.toLower().trimmed();
208
209 if ( cleaned == QLatin1String( "pole_of_inaccessibility" ) )
211 else if ( cleaned == QLatin1String( "point_on_exterior" ) )
212 return PointOnExterior;
213 else if ( cleaned == QLatin1String( "point_on_surface" ) )
214 return PointOnSurface;
215 else if ( cleaned == QLatin1String( "centroid" ) )
216 return Centroid;
217
218 if ( ok )
219 *ok = false;
221}
222
224{
225 switch ( anchor )
226 {
228 return QStringLiteral( "pole_of_inaccessibility" );
229 case PointOnExterior:
230 return QStringLiteral( "point_on_exterior" );
231 case PointOnSurface:
232 return QStringLiteral( "point_on_surface" );
233 case Centroid:
234 return QStringLiteral( "centroid" );
235 }
236 return QString();
237}
238
240{
241 switch ( anchor )
242 {
244 return QStringLiteral( "point_on_exterior" );
245 case LabelCentroid:
246 return QStringLiteral( "centroid" );
247 case LabelTopLeft:
248 return QStringLiteral( "tl" );
249 case LabelTopMiddle:
250 return QStringLiteral( "t" );
251 case LabelTopRight:
252 return QStringLiteral( "tr" );
253 case LabelMiddleLeft:
254 return QStringLiteral( "l" );
255 case LabelMiddleRight:
256 return QStringLiteral( "r" );
257 case LabelBottomLeft:
258 return QStringLiteral( "bl" );
260 return QStringLiteral( "b" );
261 case LabelBottomRight:
262 return QStringLiteral( "br" );
263 }
264
265 return QString();
266}
267
269{
270 if ( ok )
271 *ok = true;
272 const QString cleaned = name.toLower().trimmed();
273
274 if ( cleaned == QLatin1String( "point_on_exterior" ) )
276 else if ( cleaned == QLatin1String( "centroid" ) )
277 return LabelCentroid;
278 else if ( cleaned == QLatin1String( "tl" ) )
279 return LabelTopLeft;
280 else if ( cleaned == QLatin1String( "t" ) )
281 return LabelTopMiddle;
282 else if ( cleaned == QLatin1String( "tr" ) )
283 return LabelTopRight;
284 else if ( cleaned == QLatin1String( "l" ) )
285 return LabelMiddleLeft;
286 else if ( cleaned == QLatin1String( "r" ) )
287 return LabelMiddleRight;
288 else if ( cleaned == QLatin1String( "bl" ) )
289 return LabelBottomLeft;
290 else if ( cleaned == QLatin1String( "b" ) )
291 return LabelBottomMiddle;
292 else if ( cleaned == QLatin1String( "br" ) )
293 return LabelBottomRight;
294
295 if ( ok )
296 *ok = false;
298}
299
300QgsGeometry QgsCallout::labelAnchorGeometry( const QRectF &rect, const double angle, LabelAnchorPoint anchor ) const
301{
302 QgsGeometry label;
303 switch ( anchor )
304 {
306 label = QgsGeometry::fromRect( rect );
307 break;
308
309 case LabelCentroid:
310 label = QgsGeometry::fromRect( rect ).centroid();
311 break;
312
313 case LabelTopLeft:
314 label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomLeft() ) );
315 break;
316
317 case LabelTopMiddle:
318 label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.bottom() ) );
319 break;
320
321 case LabelTopRight:
322 label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomRight() ) );
323 break;
324
325 case LabelMiddleLeft:
326 label = QgsGeometry::fromPointXY( QgsPointXY( rect.left(), ( rect.top() + rect.bottom() ) / 2.0 ) );
327 break;
328
329 case LabelMiddleRight:
330 label = QgsGeometry::fromPointXY( QgsPointXY( rect.right(), ( rect.top() + rect.bottom() ) / 2.0 ) );
331 break;
332
333 case LabelBottomLeft:
334 label = QgsGeometry::fromPointXY( QgsPointXY( rect.topLeft() ) );
335 break;
336
338 label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.top() ) );
339 break;
340
341 case LabelBottomRight:
342 label = QgsGeometry::fromPointXY( QgsPointXY( rect.topRight() ) );
343 break;
344 }
345
346 label.rotate( angle, rect.topLeft() );
347 return label;
348}
349
350QgsGeometry QgsCallout::calloutLabelPoint( const QRectF &rect, const double angle, QgsCallout::LabelAnchorPoint anchor, QgsRenderContext &context, const QgsCallout::QgsCalloutContext &calloutContext, bool &pinned ) const
351{
352 pinned = false;
354 {
355 bool ok = false;
356 const double x = dataDefinedProperties().valueAsDouble( QgsCallout::OriginX, context.expressionContext(), 0, &ok );
357 if ( ok )
358 {
359 const double y = dataDefinedProperties().valueAsDouble( QgsCallout::OriginY, context.expressionContext(), 0, &ok );
360 if ( ok )
361 {
362 pinned = true;
363 // data defined label point, use it directly
364 QgsGeometry labelPoint = QgsGeometry::fromPointXY( QgsPointXY( x, y ) );
365 try
366 {
367 labelPoint.transform( calloutContext.originalFeatureToMapTransform( context ) );
368 labelPoint.transform( context.mapToPixel().transform() );
369 }
370 catch ( QgsCsException & )
371 {
372 return QgsGeometry();
373 }
374 return labelPoint;
375 }
376 }
377 }
378
379 QgsGeometry label;
380 switch ( anchor )
381 {
383 label = QgsGeometry::fromRect( rect );
384 break;
385
386 case LabelCentroid:
387 label = QgsGeometry::fromRect( rect ).centroid();
388 break;
389
390 case LabelTopLeft:
391 label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomLeft() ) );
392 break;
393
394 case LabelTopMiddle:
395 label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.bottom() ) );
396 break;
397
398 case LabelTopRight:
399 label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomRight() ) );
400 break;
401
402 case LabelMiddleLeft:
403 label = QgsGeometry::fromPointXY( QgsPointXY( rect.left(), ( rect.top() + rect.bottom() ) / 2.0 ) );
404 break;
405
406 case LabelMiddleRight:
407 label = QgsGeometry::fromPointXY( QgsPointXY( rect.right(), ( rect.top() + rect.bottom() ) / 2.0 ) );
408 break;
409
410 case LabelBottomLeft:
411 label = QgsGeometry::fromPointXY( QgsPointXY( rect.topLeft() ) );
412 break;
413
415 label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.top() ) );
416 break;
417
418 case LabelBottomRight:
419 label = QgsGeometry::fromPointXY( QgsPointXY( rect.topRight() ) );
420 break;
421 }
422
423 label.rotate( angle, rect.topLeft() );
424 return label;
425}
426
427QgsGeometry QgsCallout::calloutLineToPart( const QgsGeometry &labelGeometry, const QgsAbstractGeometry *partGeometry, QgsRenderContext &context, const QgsCalloutContext &calloutContext, bool &pinned ) const
428{
429 pinned = false;
430 AnchorPoint anchor = anchorPoint();
431 const QgsAbstractGeometry *evaluatedPartAnchor = partGeometry;
432 std::unique_ptr< QgsAbstractGeometry > tempPartAnchor;
433
435 {
436 bool ok = false;
437 const double x = dataDefinedProperties().valueAsDouble( QgsCallout::DestinationX, context.expressionContext(), 0, &ok );
438 if ( ok )
439 {
440 const double y = dataDefinedProperties().valueAsDouble( QgsCallout::DestinationY, context.expressionContext(), 0, &ok );
441 if ( ok )
442 {
443 pinned = true;
444 tempPartAnchor = std::make_unique< QgsPoint >( Qgis::WkbType::Point, x, y );
445 evaluatedPartAnchor = tempPartAnchor.get();
446 try
447 {
448 tempPartAnchor->transform( calloutContext.originalFeatureToMapTransform( context ) );
449 tempPartAnchor->transform( context.mapToPixel().transform() );
450 }
451 catch ( QgsCsException & )
452 {
453 evaluatedPartAnchor = partGeometry;
454 }
455 }
456 }
457 }
458
460 {
461 const QString encodedAnchor = encodeAnchorPoint( anchor );
462 context.expressionContext().setOriginalValueVariable( encodedAnchor );
463 anchor = decodeAnchorPoint( dataDefinedProperties().valueAsString( QgsCallout::AnchorPointPosition, context.expressionContext(), encodedAnchor ) );
464 }
465
466 QgsGeometry line;
467 const QgsGeos labelGeos( labelGeometry.constGet() );
468
469 switch ( QgsWkbTypes::geometryType( evaluatedPartAnchor->wkbType() ) )
470 {
471 case Qgis::GeometryType::Point:
472 case Qgis::GeometryType::Line:
473 {
474 line = labelGeos.shortestLine( evaluatedPartAnchor );
475 break;
476 }
477
478 case Qgis::GeometryType::Polygon:
479 {
480 if ( labelGeos.intersects( evaluatedPartAnchor ) )
481 return QgsGeometry();
482
483 // ideally avoid this unwanted clone in future. For now we need it because poleOfInaccessibility/pointOnSurface are
484 // only available to QgsGeometry objects
485 const QgsGeometry evaluatedPartAnchorGeom( evaluatedPartAnchor->clone() );
486 switch ( anchor )
487 {
489 line = labelGeos.shortestLine( evaluatedPartAnchorGeom.poleOfInaccessibility( std::max( evaluatedPartAnchor->boundingBox().width(), evaluatedPartAnchor->boundingBox().height() ) / 20.0 ) ); // really rough (but quick) pole of inaccessibility
490 break;
492 line = labelGeos.shortestLine( evaluatedPartAnchorGeom.pointOnSurface() );
493 break;
495 line = labelGeos.shortestLine( evaluatedPartAnchor );
496 break;
498 line = labelGeos.shortestLine( evaluatedPartAnchorGeom.centroid() );
499 break;
500 }
501 break;
502 }
503
504 case Qgis::GeometryType::Null:
505 case Qgis::GeometryType::Unknown:
506 return QgsGeometry(); // shouldn't even get here..
507 }
508 return line;
509}
510
511//
512// QgsCallout::QgsCalloutContext
513//
514
516{
517 if ( !mOriginalFeatureToMapTransform.isValid() )
518 {
519 // lazy initialization, only create if needed...
520 mOriginalFeatureToMapTransform = QgsCoordinateTransform( originalFeatureCrs, renderContext.coordinateTransform().destinationCrs(), renderContext.transformContext() );
521 }
522 return mOriginalFeatureToMapTransform;
523}
524
525
526//
527// QgsSimpleLineCallout
528//
529
531{
532 mLineSymbol = std::make_unique< QgsLineSymbol >( QgsSymbolLayerList() << new QgsSimpleLineSymbolLayer( QColor( 60, 60, 60 ), .3 ) );
533
534}
535
537
539 : QgsCallout( other )
540 , mLineSymbol( other.mLineSymbol ? other.mLineSymbol->clone() : nullptr )
541 , mMinCalloutLength( other.mMinCalloutLength )
542 , mMinCalloutLengthUnit( other.mMinCalloutLengthUnit )
543 , mMinCalloutLengthScale( other.mMinCalloutLengthScale )
544 , mOffsetFromAnchorDistance( other.mOffsetFromAnchorDistance )
545 , mOffsetFromAnchorUnit( other.mOffsetFromAnchorUnit )
546 , mOffsetFromAnchorScale( other.mOffsetFromAnchorScale )
547 , mOffsetFromLabelDistance( other.mOffsetFromLabelDistance )
548 , mOffsetFromLabelUnit( other.mOffsetFromLabelUnit )
549 , mOffsetFromLabelScale( other.mOffsetFromLabelScale )
550 , mDrawCalloutToAllParts( other.mDrawCalloutToAllParts )
551{
552
553}
554
555QgsCallout *QgsSimpleLineCallout::create( const QVariantMap &properties, const QgsReadWriteContext &context )
556{
557 std::unique_ptr< QgsSimpleLineCallout > callout = std::make_unique< QgsSimpleLineCallout >();
558 callout->readProperties( properties, context );
559 return callout.release();
560}
561
563{
564 return QStringLiteral( "simple" );
565}
566
568{
569 return new QgsSimpleLineCallout( *this );
570}
571
572QVariantMap QgsSimpleLineCallout::properties( const QgsReadWriteContext &context ) const
573{
574 QVariantMap props = QgsCallout::properties( context );
575
576 if ( mLineSymbol )
577 {
578 props[ QStringLiteral( "lineSymbol" ) ] = QgsSymbolLayerUtils::symbolProperties( mLineSymbol.get() );
579 }
580 props[ QStringLiteral( "minLength" ) ] = mMinCalloutLength;
581 props[ QStringLiteral( "minLengthUnit" ) ] = QgsUnitTypes::encodeUnit( mMinCalloutLengthUnit );
582 props[ QStringLiteral( "minLengthMapUnitScale" ) ] = QgsSymbolLayerUtils::encodeMapUnitScale( mMinCalloutLengthScale );
583
584 props[ QStringLiteral( "offsetFromAnchor" ) ] = mOffsetFromAnchorDistance;
585 props[ QStringLiteral( "offsetFromAnchorUnit" ) ] = QgsUnitTypes::encodeUnit( mOffsetFromAnchorUnit );
586 props[ QStringLiteral( "offsetFromAnchorMapUnitScale" ) ] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetFromAnchorScale );
587 props[ QStringLiteral( "offsetFromLabel" ) ] = mOffsetFromLabelDistance;
588 props[ QStringLiteral( "offsetFromLabelUnit" ) ] = QgsUnitTypes::encodeUnit( mOffsetFromLabelUnit );
589 props[ QStringLiteral( "offsetFromLabelMapUnitScale" ) ] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetFromLabelScale );
590
591 props[ QStringLiteral( "drawToAllParts" ) ] = mDrawCalloutToAllParts;
592
593 return props;
594}
595
596void QgsSimpleLineCallout::readProperties( const QVariantMap &props, const QgsReadWriteContext &context )
597{
598 QgsCallout::readProperties( props, context );
599
600 const QString lineSymbolDef = props.value( QStringLiteral( "lineSymbol" ) ).toString();
601 QDomDocument doc( QStringLiteral( "symbol" ) );
602 doc.setContent( lineSymbolDef );
603 const QDomElement symbolElem = doc.firstChildElement( QStringLiteral( "symbol" ) );
604 std::unique_ptr< QgsLineSymbol > lineSymbol( QgsSymbolLayerUtils::loadSymbol< QgsLineSymbol >( symbolElem, context ) );
605 if ( lineSymbol )
606 mLineSymbol = std::move( lineSymbol );
607
608 mMinCalloutLength = props.value( QStringLiteral( "minLength" ), 0 ).toDouble();
609 mMinCalloutLengthUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "minLengthUnit" ) ).toString() );
610 mMinCalloutLengthScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "minLengthMapUnitScale" ) ).toString() );
611
612 mOffsetFromAnchorDistance = props.value( QStringLiteral( "offsetFromAnchor" ), 0 ).toDouble();
613 mOffsetFromAnchorUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "offsetFromAnchorUnit" ) ).toString() );
614 mOffsetFromAnchorScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "offsetFromAnchorMapUnitScale" ) ).toString() );
615 mOffsetFromLabelDistance = props.value( QStringLiteral( "offsetFromLabel" ), 0 ).toDouble();
616 mOffsetFromLabelUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "offsetFromLabelUnit" ) ).toString() );
617 mOffsetFromLabelScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "offsetFromLabelMapUnitScale" ) ).toString() );
618
619 mDrawCalloutToAllParts = props.value( QStringLiteral( "drawToAllParts" ), false ).toBool();
620}
621
623{
624 QgsCallout::startRender( context );
625 if ( mLineSymbol )
626 mLineSymbol->startRender( context );
627}
628
630{
631 QgsCallout::stopRender( context );
632 if ( mLineSymbol )
633 mLineSymbol->stopRender( context );
634}
635
636QSet<QString> QgsSimpleLineCallout::referencedFields( const QgsRenderContext &context ) const
637{
638 QSet<QString> fields = QgsCallout::referencedFields( context );
639 if ( mLineSymbol )
640 fields.unite( mLineSymbol->usedAttributes( context ) );
641 return fields;
642}
643
645{
646 return mLineSymbol.get();
647}
648
650{
651 mLineSymbol.reset( symbol );
652}
653
654void QgsSimpleLineCallout::draw( QgsRenderContext &context, const QRectF &rect, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext )
655{
656 LabelAnchorPoint labelAnchor = labelAnchorPoint();
658 {
659 const QString encodedAnchor = encodeLabelAnchorPoint( labelAnchor );
660 context.expressionContext().setOriginalValueVariable( encodedAnchor );
661 labelAnchor = decodeLabelAnchorPoint( dataDefinedProperties().valueAsString( QgsCallout::LabelAnchorPointPosition, context.expressionContext(), encodedAnchor ) );
662 }
663
664 bool originPinned = false;
665 const QgsGeometry label = calloutLabelPoint( rect, angle, labelAnchor, context, calloutContext, originPinned );
666 if ( label.isNull() )
667 return;
668
669 auto drawCalloutLine = [this, &context, &calloutContext, &label, &rect, angle, &anchor, originPinned]( const QgsAbstractGeometry * partAnchor )
670 {
671 bool destinationPinned = false;
672 const QgsGeometry line = calloutLineToPart( label, partAnchor, context, calloutContext, destinationPinned );
673 if ( line.isEmpty() )
674 return;
675
676 const double lineLength = line.length();
677 if ( qgsDoubleNear( lineLength, 0 ) )
678 return;
679
680 double minLength = mMinCalloutLength;
682 {
683 context.expressionContext().setOriginalValueVariable( minLength );
685 }
686 const double minLengthPixels = context.convertToPainterUnits( minLength, mMinCalloutLengthUnit, mMinCalloutLengthScale );
687 if ( minLengthPixels > 0 && lineLength < minLengthPixels )
688 return; // too small!
689
690 std::unique_ptr< QgsCurve > calloutCurve( createCalloutLine( qgsgeometry_cast< const QgsLineString * >( line.constGet() )->startPoint(),
691 qgsgeometry_cast< const QgsLineString * >( line.constGet() )->endPoint(), context, rect, angle, anchor, calloutContext ) );
692
693 double offsetFromAnchor = mOffsetFromAnchorDistance;
695 {
698 }
699 const double offsetFromAnchorPixels = context.convertToPainterUnits( offsetFromAnchor, mOffsetFromAnchorUnit, mOffsetFromAnchorScale );
700
701 double offsetFromLabel = mOffsetFromLabelDistance;
703 {
706 }
707 const double offsetFromLabelPixels = context.convertToPainterUnits( offsetFromLabel, mOffsetFromLabelUnit, mOffsetFromLabelScale );
708 if ( offsetFromAnchorPixels > 0 || offsetFromLabelPixels > 0 )
709 {
710 calloutCurve.reset( calloutCurve->curveSubstring( offsetFromLabelPixels, calloutCurve->length() - offsetFromAnchorPixels ) );
711 }
712
713 const QPolygonF points = calloutCurve->asQPolygonF();
714
715 if ( points.empty() )
716 return;
717
718 QgsCalloutPosition position;
719 position.setOrigin( context.mapToPixel().toMapCoordinates( points.at( 0 ).x(), points.at( 0 ).y() ).toQPointF() );
720 position.setOriginIsPinned( originPinned );
721 position.setDestination( context.mapToPixel().toMapCoordinates( points.constLast().x(), points.constLast().y() ).toQPointF() );
722 position.setDestinationIsPinned( destinationPinned );
723 calloutContext.addCalloutPosition( position );
724
725 mLineSymbol->renderPolyline( points, nullptr, context );
726 };
727
728 bool toAllParts = mDrawCalloutToAllParts;
730 {
731 context.expressionContext().setOriginalValueVariable( toAllParts );
733 }
734
735 if ( calloutContext.allFeaturePartsLabeled || !toAllParts )
736 drawCalloutLine( anchor.constGet() );
737 else
738 {
739 for ( auto it = anchor.const_parts_begin(); it != anchor.const_parts_end(); ++it )
740 drawCalloutLine( *it );
741 }
742}
743
744QgsCurve *QgsSimpleLineCallout::createCalloutLine( const QgsPoint &start, const QgsPoint &end, QgsRenderContext &, const QRectF &, const double, const QgsGeometry &, QgsCallout::QgsCalloutContext & ) const
745{
746 return new QgsLineString( start, end );
747}
748
749//
750// QgsManhattanLineCallout
751//
752
754{
755}
756
758 : QgsSimpleLineCallout( other )
759{
760
761}
762
763
764QgsCallout *QgsManhattanLineCallout::create( const QVariantMap &properties, const QgsReadWriteContext &context )
765{
766 std::unique_ptr< QgsManhattanLineCallout > callout = std::make_unique< QgsManhattanLineCallout >();
767 callout->readProperties( properties, context );
768 return callout.release();
769}
770
772{
773 return QStringLiteral( "manhattan" );
774}
775
777{
778 return new QgsManhattanLineCallout( *this );
779}
780
781QgsCurve *QgsManhattanLineCallout::createCalloutLine( const QgsPoint &start, const QgsPoint &end, QgsRenderContext &, const QRectF &, const double, const QgsGeometry &, QgsCallout::QgsCalloutContext & ) const
782{
783 const QgsPoint mid1 = QgsPoint( start.x(), end.y() );
784 return new QgsLineString( QVector< QgsPoint >() << start << mid1 << end );
785}
786
787
788//
789// QgsCurvedLineCallout
790//
791
793{
794}
795
797 : QgsSimpleLineCallout( other )
798 , mOrientation( other.mOrientation )
799 , mCurvature( other.mCurvature )
800{
801
802}
803
804QgsCallout *QgsCurvedLineCallout::create( const QVariantMap &properties, const QgsReadWriteContext &context )
805{
806 std::unique_ptr< QgsCurvedLineCallout > callout = std::make_unique< QgsCurvedLineCallout >();
807 callout->readProperties( properties, context );
808
809 callout->setCurvature( properties.value( QStringLiteral( "curvature" ), 0.1 ).toDouble() );
810 callout->setOrientation( decodeOrientation( properties.value( QStringLiteral( "orientation" ), QStringLiteral( "auto" ) ).toString() ) );
811
812 return callout.release();
813}
814
816{
817 return QStringLiteral( "curved" );
818}
819
821{
822 return new QgsCurvedLineCallout( *this );
823}
824
825QVariantMap QgsCurvedLineCallout::properties( const QgsReadWriteContext &context ) const
826{
827 QVariantMap props = QgsSimpleLineCallout::properties( context );
828 props.insert( QStringLiteral( "curvature" ), mCurvature );
829 props.insert( QStringLiteral( "orientation" ), encodeOrientation( mOrientation ) );
830 return props;
831}
832
833QgsCurve *QgsCurvedLineCallout::createCalloutLine( const QgsPoint &start, const QgsPoint &end, QgsRenderContext &context, const QRectF &rect, const double, const QgsGeometry &, QgsCallout::QgsCalloutContext & ) const
834{
835 double curvature = mCurvature * 100;
837 {
840 }
841
842 Orientation orientation = mOrientation;
844 {
845 bool ok = false;
846 const QString orientationString = dataDefinedProperties().property( QgsCallout::Orientation ).valueAsString( context.expressionContext(), QString(), &ok );
847 if ( ok )
848 {
849 orientation = decodeOrientation( orientationString );
850 }
851 }
852
853 if ( orientation == Automatic )
854 {
855 // to calculate automatically the best curve orientation, we first check which side of the label bounding box
856 // the callout origin is nearest to
857 switch ( QgsGeometryUtils::closestSideOfRectangle( rect.right(), rect.bottom(), rect.left(), rect.top(), start.x(), start.y() ) )
858 {
859 case 1:
860 // closest to bottom
861 if ( qgsDoubleNear( end.x(), start.x() ) )
862 {
863 // if vertical line, we bend depending on whether the line sits towards the left or right side of the label
864 if ( start.x() < ( rect.left() + 0.5 * rect.width() ) )
866 else
868 }
869 else if ( end.x() > start.x() )
871 else
873 break;
874
875 case 2:
876 // closest to bottom-right
877 if ( end.x() < start.x() )
879 else if ( end.y() < start.y() )
881 else if ( end.x() - start.x() < end.y() - start.y() )
883 else
885 break;
886
887 case 3:
888 // closest to right
889 if ( qgsDoubleNear( end.y(), start.y() ) )
890 {
891 // if horizontal line, we bend depending on whether the line sits towards the top or bottom side of the label
892 if ( start.y() < ( rect.top() + 0.5 * rect.height() ) )
894 else
896 }
897 else if ( end.y() < start.y() )
899 else
901 break;
902
903 case 4:
904 // closest to top-right
905 if ( end.x() < start.x() )
907 else if ( end.y() > start.y() )
909 else if ( end.x() - start.x() < start.y() - end.y() )
911 else
913 break;
914
915 case 5:
916 // closest to top
917 if ( qgsDoubleNear( end.x(), start.x() ) )
918 {
919 // if vertical line, we bend depending on whether the line sits towards the left or right side of the label
920 if ( start.x() < ( rect.left() + 0.5 * rect.width() ) )
922 else
924 }
925 else if ( end.x() < start.x() )
927 else
929 break;
930
931 case 6:
932 // closest to top-left
933 if ( end.x() > start.x() )
935 else if ( end.y() > start.y() )
937 else if ( start.x() - end.x() < start.y() - end.y() )
939 else
941 break;
942
943 case 7:
944 //closest to left
945 if ( qgsDoubleNear( end.y(), start.y() ) )
946 {
947 // if horizontal line, we bend depending on whether the line sits towards the top or bottom side of the label
948 if ( start.y() < ( rect.top() + 0.5 * rect.height() ) )
950 else
952 }
953 else if ( end.y() > start.y() )
955 else
957 break;
958
959 case 8:
960 //closest to bottom-left
961 if ( end.x() > start.x() )
963 else if ( end.y() < start.y() )
965 else if ( start.x() - end.x() < end.y() - start.y() )
967 else
969 break;
970 }
971 }
972
973 // turn the line into a curved line. We do this by creating a circular string from the callout line's
974 // start to end point, where the curve point is in the middle of the callout line and perpendicularly offset
975 // by a proportion of the overall callout line length
976 const double distance = ( orientation == Clockwise ? 1 : -1 ) * start.distance( end ) * curvature / 100.0;
977 double midX, midY;
978 QgsGeometryUtils::perpendicularOffsetPointAlongSegment( start.x(), start.y(), end.x(), end.y(), 0.5, distance, &midX, &midY );
979
980 return new QgsCircularString( start, QgsPoint( midX, midY ), end );
981}
982
983QgsCurvedLineCallout::Orientation QgsCurvedLineCallout::decodeOrientation( const QString &string )
984{
985 const QString cleaned = string.toLower().trimmed();
986 if ( cleaned == QLatin1String( "auto" ) )
987 return Automatic;
988 if ( cleaned == QLatin1String( "clockwise" ) )
989 return Clockwise;
990 if ( cleaned == QLatin1String( "counterclockwise" ) )
991 return CounterClockwise;
992 return Automatic;
993}
994
995QString QgsCurvedLineCallout::encodeOrientation( QgsCurvedLineCallout::Orientation orientation )
996{
997 switch ( orientation )
998 {
1000 return QStringLiteral( "auto" );
1002 return QStringLiteral( "clockwise" );
1004 return QStringLiteral( "counterclockwise" );
1005 }
1006 return QString();
1007}
1008
1010{
1011 return mOrientation;
1012}
1013
1015{
1016 mOrientation = orientation;
1017}
1018
1020{
1021 return mCurvature;
1022}
1023
1025{
1026 mCurvature = curvature;
1027}
1028
1029
1030
1031//
1032// QgsBalloonCallout
1033//
1034
1036{
1037 mFillSymbol = std::make_unique< QgsFillSymbol >( QgsSymbolLayerList() << new QgsSimpleFillSymbolLayer( QColor( 255, 200, 60 ) ) );
1038}
1039
1041
1043 : QgsCallout( other )
1044 , mFillSymbol( other.mFillSymbol ? other.mFillSymbol->clone() : nullptr )
1045 , mOffsetFromAnchorDistance( other.mOffsetFromAnchorDistance )
1046 , mOffsetFromAnchorUnit( other.mOffsetFromAnchorUnit )
1047 , mOffsetFromAnchorScale( other.mOffsetFromAnchorScale )
1048 , mMargins( other.mMargins )
1049 , mMarginUnit( other.mMarginUnit )
1050 , mWedgeWidth( other.mWedgeWidth )
1051 , mWedgeWidthUnit( other.mWedgeWidthUnit )
1052 , mWedgeWidthScale( other.mWedgeWidthScale )
1053 , mCornerRadius( other.mCornerRadius )
1054 , mCornerRadiusUnit( other.mCornerRadiusUnit )
1055 , mCornerRadiusScale( other.mCornerRadiusScale )
1056{
1057
1058}
1059
1060QgsCallout *QgsBalloonCallout::create( const QVariantMap &properties, const QgsReadWriteContext &context )
1061{
1062 std::unique_ptr< QgsBalloonCallout > callout = std::make_unique< QgsBalloonCallout >();
1063 callout->readProperties( properties, context );
1064 return callout.release();
1065}
1066
1068{
1069 return QStringLiteral( "balloon" );
1070}
1071
1073{
1074 return new QgsBalloonCallout( *this );
1075}
1076
1077QVariantMap QgsBalloonCallout::properties( const QgsReadWriteContext &context ) const
1078{
1079 QVariantMap props = QgsCallout::properties( context );
1080
1081 if ( mFillSymbol )
1082 {
1083 props[ QStringLiteral( "fillSymbol" ) ] = QgsSymbolLayerUtils::symbolProperties( mFillSymbol.get() );
1084 }
1085
1086 props[ QStringLiteral( "offsetFromAnchor" ) ] = mOffsetFromAnchorDistance;
1087 props[ QStringLiteral( "offsetFromAnchorUnit" ) ] = QgsUnitTypes::encodeUnit( mOffsetFromAnchorUnit );
1088 props[ QStringLiteral( "offsetFromAnchorMapUnitScale" ) ] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetFromAnchorScale );
1089
1090 props[ QStringLiteral( "margins" ) ] = mMargins.toString();
1091 props[ QStringLiteral( "marginsUnit" ) ] = QgsUnitTypes::encodeUnit( mMarginUnit );
1092
1093 props[ QStringLiteral( "wedgeWidth" ) ] = mWedgeWidth;
1094 props[ QStringLiteral( "wedgeWidthUnit" ) ] = QgsUnitTypes::encodeUnit( mWedgeWidthUnit );
1095 props[ QStringLiteral( "wedgeWidthMapUnitScale" ) ] = QgsSymbolLayerUtils::encodeMapUnitScale( mWedgeWidthScale );
1096
1097 props[ QStringLiteral( "cornerRadius" ) ] = mCornerRadius;
1098 props[ QStringLiteral( "cornerRadiusUnit" ) ] = QgsUnitTypes::encodeUnit( mCornerRadiusUnit );
1099 props[ QStringLiteral( "cornerRadiusMapUnitScale" ) ] = QgsSymbolLayerUtils::encodeMapUnitScale( mCornerRadiusScale );
1100
1101 return props;
1102}
1103
1104void QgsBalloonCallout::readProperties( const QVariantMap &props, const QgsReadWriteContext &context )
1105{
1106 QgsCallout::readProperties( props, context );
1107
1108 const QString fillSymbolDef = props.value( QStringLiteral( "fillSymbol" ) ).toString();
1109 QDomDocument doc( QStringLiteral( "symbol" ) );
1110 doc.setContent( fillSymbolDef );
1111 const QDomElement symbolElem = doc.firstChildElement( QStringLiteral( "symbol" ) );
1112 std::unique_ptr< QgsFillSymbol > fillSymbol( QgsSymbolLayerUtils::loadSymbol< QgsFillSymbol >( symbolElem, context ) );
1113 if ( fillSymbol )
1114 mFillSymbol = std::move( fillSymbol );
1115
1116 mOffsetFromAnchorDistance = props.value( QStringLiteral( "offsetFromAnchor" ), 0 ).toDouble();
1117 mOffsetFromAnchorUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "offsetFromAnchorUnit" ) ).toString() );
1118 mOffsetFromAnchorScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "offsetFromAnchorMapUnitScale" ) ).toString() );
1119
1120 mMargins = QgsMargins::fromString( props.value( QStringLiteral( "margins" ) ).toString() );
1121 mMarginUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "marginsUnit" ) ).toString() );
1122
1123 mWedgeWidth = props.value( QStringLiteral( "wedgeWidth" ), 2.64 ).toDouble();
1124 mWedgeWidthUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "wedgeWidthUnit" ) ).toString() );
1125 mWedgeWidthScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "wedgeWidthMapUnitScale" ) ).toString() );
1126
1127 mCornerRadius = props.value( QStringLiteral( "cornerRadius" ), 0 ).toDouble();
1128 mCornerRadiusUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "cornerRadiusUnit" ) ).toString() );
1129 mCornerRadiusScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "cornerRadiusMapUnitScale" ) ).toString() );
1130}
1131
1133{
1134 QgsCallout::startRender( context );
1135 if ( mFillSymbol )
1136 mFillSymbol->startRender( context );
1137}
1138
1140{
1141 QgsCallout::stopRender( context );
1142 if ( mFillSymbol )
1143 mFillSymbol->stopRender( context );
1144}
1145
1146QSet<QString> QgsBalloonCallout::referencedFields( const QgsRenderContext &context ) const
1147{
1148 QSet<QString> fields = QgsCallout::referencedFields( context );
1149 if ( mFillSymbol )
1150 fields.unite( mFillSymbol->usedAttributes( context ) );
1151 return fields;
1152}
1153
1155{
1156 return mFillSymbol.get();
1157}
1158
1160{
1161 mFillSymbol.reset( symbol );
1162}
1163
1164void QgsBalloonCallout::draw( QgsRenderContext &context, const QRectF &rect, const double, const QgsGeometry &anchor, QgsCalloutContext &calloutContext )
1165{
1166 bool destinationIsPinned = false;
1167 QgsGeometry line = calloutLineToPart( QgsGeometry::fromRect( rect ), anchor.constGet(), context, calloutContext, destinationIsPinned );
1168
1169 double offsetFromAnchor = mOffsetFromAnchorDistance;
1171 {
1174 }
1175 const double offsetFromAnchorPixels = context.convertToPainterUnits( offsetFromAnchor, mOffsetFromAnchorUnit, mOffsetFromAnchorScale );
1176
1177 if ( offsetFromAnchorPixels > 0 )
1178 {
1179 if ( const QgsLineString *ls = qgsgeometry_cast< const QgsLineString * >( line.constGet() ) )
1180 {
1181 line = QgsGeometry( ls->curveSubstring( 0, ls->length() - offsetFromAnchorPixels ) );
1182 }
1183 }
1184
1185 QgsPointXY destination;
1186 QgsPointXY origin;
1187 if ( const QgsLineString *ls = qgsgeometry_cast< const QgsLineString * >( line.constGet() ) )
1188 {
1189 origin = ls->startPoint();
1190 destination = ls->endPoint();
1191 }
1192 else
1193 {
1194 destination = QgsPointXY( rect.center() );
1195 }
1196
1197 const QPolygonF points = getPoints( context, destination, rect );
1198 if ( points.empty() )
1199 return;
1200
1201 if ( !origin.isEmpty() )
1202 {
1203 QgsCalloutPosition position;
1204 position.setOrigin( context.mapToPixel().toMapCoordinates( origin.x(), origin.y() ).toQPointF() );
1205 position.setOriginIsPinned( false );
1206 position.setDestination( context.mapToPixel().toMapCoordinates( destination.x(), destination.y() ).toQPointF() );
1207 position.setDestinationIsPinned( destinationIsPinned );
1208 calloutContext.addCalloutPosition( position );
1209 }
1210
1211 mFillSymbol->renderPolygon( points, nullptr, nullptr, context );
1212}
1213
1214QPolygonF QgsBalloonCallout::getPoints( QgsRenderContext &context, QgsPointXY origin, QRectF rect ) const
1215{
1216 double segmentPointWidth = mWedgeWidth;
1218 {
1219 context.expressionContext().setOriginalValueVariable( segmentPointWidth );
1220 segmentPointWidth = dataDefinedProperties().valueAsDouble( QgsCallout::WedgeWidth, context.expressionContext(), segmentPointWidth );
1221 }
1222 segmentPointWidth = context.convertToPainterUnits( segmentPointWidth, mWedgeWidthUnit, mWedgeWidthScale );
1223
1224 double cornerRadius = mCornerRadius;
1226 {
1229 }
1230 cornerRadius = context.convertToPainterUnits( cornerRadius, mCornerRadiusUnit, mCornerRadiusScale );
1231
1232 double left = mMargins.left();
1233 double right = mMargins.right();
1234 double top = mMargins.top();
1235 double bottom = mMargins.bottom();
1236
1237 if ( dataDefinedProperties().isActive( QgsCallout::Margins ) )
1238 {
1239 const QVariant value = dataDefinedProperties().value( QgsCallout::Margins, context.expressionContext() );
1240 if ( !QgsVariantUtils::isNull( value ) )
1241 {
1242 if ( value.type() == QVariant::List )
1243 {
1244 const QVariantList list = value.toList();
1245 if ( list.size() == 4 )
1246 {
1247 bool topOk = false;
1248 bool rightOk = false;
1249 bool bottomOk = false;
1250 bool leftOk = false;
1251 const double evaluatedTop = list.at( 0 ).toDouble( &topOk );
1252 const double evaluatedRight = list.at( 1 ).toDouble( &rightOk );
1253 const double evaluatedBottom = list.at( 2 ).toDouble( &bottomOk );
1254 const double evaluatedLeft = list.at( 3 ).toDouble( &leftOk );
1255 if ( topOk && rightOk && bottomOk && leftOk )
1256 {
1257 left = evaluatedLeft;
1258 top = evaluatedTop;
1259 right = evaluatedRight;
1260 bottom = evaluatedBottom;
1261 }
1262 }
1263 }
1264 else
1265 {
1266 const QStringList list = value.toString().trimmed().split( ',' );
1267 if ( list.count() == 4 )
1268 {
1269 bool topOk = false;
1270 bool rightOk = false;
1271 bool bottomOk = false;
1272 bool leftOk = false;
1273 const double evaluatedTop = list.at( 0 ).toDouble( &topOk );
1274 const double evaluatedRight = list.at( 1 ).toDouble( &rightOk );
1275 const double evaluatedBottom = list.at( 2 ).toDouble( &bottomOk );
1276 const double evaluatedLeft = list.at( 3 ).toDouble( &leftOk );
1277 if ( topOk && rightOk && bottomOk && leftOk )
1278 {
1279 left = evaluatedLeft;
1280 top = evaluatedTop;
1281 right = evaluatedRight;
1282 bottom = evaluatedBottom;
1283 }
1284 }
1285 }
1286 }
1287 }
1288
1289 const double marginLeft = context.convertToPainterUnits( left, mMarginUnit );
1290 const double marginRight = context.convertToPainterUnits( right, mMarginUnit );
1291 const double marginTop = context.convertToPainterUnits( top, mMarginUnit );
1292 const double marginBottom = context.convertToPainterUnits( bottom, mMarginUnit );
1293
1294 const QRectF expandedRect = rect.height() < 0 ?
1295 QRectF( rect.left() - marginLeft, rect.top() + marginBottom,
1296 rect.width() + marginLeft + marginRight,
1297 rect.height() - marginTop - marginBottom ) :
1298 QRectF( rect.left() - marginLeft, rect.top() - marginTop,
1299 rect.width() + marginLeft + marginRight,
1300 rect.height() + marginTop + marginBottom );
1301
1302 // IMPORTANT -- check for degenerate height is sometimes >=0, because QRectF are not normalized and we are using painter
1303 // coordinates with descending vertical axis!
1304 if ( expandedRect.width() <= 0 || ( rect.height() < 0 && expandedRect.height() >= 0 ) || ( rect.height() > 0 && expandedRect.height() <= 0 ) )
1305 return QPolygonF();
1306
1307 const QPainterPath path = QgsShapeGenerator::createBalloon( origin, expandedRect, segmentPointWidth, cornerRadius );
1308 const QTransform t = QTransform::fromScale( 100, 100 );
1309 const QTransform ti = t.inverted();
1310 const QPolygonF poly = path.toFillPolygon( t );
1311 return ti.map( poly );
1312}
BlendMode
Blending modes defining the available composition modes that can be used when painting.
Definition: qgis.h:2978
Abstract base class for all geometries.
virtual QgsRectangle boundingBox() const =0
Returns the minimal bounding box for the geometry.
virtual void transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection d=Qgis::TransformDirection::Forward, bool transformZ=false) SIP_THROW(QgsCsException)=0
Transforms the geometry using a coordinate transform.
Qgis::WkbType wkbType() const SIP_HOLDGIL
Returns the WKB type of the geometry.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
bool valueAsBool(int key, const QgsExpressionContext &context, bool defaultValue=false, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as an boolean.
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double.
A cartoon talking bubble callout style.
Definition: qgscallout.h:891
QgsFillSymbol * fillSymbol()
Returns the fill symbol used to render the callout.
double cornerRadius() const
Returns the corner radius of the balloon shapes.
Definition: qgscallout.h:1084
QString type() const override
Returns a unique string representing the callout type.
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.
~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:943
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:246
QgsCoordinateTransform originalFeatureToMapTransform(const QgsRenderContext &renderContext) const
Returns the coordinate transform to convert from the original layer associated with the callout to th...
Definition: qgscallout.cpp:515
void addCalloutPosition(const QgsCalloutPosition &position)
Adds a rendered callout position.
Definition: qgscallout.h:276
bool allFeaturePartsLabeled
true if all parts of associated feature were labeled
Definition: qgscallout.h:249
QgsCoordinateReferenceSystem originalFeatureCrs
Contains the CRS of the original feature associated with this callout.
Definition: qgscallout.h:256
Abstract base class for callout renderers.
Definition: qgscallout.h:53
static QString encodeAnchorPoint(AnchorPoint anchor)
Encodes an anchor point to its string representation.
Definition: qgscallout.cpp:223
bool containsAdvancedEffects() const
Returns true if the callout requires advanced effects such as blend modes, which require output in ra...
Definition: qgscallout.cpp:133
virtual bool saveProperties(QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context) const
Saves the current state of the callout to a DOM element.
Definition: qgscallout.cpp:101
virtual void stopRender(QgsRenderContext &context)
Finalises the callout after a set of rendering operations on the specified render context.
Definition: qgscallout.cpp:128
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...
Definition: qgscallout.cpp:427
void setEnabled(bool enabled)
Sets whether the callout is enabled.
Definition: qgscallout.cpp:188
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:331
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.
Definition: qgscallout.cpp:350
static QgsPropertiesDefinition propertyDefinitions()
Returns the definitions for data defined properties available for use in callouts.
Definition: qgscallout.cpp:193
AnchorPoint anchorPoint() const
Returns the feature's anchor point position.
Definition: qgscallout.h:361
LabelAnchorPoint labelAnchorPoint() const
Returns the label's anchor point position.
Definition: qgscallout.h:394
AnchorPoint
Feature's anchor point position.
Definition: qgscallout.h:112
@ PointOnExterior
A point on the surface's outline closest to the label is used as anchor for polygon geometries.
Definition: qgscallout.h:114
@ Centroid
The surface's centroid is used as anchor for polygon geometries.
Definition: qgscallout.h:116
@ PointOnSurface
A point guaranteed to be on the surface is used as anchor for polygon geometries.
Definition: qgscallout.h:115
@ PoleOfInaccessibility
The surface's pole of inaccessibility used as anchor for polygon geometries.
Definition: qgscallout.h:113
void render(QgsRenderContext &context, const QRectF &rect, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext)
Renders the callout onto the specified render context.
Definition: qgscallout.cpp:149
virtual void restoreProperties(const QDomElement &element, const QgsReadWriteContext &context)
Restores the callout's properties from a DOM element.
Definition: qgscallout.cpp:118
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.
Definition: qgscallout.cpp:239
virtual void startRender(QgsRenderContext &context)
Prepares the callout for rendering on the specified render context.
Definition: qgscallout.cpp:124
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...
Definition: qgscallout.cpp:268
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.
Definition: qgscallout.cpp:300
QgsCallout()
Constructor for QgsCallout.
Definition: qgscallout.cpp:76
DrawOrder
Options for draw order (stacking) of callouts.
Definition: qgscallout.h:105
@ OrderBelowAllLabels
Render callouts below all labels.
Definition: qgscallout.h:106
virtual DrawOrder drawOrder() const
Returns the desired drawing order (stacking) to use while rendering this callout.
Definition: qgscallout.cpp:144
QPainter::CompositionMode blendMode() const
Returns the blending mode used for drawing callouts.
Definition: qgscallout.h:428
LabelAnchorPoint
Label's anchor point position.
Definition: qgscallout.h:124
@ LabelPointOnExterior
The point on the label's boundary closest to the feature.
Definition: qgscallout.h:125
@ LabelBottomLeft
Bottom left corner of the label's boundary.
Definition: qgscallout.h:132
@ LabelBottomMiddle
Bottom middle of the label's boundary.
Definition: qgscallout.h:133
@ LabelMiddleLeft
Middle left of the label's boundary.
Definition: qgscallout.h:130
@ LabelBottomRight
Bottom right corner of the label's boundary.
Definition: qgscallout.h:134
@ LabelMiddleRight
Middle right of the label's boundary.
Definition: qgscallout.h:131
@ LabelTopMiddle
Top middle of the label's boundary.
Definition: qgscallout.h:128
@ LabelTopLeft
Top left corner of the label's boundary.
Definition: qgscallout.h:127
@ LabelCentroid
The labe's centroid.
Definition: qgscallout.h:126
@ LabelTopRight
Top right corner of the label's boundary.
Definition: qgscallout.h:129
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.
Definition: qgscallout.cpp:203
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 ...
Definition: qgscallout.cpp:91
virtual QSet< QString > referencedFields(const QgsRenderContext &context) const
Returns the set of attributes referenced by the callout.
Definition: qgscallout.cpp:138
@ DestinationX
X-coordinate of callout destination (feature anchor) (since QGIS 3.20)
Definition: qgscallout.h:93
@ Curvature
Curvature of curved line callouts (since QGIS 3.20)
Definition: qgscallout.h:95
@ BlendMode
Callout blend mode (since QGIS 3.20)
Definition: qgscallout.h:100
@ DestinationY
Y-coordinate of callout destination (feature anchor) (since QGIS 3.20)
Definition: qgscallout.h:94
@ CornerRadius
Balloon callout corner radius (since QGIS 3.20)
Definition: qgscallout.h:99
@ AnchorPointPosition
Feature's anchor point position.
Definition: qgscallout.h:89
@ LabelAnchorPointPosition
Label's anchor point position.
Definition: qgscallout.h:90
@ DrawCalloutToAllParts
Whether callout lines should be drawn to all feature parts.
Definition: qgscallout.h:88
@ OffsetFromAnchor
Distance to offset lines from anchor points.
Definition: qgscallout.h:86
@ WedgeWidth
Balloon callout wedge width (since QGIS 3.20)
Definition: qgscallout.h:98
@ OriginY
Y-coordinate of callout origin (label anchor) (since QGIS 3.20)
Definition: qgscallout.h:92
@ OffsetFromLabel
Distance to offset lines from label area.
Definition: qgscallout.h:87
@ Orientation
Orientation of curved line callouts (since QGIS 3.20)
Definition: qgscallout.h:96
@ OriginX
X-coordinate of callout origin (label anchor) (since QGIS 3.20)
Definition: qgscallout.h:91
@ MinimumCalloutLength
Minimum length of callouts.
Definition: qgscallout.h:85
@ Margins
Margin from text (since QGIS 3.20)
Definition: qgscallout.h:97
virtual QVariantMap properties(const QgsReadWriteContext &context) const
Returns the properties describing the callout encoded in a string format.
Definition: qgscallout.cpp:80
bool enabled() const
Returns true if the the callout is enabled.
Definition: qgscallout.h:319
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.
Definition: qgsexception.h:66
Abstract base class for curved geometry type.
Definition: qgscurve.h:36
Draws curved lines as callouts.
Definition: qgscallout.h:791
QgsCurvedLineCallout * clone() const override
Duplicates a callout by creating a deep copy of the callout.
Definition: qgscallout.cpp:820
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.
Definition: qgscallout.cpp:833
QVariantMap properties(const QgsReadWriteContext &context) const override
Returns the properties describing the callout encoded in a string format.
Definition: qgscallout.cpp:825
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...
Definition: qgscallout.cpp:804
QString type() const override
Returns a unique string representing the callout type.
Definition: qgscallout.cpp:815
double curvature() const
Returns the callout line's curvature.
Orientation
Curve orientation.
Definition: qgscallout.h:798
@ Clockwise
Curve lines in a clockwise direction.
Definition: qgscallout.h:800
@ CounterClockwise
Curve lines in a counter-clockwise direction.
Definition: qgscallout.h:801
@ Automatic
Automatically choose most cartographically pleasing orientation based on label and callout arrangemen...
Definition: qgscallout.h:799
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.
Definition: qgsfillsymbol.h:30
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,...
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,...
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:164
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
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) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
Q_GADGET bool isNull
Definition: qgsgeometry.h:166
static QgsGeometry fromRect(const QgsRectangle &rect) SIP_HOLDGIL
Creates a new geometry from a QgsRectangle.
static QgsGeometry fromPointXY(const QgsPointXY &point) SIP_HOLDGIL
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, exception handling*.
Definition: qgsgeos.h:99
QgsGeometry shortestLine(const QgsGeometry &other, QString *errorMsg=nullptr) const
Returns the shortest line joining this geometry to the other geometry.
Definition: qgsgeos.cpp:2757
bool intersects(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom intersects this.
Definition: qgsgeos.cpp:729
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:45
A line symbol type, for rendering LineString and MultiLineString geometries.
Definition: qgslinesymbol.h:30
Draws straight (right angled) lines as callouts.
Definition: qgscallout.h:748
QString type() const override
Returns a unique string representing the callout type.
Definition: qgscallout.cpp:771
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.
Definition: qgscallout.cpp:781
static QgsCallout * create(const QVariantMap &properties=QVariantMap(), const QgsReadWriteContext &context=QgsReadWriteContext())
Creates a new QgsManhattanLineCallout, using the settings serialized in the properties map (correspon...
Definition: qgscallout.cpp:764
QgsManhattanLineCallout * clone() const override
Duplicates a callout by creating a deep copy of the callout.
Definition: qgscallout.cpp:776
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.
Definition: qgsmaptopixel.h:90
double top() const
Returns the top margin.
Definition: qgsmargins.h:78
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...
Definition: qgsmargins.cpp:27
double right() const
Returns the right margin.
Definition: qgsmargins.h:84
double bottom() const
Returns the bottom margin.
Definition: qgsmargins.h:90
QString toString() const
Returns the margins encoded to a string.
Definition: qgsmargins.cpp:18
double left() const
Returns the left margin.
Definition: qgsmargins.h:72
static Qgis::BlendMode getBlendModeEnum(QPainter::CompositionMode blendMode)
Returns a BlendMode corresponding to a QPainter::CompositionMode.
Definition: qgspainting.cpp:80
static QPainter::CompositionMode getCompositionMode(Qgis::BlendMode blendMode)
Returns a QPainter::CompositionMode corresponding to a BlendMode.
Definition: qgspainting.cpp:20
A class to represent a 2D point.
Definition: qgspointxy.h:59
bool isEmpty() const SIP_HOLDGIL
Returns true if the geometry is empty.
Definition: qgspointxy.h:249
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
QPointF toQPointF() const
Converts a point to a QPointF.
Definition: qgspointxy.h:169
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
double distance(double x, double y) const SIP_HOLDGIL
Returns the Cartesian 2D distance between this point and a specified x, y coordinate.
Definition: qgspoint.h:343
Q_GADGET double x
Definition: qgspoint.h:52
double y
Definition: qgspoint.h:53
QgsProperty property(int key) const override
Returns a matching property from the collection, if one exists.
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const override
Returns the calculated value of the property with the specified key from within the collection.
bool loadVariant(const QVariant &configuration, const QgsPropertiesDefinition &definitions) override
Loads this property collection from a QVariantMap, wrapped in a QVariant.
QSet< QString > referencedFields(const QgsExpressionContext &context=QgsExpressionContext(), bool ignoreContext=false) const override
Returns the set of any fields referenced by the active properties from the collection.
QVariant toVariant(const QgsPropertiesDefinition &definitions) const override
Saves this property collection to a QVariantMap, wrapped in a QVariant.
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
bool prepare(const QgsExpressionContext &context=QgsExpressionContext()) const override
Prepares the collection against a specified expression context.
Definition for a property.
Definition: qgsproperty.h:46
@ Double
Double value (including negative values)
Definition: qgsproperty.h:56
@ BlendMode
Blend mode.
Definition: qgsproperty.h:66
@ Boolean
Boolean value.
Definition: qgsproperty.h:52
@ DoublePositive
Positive double value (including 0)
Definition: qgsproperty.h:57
@ DataTypeString
Property requires a string value.
Definition: qgsproperty.h:91
QString valueAsString(const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property and interprets it as a string.
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.
Definition: qgsrectangle.h:500
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
QgsRectangle buffered(double width) const
Gets rectangle enlarged by buffer.
Definition: qgsrectangle.h:325
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).
A simple direct line callout style.
Definition: qgscallout.h:511
void startRender(QgsRenderContext &context) override
Prepares the callout for rendering on the specified render context.
Definition: qgscallout.cpp:622
void setLineSymbol(QgsLineSymbol *symbol)
Sets the line symbol used to render the callout line.
Definition: qgscallout.cpp:649
void stopRender(QgsRenderContext &context) override
Finalises the callout after a set of rendering operations on the specified render context.
Definition: qgscallout.cpp:629
QSet< QString > referencedFields(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the callout.
Definition: qgscallout.cpp:636
double offsetFromLabel() const
Returns the offset distance from label area at which to end the line.
Definition: qgscallout.h:652
void readProperties(const QVariantMap &props, const QgsReadWriteContext &context) override
Reads a string map of an callout's properties and restores the callout to the state described by the ...
Definition: qgscallout.cpp:596
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.
Definition: qgscallout.cpp:654
QString type() const override
Returns a unique string representing the callout type.
Definition: qgscallout.cpp:562
double offsetFromAnchor() const
Returns the offset distance from the anchor point at which to start the line.
Definition: qgscallout.h:608
~QgsSimpleLineCallout() override
virtual QgsCurve * createCalloutLine(const QgsPoint &start, const QgsPoint &end, QgsRenderContext &context, const QRectF &bodyBoundingBox, const double angle, const QgsGeometry &anchor, QgsCallout::QgsCalloutContext &calloutContext) const
Creates a callout line between start and end in the desired style.
Definition: qgscallout.cpp:744
static QgsCallout * create(const QVariantMap &properties=QVariantMap(), const QgsReadWriteContext &context=QgsReadWriteContext())
Creates a new QgsSimpleLineCallout, using the settings serialized in the properties map (correspondin...
Definition: qgscallout.cpp:555
QVariantMap properties(const QgsReadWriteContext &context) const override
Returns the properties describing the callout encoded in a string format.
Definition: qgscallout.cpp:572
QgsSimpleLineCallout * clone() const override
Duplicates a callout by creating a deep copy of the callout.
Definition: qgscallout.cpp:567
QgsLineSymbol * lineSymbol()
Returns the line symbol used to render the callout line.
Definition: qgscallout.cpp:644
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)
Returns true if the specified variant should be considered a NULL value.
static Qgis::GeometryType geometryType(Qgis::WkbType type) SIP_HOLDGIL
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:865
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.
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:3509
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition: qgssymbol.h:29
record about vertex coordinates and index of anchor to which it is snapped