QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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
35#include <mutex>
36
37QgsPropertiesDefinition QgsCallout::sPropertyDefinitions;
38
39void QgsCallout::initPropertyDefinitions()
40{
41 const QString origin = QStringLiteral( "callouts" );
42
43 sPropertyDefinitions = QgsPropertiesDefinition
44 {
45 { QgsCallout::MinimumCalloutLength, QgsPropertyDefinition( "MinimumCalloutLength", QObject::tr( "Minimum callout length" ), QgsPropertyDefinition::DoublePositive, origin ) },
46 { QgsCallout::OffsetFromAnchor, QgsPropertyDefinition( "OffsetFromAnchor", QObject::tr( "Offset from feature" ), QgsPropertyDefinition::DoublePositive, origin ) },
47 { QgsCallout::OffsetFromLabel, QgsPropertyDefinition( "OffsetFromLabel", QObject::tr( "Offset from label" ), QgsPropertyDefinition::DoublePositive, origin ) },
48 { QgsCallout::DrawCalloutToAllParts, QgsPropertyDefinition( "DrawCalloutToAllParts", QObject::tr( "Draw lines to all feature parts" ), QgsPropertyDefinition::Boolean, origin ) },
49 { 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 ) },
50 {
51 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|"
52 "<b>TR</b>=Top right|<br>"
53 "<b>L</b>=Left|<b>R</b>=Right|<br>"
54 "<b>BL</b>=Bottom left|<b>B</b>=Bottom middle|"
55 "<b>BR</b>=Bottom right]", origin )
56 },
57 { QgsCallout::OriginX, QgsPropertyDefinition( "OriginX", QObject::tr( "Callout origin (X)" ), QgsPropertyDefinition::Double, origin ) },
58 { QgsCallout::OriginY, QgsPropertyDefinition( "OriginY", QObject::tr( "Callout origin (Y)" ), QgsPropertyDefinition::Double, origin ) },
59 { QgsCallout::DestinationX, QgsPropertyDefinition( "DestinationX", QObject::tr( "Callout destination (X)" ), QgsPropertyDefinition::Double, origin ) },
60 { QgsCallout::DestinationY, QgsPropertyDefinition( "DestinationY", QObject::tr( "Callout destination (Y)" ), QgsPropertyDefinition::Double, origin ) },
61 { QgsCallout::Curvature, QgsPropertyDefinition( "Curvature", QObject::tr( "Callout line curvature" ), QgsPropertyDefinition::Double, origin ) },
62 {
63 QgsCallout::Orientation, QgsPropertyDefinition( "Orientation", QgsPropertyDefinition::DataTypeString, QObject::tr( "Callout curve orientation" ), QObject::tr( "string " ) + "[<b>auto</b>|<b>clockwise</b>|<b>counterclockwise</b>]", origin )
64 },
65 {
66 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>" ) )
67 },
68 { QgsCallout::WedgeWidth, QgsPropertyDefinition( "WedgeWidth", QObject::tr( "Wedge width" ), QgsPropertyDefinition::DoublePositive, origin ) },
69 { QgsCallout::CornerRadius, QgsPropertyDefinition( "CornerRadius", QObject::tr( "Corner radius" ), QgsPropertyDefinition::DoublePositive, origin ) },
70 { QgsCallout::BlendMode, QgsPropertyDefinition( "BlendMode", QObject::tr( "Callout blend mode" ), QgsPropertyDefinition::BlendMode, origin ) },
71 };
72}
73
74
76{
77}
78
79QVariantMap QgsCallout::properties( const QgsReadWriteContext & ) const
80{
81 QVariantMap props;
82 props.insert( QStringLiteral( "enabled" ), mEnabled ? "1" : "0" );
83 props.insert( QStringLiteral( "anchorPoint" ), encodeAnchorPoint( mAnchorPoint ) );
84 props.insert( QStringLiteral( "labelAnchorPoint" ), encodeLabelAnchorPoint( mLabelAnchorPoint ) );
85 props.insert( QStringLiteral( "blendMode" ), QgsPainting::getBlendModeEnum( mBlendMode ) );
86 props.insert( QStringLiteral( "ddProperties" ), mDataDefinedProperties.toVariant( propertyDefinitions() ) );
87 return props;
88}
89
90void QgsCallout::readProperties( const QVariantMap &props, const QgsReadWriteContext & )
91{
92 mEnabled = props.value( QStringLiteral( "enabled" ), QStringLiteral( "0" ) ).toInt();
93 mAnchorPoint = decodeAnchorPoint( props.value( QStringLiteral( "anchorPoint" ), QString() ).toString() );
94 mLabelAnchorPoint = decodeLabelAnchorPoint( props.value( QStringLiteral( "labelAnchorPoint" ), QString() ).toString() );
96 static_cast< QgsPainting::BlendMode >( props.value( QStringLiteral( "blendMode" ), QString::number( QgsPainting::BlendNormal ) ).toUInt() ) );
97 mDataDefinedProperties.loadVariant( props.value( QStringLiteral( "ddProperties" ) ), propertyDefinitions() );
98}
99
100bool QgsCallout::saveProperties( QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context ) const
101{
102 if ( element.isNull() )
103 {
104 return false;
105 }
106
107 const QDomElement calloutPropsElement = QgsXmlUtils::writeVariant( properties( context ), doc );
108
109 QDomElement calloutElement = doc.createElement( QStringLiteral( "callout" ) );
110 calloutElement.setAttribute( QStringLiteral( "type" ), type() );
111 calloutElement.appendChild( calloutPropsElement );
112
113 element.appendChild( calloutElement );
114 return true;
115}
116
117void QgsCallout::restoreProperties( const QDomElement &element, const QgsReadWriteContext &context )
118{
119 const QVariantMap props = QgsXmlUtils::readVariant( element.firstChildElement() ).toMap();
120 readProperties( props, context );
121}
122
124{
125
126}
128{
129
130}
131
133{
134 return mBlendMode != QPainter::CompositionMode_SourceOver || dataDefinedProperties().isActive( QgsCallout::BlendMode );
135}
136
137QSet<QString> QgsCallout::referencedFields( const QgsRenderContext &context ) const
138{
139 mDataDefinedProperties.prepare( context.expressionContext() );
140 return mDataDefinedProperties.referencedFields( context.expressionContext() );
141}
142
144{
145 return OrderBelowAllLabels;
146}
147
148void QgsCallout::render( QgsRenderContext &context, const QRectF &rect, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext )
149{
150 QPainter *painter = context.painter();
151 if ( context.useAdvancedEffects() )
152 {
153
154 const QPainter::CompositionMode blendMode = mBlendMode;
156 {
157 context.expressionContext().setOriginalValueVariable( QString() );
158 mBlendMode = QgsSymbolLayerUtils::decodeBlendMode( dataDefinedProperties().valueAsString( QgsCallout::BlendMode, context.expressionContext(), QString() ) );
159 }
160
161 painter->setCompositionMode( blendMode );
162 }
163
164#if 0 // for debugging
165 painter->save();
166 painter->setRenderHint( QPainter::Antialiasing, false );
167 painter->translate( rect.center() );
168 painter->rotate( -angle );
169
170 painter->setBrush( QColor( 255, 0, 0, 100 ) );
171 painter->setPen( QColor( 255, 0, 0, 150 ) );
172
173 painter->drawRect( rect.width() * -0.5, rect.height() * -0.5, rect.width(), rect.height() );
174 painter->restore();
175
176 painter->setBrush( QColor( 0, 255, 0, 100 ) );
177 painter->setPen( QColor( 0, 255, 0, 150 ) );
178
179 painter->drawRect( anchor.boundingBox( ).buffered( 30 ).toRectF() );
180#endif
181
182 draw( context, rect, angle, anchor, calloutContext );
183
184 painter->setCompositionMode( QPainter::CompositionMode_SourceOver ); // just to be sure
185}
186
187void QgsCallout::setEnabled( bool enabled )
188{
189 mEnabled = enabled;
190}
191
193{
194 static std::once_flag initialized;
195 std::call_once( initialized, [ = ]( )
196 {
197 initPropertyDefinitions();
198 } );
199 return sPropertyDefinitions;
200}
201
203{
204 if ( ok )
205 *ok = true;
206 const QString cleaned = name.toLower().trimmed();
207
208 if ( cleaned == QLatin1String( "pole_of_inaccessibility" ) )
210 else if ( cleaned == QLatin1String( "point_on_exterior" ) )
211 return PointOnExterior;
212 else if ( cleaned == QLatin1String( "point_on_surface" ) )
213 return PointOnSurface;
214 else if ( cleaned == QLatin1String( "centroid" ) )
215 return Centroid;
216
217 if ( ok )
218 *ok = false;
220}
221
223{
224 switch ( anchor )
225 {
227 return QStringLiteral( "pole_of_inaccessibility" );
228 case PointOnExterior:
229 return QStringLiteral( "point_on_exterior" );
230 case PointOnSurface:
231 return QStringLiteral( "point_on_surface" );
232 case Centroid:
233 return QStringLiteral( "centroid" );
234 }
235 return QString();
236}
237
239{
240 switch ( anchor )
241 {
243 return QStringLiteral( "point_on_exterior" );
244 case LabelCentroid:
245 return QStringLiteral( "centroid" );
246 case LabelTopLeft:
247 return QStringLiteral( "tl" );
248 case LabelTopMiddle:
249 return QStringLiteral( "t" );
250 case LabelTopRight:
251 return QStringLiteral( "tr" );
252 case LabelMiddleLeft:
253 return QStringLiteral( "l" );
254 case LabelMiddleRight:
255 return QStringLiteral( "r" );
256 case LabelBottomLeft:
257 return QStringLiteral( "bl" );
259 return QStringLiteral( "b" );
260 case LabelBottomRight:
261 return QStringLiteral( "br" );
262 }
263
264 return QString();
265}
266
268{
269 if ( ok )
270 *ok = true;
271 const QString cleaned = name.toLower().trimmed();
272
273 if ( cleaned == QLatin1String( "point_on_exterior" ) )
275 else if ( cleaned == QLatin1String( "centroid" ) )
276 return LabelCentroid;
277 else if ( cleaned == QLatin1String( "tl" ) )
278 return LabelTopLeft;
279 else if ( cleaned == QLatin1String( "t" ) )
280 return LabelTopMiddle;
281 else if ( cleaned == QLatin1String( "tr" ) )
282 return LabelTopRight;
283 else if ( cleaned == QLatin1String( "l" ) )
284 return LabelMiddleLeft;
285 else if ( cleaned == QLatin1String( "r" ) )
286 return LabelMiddleRight;
287 else if ( cleaned == QLatin1String( "bl" ) )
288 return LabelBottomLeft;
289 else if ( cleaned == QLatin1String( "b" ) )
290 return LabelBottomMiddle;
291 else if ( cleaned == QLatin1String( "br" ) )
292 return LabelBottomRight;
293
294 if ( ok )
295 *ok = false;
297}
298
299QgsGeometry QgsCallout::labelAnchorGeometry( const QRectF &rect, const double angle, LabelAnchorPoint anchor ) const
300{
301 QgsGeometry label;
302 switch ( anchor )
303 {
305 label = QgsGeometry::fromRect( rect );
306 break;
307
308 case LabelCentroid:
309 label = QgsGeometry::fromRect( rect ).centroid();
310 break;
311
312 case LabelTopLeft:
313 label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomLeft() ) );
314 break;
315
316 case LabelTopMiddle:
317 label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.bottom() ) );
318 break;
319
320 case LabelTopRight:
321 label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomRight() ) );
322 break;
323
324 case LabelMiddleLeft:
325 label = QgsGeometry::fromPointXY( QgsPointXY( rect.left(), ( rect.top() + rect.bottom() ) / 2.0 ) );
326 break;
327
328 case LabelMiddleRight:
329 label = QgsGeometry::fromPointXY( QgsPointXY( rect.right(), ( rect.top() + rect.bottom() ) / 2.0 ) );
330 break;
331
332 case LabelBottomLeft:
333 label = QgsGeometry::fromPointXY( QgsPointXY( rect.topLeft() ) );
334 break;
335
337 label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.top() ) );
338 break;
339
340 case LabelBottomRight:
341 label = QgsGeometry::fromPointXY( QgsPointXY( rect.topRight() ) );
342 break;
343 }
344
345 label.rotate( angle, rect.topLeft() );
346 return label;
347}
348
349QgsGeometry QgsCallout::calloutLabelPoint( const QRectF &rect, const double angle, QgsCallout::LabelAnchorPoint anchor, QgsRenderContext &context, const QgsCallout::QgsCalloutContext &calloutContext, bool &pinned ) const
350{
351 pinned = false;
353 {
354 bool ok = false;
355 const double x = dataDefinedProperties().valueAsDouble( QgsCallout::OriginX, context.expressionContext(), 0, &ok );
356 if ( ok )
357 {
358 const double y = dataDefinedProperties().valueAsDouble( QgsCallout::OriginY, context.expressionContext(), 0, &ok );
359 if ( ok )
360 {
361 pinned = true;
362 // data defined label point, use it directly
363 QgsGeometry labelPoint = QgsGeometry::fromPointXY( QgsPointXY( x, y ) );
364 try
365 {
366 labelPoint.transform( calloutContext.originalFeatureToMapTransform( context ) );
367 labelPoint.transform( context.mapToPixel().transform() );
368 }
369 catch ( QgsCsException & )
370 {
371 return QgsGeometry();
372 }
373 return labelPoint;
374 }
375 }
376 }
377
378 QgsGeometry label;
379 switch ( anchor )
380 {
382 label = QgsGeometry::fromRect( rect );
383 break;
384
385 case LabelCentroid:
386 label = QgsGeometry::fromRect( rect ).centroid();
387 break;
388
389 case LabelTopLeft:
390 label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomLeft() ) );
391 break;
392
393 case LabelTopMiddle:
394 label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.bottom() ) );
395 break;
396
397 case LabelTopRight:
398 label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomRight() ) );
399 break;
400
401 case LabelMiddleLeft:
402 label = QgsGeometry::fromPointXY( QgsPointXY( rect.left(), ( rect.top() + rect.bottom() ) / 2.0 ) );
403 break;
404
405 case LabelMiddleRight:
406 label = QgsGeometry::fromPointXY( QgsPointXY( rect.right(), ( rect.top() + rect.bottom() ) / 2.0 ) );
407 break;
408
409 case LabelBottomLeft:
410 label = QgsGeometry::fromPointXY( QgsPointXY( rect.topLeft() ) );
411 break;
412
414 label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.top() ) );
415 break;
416
417 case LabelBottomRight:
418 label = QgsGeometry::fromPointXY( QgsPointXY( rect.topRight() ) );
419 break;
420 }
421
422 label.rotate( angle, rect.topLeft() );
423 return label;
424}
425
426QgsGeometry QgsCallout::calloutLineToPart( const QgsGeometry &labelGeometry, const QgsAbstractGeometry *partGeometry, QgsRenderContext &context, const QgsCalloutContext &calloutContext, bool &pinned ) const
427{
428 pinned = false;
429 AnchorPoint anchor = anchorPoint();
430 const QgsAbstractGeometry *evaluatedPartAnchor = partGeometry;
431 std::unique_ptr< QgsAbstractGeometry > tempPartAnchor;
432
434 {
435 bool ok = false;
436 const double x = dataDefinedProperties().valueAsDouble( QgsCallout::DestinationX, context.expressionContext(), 0, &ok );
437 if ( ok )
438 {
439 const double y = dataDefinedProperties().valueAsDouble( QgsCallout::DestinationY, context.expressionContext(), 0, &ok );
440 if ( ok )
441 {
442 pinned = true;
443 tempPartAnchor = std::make_unique< QgsPoint >( QgsWkbTypes::Point, x, y );
444 evaluatedPartAnchor = tempPartAnchor.get();
445 try
446 {
447 tempPartAnchor->transform( calloutContext.originalFeatureToMapTransform( context ) );
448 tempPartAnchor->transform( context.mapToPixel().transform() );
449 }
450 catch ( QgsCsException & )
451 {
452 evaluatedPartAnchor = partGeometry;
453 }
454 }
455 }
456 }
457
459 {
460 const QString encodedAnchor = encodeAnchorPoint( anchor );
461 context.expressionContext().setOriginalValueVariable( encodedAnchor );
462 anchor = decodeAnchorPoint( dataDefinedProperties().valueAsString( QgsCallout::AnchorPointPosition, context.expressionContext(), encodedAnchor ) );
463 }
464
465 QgsGeometry line;
466 const QgsGeos labelGeos( labelGeometry.constGet() );
467
468 switch ( QgsWkbTypes::geometryType( evaluatedPartAnchor->wkbType() ) )
469 {
472 {
473 line = labelGeos.shortestLine( evaluatedPartAnchor );
474 break;
475 }
476
478 {
479 if ( labelGeos.intersects( evaluatedPartAnchor ) )
480 return QgsGeometry();
481
482 // ideally avoid this unwanted clone in future. For now we need it because poleOfInaccessibility/pointOnSurface are
483 // only available to QgsGeometry objects
484 const QgsGeometry evaluatedPartAnchorGeom( evaluatedPartAnchor->clone() );
485 switch ( anchor )
486 {
488 line = labelGeos.shortestLine( evaluatedPartAnchorGeom.poleOfInaccessibility( std::max( evaluatedPartAnchor->boundingBox().width(), evaluatedPartAnchor->boundingBox().height() ) / 20.0 ) ); // really rough (but quick) pole of inaccessibility
489 break;
491 line = labelGeos.shortestLine( evaluatedPartAnchorGeom.pointOnSurface() );
492 break;
494 line = labelGeos.shortestLine( evaluatedPartAnchor );
495 break;
497 line = labelGeos.shortestLine( evaluatedPartAnchorGeom.centroid() );
498 break;
499 }
500 break;
501 }
502
505 return QgsGeometry(); // shouldn't even get here..
506 }
507 return line;
508}
509
510//
511// QgsCallout::QgsCalloutContext
512//
513
515{
516 if ( !mOriginalFeatureToMapTransform.isValid() )
517 {
518 // lazy initialization, only create if needed...
519 mOriginalFeatureToMapTransform = QgsCoordinateTransform( originalFeatureCrs, renderContext.coordinateTransform().destinationCrs(), renderContext.transformContext() );
520 }
521 return mOriginalFeatureToMapTransform;
522}
523
524
525//
526// QgsSimpleLineCallout
527//
528
530{
531 mLineSymbol = std::make_unique< QgsLineSymbol >( QgsSymbolLayerList() << new QgsSimpleLineSymbolLayer( QColor( 60, 60, 60 ), .3 ) );
532
533}
534
536
538 : QgsCallout( other )
539 , mLineSymbol( other.mLineSymbol ? other.mLineSymbol->clone() : nullptr )
540 , mMinCalloutLength( other.mMinCalloutLength )
541 , mMinCalloutLengthUnit( other.mMinCalloutLengthUnit )
542 , mMinCalloutLengthScale( other.mMinCalloutLengthScale )
543 , mOffsetFromAnchorDistance( other.mOffsetFromAnchorDistance )
544 , mOffsetFromAnchorUnit( other.mOffsetFromAnchorUnit )
545 , mOffsetFromAnchorScale( other.mOffsetFromAnchorScale )
546 , mOffsetFromLabelDistance( other.mOffsetFromLabelDistance )
547 , mOffsetFromLabelUnit( other.mOffsetFromLabelUnit )
548 , mOffsetFromLabelScale( other.mOffsetFromLabelScale )
549 , mDrawCalloutToAllParts( other.mDrawCalloutToAllParts )
550{
551
552}
553
554QgsCallout *QgsSimpleLineCallout::create( const QVariantMap &properties, const QgsReadWriteContext &context )
555{
556 std::unique_ptr< QgsSimpleLineCallout > callout = std::make_unique< QgsSimpleLineCallout >();
557 callout->readProperties( properties, context );
558 return callout.release();
559}
560
562{
563 return QStringLiteral( "simple" );
564}
565
567{
568 return new QgsSimpleLineCallout( *this );
569}
570
571QVariantMap QgsSimpleLineCallout::properties( const QgsReadWriteContext &context ) const
572{
573 QVariantMap props = QgsCallout::properties( context );
574
575 if ( mLineSymbol )
576 {
577 props[ QStringLiteral( "lineSymbol" ) ] = QgsSymbolLayerUtils::symbolProperties( mLineSymbol.get() );
578 }
579 props[ QStringLiteral( "minLength" ) ] = mMinCalloutLength;
580 props[ QStringLiteral( "minLengthUnit" ) ] = QgsUnitTypes::encodeUnit( mMinCalloutLengthUnit );
581 props[ QStringLiteral( "minLengthMapUnitScale" ) ] = QgsSymbolLayerUtils::encodeMapUnitScale( mMinCalloutLengthScale );
582
583 props[ QStringLiteral( "offsetFromAnchor" ) ] = mOffsetFromAnchorDistance;
584 props[ QStringLiteral( "offsetFromAnchorUnit" ) ] = QgsUnitTypes::encodeUnit( mOffsetFromAnchorUnit );
585 props[ QStringLiteral( "offsetFromAnchorMapUnitScale" ) ] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetFromAnchorScale );
586 props[ QStringLiteral( "offsetFromLabel" ) ] = mOffsetFromLabelDistance;
587 props[ QStringLiteral( "offsetFromLabelUnit" ) ] = QgsUnitTypes::encodeUnit( mOffsetFromLabelUnit );
588 props[ QStringLiteral( "offsetFromLabelMapUnitScale" ) ] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetFromLabelScale );
589
590 props[ QStringLiteral( "drawToAllParts" ) ] = mDrawCalloutToAllParts;
591
592 return props;
593}
594
595void QgsSimpleLineCallout::readProperties( const QVariantMap &props, const QgsReadWriteContext &context )
596{
597 QgsCallout::readProperties( props, context );
598
599 const QString lineSymbolDef = props.value( QStringLiteral( "lineSymbol" ) ).toString();
600 QDomDocument doc( QStringLiteral( "symbol" ) );
601 doc.setContent( lineSymbolDef );
602 const QDomElement symbolElem = doc.firstChildElement( QStringLiteral( "symbol" ) );
603 std::unique_ptr< QgsLineSymbol > lineSymbol( QgsSymbolLayerUtils::loadSymbol< QgsLineSymbol >( symbolElem, context ) );
604 if ( lineSymbol )
605 mLineSymbol = std::move( lineSymbol );
606
607 mMinCalloutLength = props.value( QStringLiteral( "minLength" ), 0 ).toDouble();
608 mMinCalloutLengthUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "minLengthUnit" ) ).toString() );
609 mMinCalloutLengthScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "minLengthMapUnitScale" ) ).toString() );
610
611 mOffsetFromAnchorDistance = props.value( QStringLiteral( "offsetFromAnchor" ), 0 ).toDouble();
612 mOffsetFromAnchorUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "offsetFromAnchorUnit" ) ).toString() );
613 mOffsetFromAnchorScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "offsetFromAnchorMapUnitScale" ) ).toString() );
614 mOffsetFromLabelDistance = props.value( QStringLiteral( "offsetFromLabel" ), 0 ).toDouble();
615 mOffsetFromLabelUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "offsetFromLabelUnit" ) ).toString() );
616 mOffsetFromLabelScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "offsetFromLabelMapUnitScale" ) ).toString() );
617
618 mDrawCalloutToAllParts = props.value( QStringLiteral( "drawToAllParts" ), false ).toBool();
619}
620
622{
623 QgsCallout::startRender( context );
624 if ( mLineSymbol )
625 mLineSymbol->startRender( context );
626}
627
629{
630 QgsCallout::stopRender( context );
631 if ( mLineSymbol )
632 mLineSymbol->stopRender( context );
633}
634
635QSet<QString> QgsSimpleLineCallout::referencedFields( const QgsRenderContext &context ) const
636{
637 QSet<QString> fields = QgsCallout::referencedFields( context );
638 if ( mLineSymbol )
639 fields.unite( mLineSymbol->usedAttributes( context ) );
640 return fields;
641}
642
644{
645 return mLineSymbol.get();
646}
647
649{
650 mLineSymbol.reset( symbol );
651}
652
653void QgsSimpleLineCallout::draw( QgsRenderContext &context, const QRectF &rect, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext )
654{
655 LabelAnchorPoint labelAnchor = labelAnchorPoint();
657 {
658 const QString encodedAnchor = encodeLabelAnchorPoint( labelAnchor );
659 context.expressionContext().setOriginalValueVariable( encodedAnchor );
660 labelAnchor = decodeLabelAnchorPoint( dataDefinedProperties().valueAsString( QgsCallout::LabelAnchorPointPosition, context.expressionContext(), encodedAnchor ) );
661 }
662
663 bool originPinned = false;
664 const QgsGeometry label = calloutLabelPoint( rect, angle, labelAnchor, context, calloutContext, originPinned );
665 if ( label.isNull() )
666 return;
667
668 auto drawCalloutLine = [this, &context, &calloutContext, &label, &rect, angle, &anchor, originPinned]( const QgsAbstractGeometry * partAnchor )
669 {
670 bool destinationPinned = false;
671 const QgsGeometry line = calloutLineToPart( label, partAnchor, context, calloutContext, destinationPinned );
672 if ( line.isEmpty() )
673 return;
674
675 const double lineLength = line.length();
676 if ( qgsDoubleNear( lineLength, 0 ) )
677 return;
678
679 double minLength = mMinCalloutLength;
681 {
682 context.expressionContext().setOriginalValueVariable( minLength );
684 }
685 const double minLengthPixels = context.convertToPainterUnits( minLength, mMinCalloutLengthUnit, mMinCalloutLengthScale );
686 if ( minLengthPixels > 0 && lineLength < minLengthPixels )
687 return; // too small!
688
689 std::unique_ptr< QgsCurve > calloutCurve( createCalloutLine( qgsgeometry_cast< const QgsLineString * >( line.constGet() )->startPoint(),
690 qgsgeometry_cast< const QgsLineString * >( line.constGet() )->endPoint(), context, rect, angle, anchor, calloutContext ) );
691
692 double offsetFromAnchor = mOffsetFromAnchorDistance;
694 {
697 }
698 const double offsetFromAnchorPixels = context.convertToPainterUnits( offsetFromAnchor, mOffsetFromAnchorUnit, mOffsetFromAnchorScale );
699
700 double offsetFromLabel = mOffsetFromLabelDistance;
702 {
705 }
706 const double offsetFromLabelPixels = context.convertToPainterUnits( offsetFromLabel, mOffsetFromLabelUnit, mOffsetFromLabelScale );
707 if ( offsetFromAnchorPixels > 0 || offsetFromLabelPixels > 0 )
708 {
709 calloutCurve.reset( calloutCurve->curveSubstring( offsetFromLabelPixels, calloutCurve->length() - offsetFromAnchorPixels ) );
710 }
711
712 const QPolygonF points = calloutCurve->asQPolygonF();
713
714 if ( points.empty() )
715 return;
716
717 QgsCalloutPosition position;
718 position.setOrigin( context.mapToPixel().toMapCoordinates( points.at( 0 ).x(), points.at( 0 ).y() ).toQPointF() );
719 position.setOriginIsPinned( originPinned );
720 position.setDestination( context.mapToPixel().toMapCoordinates( points.constLast().x(), points.constLast().y() ).toQPointF() );
721 position.setDestinationIsPinned( destinationPinned );
722 calloutContext.addCalloutPosition( position );
723
724 mLineSymbol->renderPolyline( points, nullptr, context );
725 };
726
727 bool toAllParts = mDrawCalloutToAllParts;
729 {
730 context.expressionContext().setOriginalValueVariable( toAllParts );
732 }
733
734 if ( calloutContext.allFeaturePartsLabeled || !toAllParts )
735 drawCalloutLine( anchor.constGet() );
736 else
737 {
738 for ( auto it = anchor.const_parts_begin(); it != anchor.const_parts_end(); ++it )
739 drawCalloutLine( *it );
740 }
741}
742
743QgsCurve *QgsSimpleLineCallout::createCalloutLine( const QgsPoint &start, const QgsPoint &end, QgsRenderContext &, const QRectF &, const double, const QgsGeometry &, QgsCallout::QgsCalloutContext & ) const
744{
745 return new QgsLineString( start, end );
746}
747
748//
749// QgsManhattanLineCallout
750//
751
753{
754}
755
757 : QgsSimpleLineCallout( other )
758{
759
760}
761
762
763QgsCallout *QgsManhattanLineCallout::create( const QVariantMap &properties, const QgsReadWriteContext &context )
764{
765 std::unique_ptr< QgsManhattanLineCallout > callout = std::make_unique< QgsManhattanLineCallout >();
766 callout->readProperties( properties, context );
767 return callout.release();
768}
769
771{
772 return QStringLiteral( "manhattan" );
773}
774
776{
777 return new QgsManhattanLineCallout( *this );
778}
779
780QgsCurve *QgsManhattanLineCallout::createCalloutLine( const QgsPoint &start, const QgsPoint &end, QgsRenderContext &, const QRectF &, const double, const QgsGeometry &, QgsCallout::QgsCalloutContext & ) const
781{
782 const QgsPoint mid1 = QgsPoint( start.x(), end.y() );
783 return new QgsLineString( QVector< QgsPoint >() << start << mid1 << end );
784}
785
786
787//
788// QgsCurvedLineCallout
789//
790
792{
793}
794
796 : QgsSimpleLineCallout( other )
797 , mOrientation( other.mOrientation )
798 , mCurvature( other.mCurvature )
799{
800
801}
802
803QgsCallout *QgsCurvedLineCallout::create( const QVariantMap &properties, const QgsReadWriteContext &context )
804{
805 std::unique_ptr< QgsCurvedLineCallout > callout = std::make_unique< QgsCurvedLineCallout >();
806 callout->readProperties( properties, context );
807
808 callout->setCurvature( properties.value( QStringLiteral( "curvature" ), 0.1 ).toDouble() );
809 callout->setOrientation( decodeOrientation( properties.value( QStringLiteral( "orientation" ), QStringLiteral( "auto" ) ).toString() ) );
810
811 return callout.release();
812}
813
815{
816 return QStringLiteral( "curved" );
817}
818
820{
821 return new QgsCurvedLineCallout( *this );
822}
823
824QVariantMap QgsCurvedLineCallout::properties( const QgsReadWriteContext &context ) const
825{
826 QVariantMap props = QgsSimpleLineCallout::properties( context );
827 props.insert( QStringLiteral( "curvature" ), mCurvature );
828 props.insert( QStringLiteral( "orientation" ), encodeOrientation( mOrientation ) );
829 return props;
830}
831
832QgsCurve *QgsCurvedLineCallout::createCalloutLine( const QgsPoint &start, const QgsPoint &end, QgsRenderContext &context, const QRectF &rect, const double, const QgsGeometry &, QgsCallout::QgsCalloutContext & ) const
833{
834 double curvature = mCurvature * 100;
836 {
839 }
840
841 Orientation orientation = mOrientation;
843 {
844 bool ok = false;
845 const QString orientationString = dataDefinedProperties().property( QgsCallout::Orientation ).valueAsString( context.expressionContext(), QString(), &ok );
846 if ( ok )
847 {
848 orientation = decodeOrientation( orientationString );
849 }
850 }
851
852 if ( orientation == Automatic )
853 {
854 // to calculate automatically the best curve orientation, we first check which side of the label bounding box
855 // the callout origin is nearest to
856 switch ( QgsGeometryUtils::closestSideOfRectangle( rect.right(), rect.bottom(), rect.left(), rect.top(), start.x(), start.y() ) )
857 {
858 case 1:
859 // closest to bottom
860 if ( qgsDoubleNear( end.x(), start.x() ) )
861 {
862 // if vertical line, we bend depending on whether the line sits towards the left or right side of the label
863 if ( start.x() < ( rect.left() + 0.5 * rect.width() ) )
865 else
867 }
868 else if ( end.x() > start.x() )
870 else
872 break;
873
874 case 2:
875 // closest to bottom-right
876 if ( end.x() < start.x() )
878 else if ( end.y() < start.y() )
880 else if ( end.x() - start.x() < end.y() - start.y() )
882 else
884 break;
885
886 case 3:
887 // closest to right
888 if ( qgsDoubleNear( end.y(), start.y() ) )
889 {
890 // if horizontal line, we bend depending on whether the line sits towards the top or bottom side of the label
891 if ( start.y() < ( rect.top() + 0.5 * rect.height() ) )
893 else
895 }
896 else if ( end.y() < start.y() )
898 else
900 break;
901
902 case 4:
903 // closest to top-right
904 if ( end.x() < start.x() )
906 else if ( end.y() > start.y() )
908 else if ( end.x() - start.x() < start.y() - end.y() )
910 else
912 break;
913
914 case 5:
915 // closest to top
916 if ( qgsDoubleNear( end.x(), start.x() ) )
917 {
918 // if vertical line, we bend depending on whether the line sits towards the left or right side of the label
919 if ( start.x() < ( rect.left() + 0.5 * rect.width() ) )
921 else
923 }
924 else if ( end.x() < start.x() )
926 else
928 break;
929
930 case 6:
931 // closest to top-left
932 if ( end.x() > start.x() )
934 else if ( end.y() > start.y() )
936 else if ( start.x() - end.x() < start.y() - end.y() )
938 else
940 break;
941
942 case 7:
943 //closest to left
944 if ( qgsDoubleNear( end.y(), start.y() ) )
945 {
946 // if horizontal line, we bend depending on whether the line sits towards the top or bottom side of the label
947 if ( start.y() < ( rect.top() + 0.5 * rect.height() ) )
949 else
951 }
952 else if ( end.y() > start.y() )
954 else
956 break;
957
958 case 8:
959 //closest to bottom-left
960 if ( end.x() > start.x() )
962 else if ( end.y() < start.y() )
964 else if ( start.x() - end.x() < end.y() - start.y() )
966 else
968 break;
969 }
970 }
971
972 // turn the line into a curved line. We do this by creating a circular string from the callout line's
973 // start to end point, where the curve point is in the middle of the callout line and perpendicularly offset
974 // by a proportion of the overall callout line length
975 const double distance = ( orientation == Clockwise ? 1 : -1 ) * start.distance( end ) * curvature / 100.0;
976 double midX, midY;
977 QgsGeometryUtils::perpendicularOffsetPointAlongSegment( start.x(), start.y(), end.x(), end.y(), 0.5, distance, &midX, &midY );
978
979 return new QgsCircularString( start, QgsPoint( midX, midY ), end );
980}
981
982QgsCurvedLineCallout::Orientation QgsCurvedLineCallout::decodeOrientation( const QString &string )
983{
984 const QString cleaned = string.toLower().trimmed();
985 if ( cleaned == QLatin1String( "auto" ) )
986 return Automatic;
987 if ( cleaned == QLatin1String( "clockwise" ) )
988 return Clockwise;
989 if ( cleaned == QLatin1String( "counterclockwise" ) )
990 return CounterClockwise;
991 return Automatic;
992}
993
994QString QgsCurvedLineCallout::encodeOrientation( QgsCurvedLineCallout::Orientation orientation )
995{
996 switch ( orientation )
997 {
999 return QStringLiteral( "auto" );
1001 return QStringLiteral( "clockwise" );
1003 return QStringLiteral( "counterclockwise" );
1004 }
1005 return QString();
1006}
1007
1009{
1010 return mOrientation;
1011}
1012
1014{
1015 mOrientation = orientation;
1016}
1017
1019{
1020 return mCurvature;
1021}
1022
1024{
1025 mCurvature = curvature;
1026}
1027
1028
1029
1030//
1031// QgsBalloonCallout
1032//
1033
1035{
1036 mFillSymbol = std::make_unique< QgsFillSymbol >( QgsSymbolLayerList() << new QgsSimpleFillSymbolLayer( QColor( 255, 200, 60 ) ) );
1037}
1038
1040
1042 : QgsCallout( other )
1043 , mFillSymbol( other.mFillSymbol ? other.mFillSymbol->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 std::unique_ptr< QgsBalloonCallout > 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 props[ QStringLiteral( "offsetFromAnchor" ) ] = mOffsetFromAnchorDistance;
1086 props[ QStringLiteral( "offsetFromAnchorUnit" ) ] = QgsUnitTypes::encodeUnit( mOffsetFromAnchorUnit );
1087 props[ QStringLiteral( "offsetFromAnchorMapUnitScale" ) ] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetFromAnchorScale );
1088
1089 props[ QStringLiteral( "margins" ) ] = mMargins.toString();
1090 props[ QStringLiteral( "marginsUnit" ) ] = QgsUnitTypes::encodeUnit( mMarginUnit );
1091
1092 props[ QStringLiteral( "wedgeWidth" ) ] = mWedgeWidth;
1093 props[ QStringLiteral( "wedgeWidthUnit" ) ] = QgsUnitTypes::encodeUnit( mWedgeWidthUnit );
1094 props[ QStringLiteral( "wedgeWidthMapUnitScale" ) ] = QgsSymbolLayerUtils::encodeMapUnitScale( mWedgeWidthScale );
1095
1096 props[ QStringLiteral( "cornerRadius" ) ] = mCornerRadius;
1097 props[ QStringLiteral( "cornerRadiusUnit" ) ] = QgsUnitTypes::encodeUnit( mCornerRadiusUnit );
1098 props[ QStringLiteral( "cornerRadiusMapUnitScale" ) ] = QgsSymbolLayerUtils::encodeMapUnitScale( mCornerRadiusScale );
1099
1100 return props;
1101}
1102
1103void QgsBalloonCallout::readProperties( const QVariantMap &props, const QgsReadWriteContext &context )
1104{
1105 QgsCallout::readProperties( props, context );
1106
1107 const QString fillSymbolDef = props.value( QStringLiteral( "fillSymbol" ) ).toString();
1108 QDomDocument doc( QStringLiteral( "symbol" ) );
1109 doc.setContent( fillSymbolDef );
1110 const QDomElement symbolElem = doc.firstChildElement( QStringLiteral( "symbol" ) );
1111 std::unique_ptr< QgsFillSymbol > fillSymbol( QgsSymbolLayerUtils::loadSymbol< QgsFillSymbol >( symbolElem, context ) );
1112 if ( fillSymbol )
1113 mFillSymbol = std::move( fillSymbol );
1114
1115 mOffsetFromAnchorDistance = props.value( QStringLiteral( "offsetFromAnchor" ), 0 ).toDouble();
1116 mOffsetFromAnchorUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "offsetFromAnchorUnit" ) ).toString() );
1117 mOffsetFromAnchorScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "offsetFromAnchorMapUnitScale" ) ).toString() );
1118
1119 mMargins = QgsMargins::fromString( props.value( QStringLiteral( "margins" ) ).toString() );
1120 mMarginUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "marginsUnit" ) ).toString() );
1121
1122 mWedgeWidth = props.value( QStringLiteral( "wedgeWidth" ), 2.64 ).toDouble();
1123 mWedgeWidthUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "wedgeWidthUnit" ) ).toString() );
1124 mWedgeWidthScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "wedgeWidthMapUnitScale" ) ).toString() );
1125
1126 mCornerRadius = props.value( QStringLiteral( "cornerRadius" ), 0 ).toDouble();
1127 mCornerRadiusUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "cornerRadiusUnit" ) ).toString() );
1128 mCornerRadiusScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "cornerRadiusMapUnitScale" ) ).toString() );
1129}
1130
1132{
1133 QgsCallout::startRender( context );
1134 if ( mFillSymbol )
1135 mFillSymbol->startRender( context );
1136}
1137
1139{
1140 QgsCallout::stopRender( context );
1141 if ( mFillSymbol )
1142 mFillSymbol->stopRender( context );
1143}
1144
1145QSet<QString> QgsBalloonCallout::referencedFields( const QgsRenderContext &context ) const
1146{
1147 QSet<QString> fields = QgsCallout::referencedFields( context );
1148 if ( mFillSymbol )
1149 fields.unite( mFillSymbol->usedAttributes( context ) );
1150 return fields;
1151}
1152
1154{
1155 return mFillSymbol.get();
1156}
1157
1159{
1160 mFillSymbol.reset( symbol );
1161}
1162
1163void QgsBalloonCallout::draw( QgsRenderContext &context, const QRectF &rect, const double, const QgsGeometry &anchor, QgsCalloutContext &calloutContext )
1164{
1165 bool destinationIsPinned = false;
1166 QgsGeometry line = calloutLineToPart( QgsGeometry::fromRect( rect ), anchor.constGet(), context, calloutContext, destinationIsPinned );
1167
1168 double offsetFromAnchor = mOffsetFromAnchorDistance;
1170 {
1173 }
1174 const double offsetFromAnchorPixels = context.convertToPainterUnits( offsetFromAnchor, mOffsetFromAnchorUnit, mOffsetFromAnchorScale );
1175
1176 if ( offsetFromAnchorPixels > 0 )
1177 {
1178 if ( const QgsLineString *ls = qgsgeometry_cast< const QgsLineString * >( line.constGet() ) )
1179 {
1180 line = QgsGeometry( ls->curveSubstring( 0, ls->length() - offsetFromAnchorPixels ) );
1181 }
1182 }
1183
1184 QgsPointXY destination;
1185 QgsPointXY origin;
1186 if ( const QgsLineString *ls = qgsgeometry_cast< const QgsLineString * >( line.constGet() ) )
1187 {
1188 origin = ls->startPoint();
1189 destination = ls->endPoint();
1190 }
1191 else
1192 {
1193 destination = QgsPointXY( rect.center() );
1194 }
1195
1196 const QPolygonF points = getPoints( context, destination, rect );
1197 if ( points.empty() )
1198 return;
1199
1200 if ( !origin.isEmpty() )
1201 {
1202 QgsCalloutPosition position;
1203 position.setOrigin( context.mapToPixel().toMapCoordinates( origin.x(), origin.y() ).toQPointF() );
1204 position.setOriginIsPinned( false );
1205 position.setDestination( context.mapToPixel().toMapCoordinates( destination.x(), destination.y() ).toQPointF() );
1206 position.setDestinationIsPinned( destinationIsPinned );
1207 calloutContext.addCalloutPosition( position );
1208 }
1209
1210 mFillSymbol->renderPolygon( points, nullptr, nullptr, context );
1211}
1212
1213QPolygonF QgsBalloonCallout::getPoints( QgsRenderContext &context, QgsPointXY origin, QRectF rect ) const
1214{
1215 double segmentPointWidth = mWedgeWidth;
1217 {
1218 context.expressionContext().setOriginalValueVariable( segmentPointWidth );
1219 segmentPointWidth = dataDefinedProperties().valueAsDouble( QgsCallout::WedgeWidth, context.expressionContext(), segmentPointWidth );
1220 }
1221 segmentPointWidth = context.convertToPainterUnits( segmentPointWidth, mWedgeWidthUnit, mWedgeWidthScale );
1222
1223 double cornerRadius = mCornerRadius;
1225 {
1228 }
1229 cornerRadius = context.convertToPainterUnits( cornerRadius, mCornerRadiusUnit, mCornerRadiusScale );
1230
1231 double left = mMargins.left();
1232 double right = mMargins.right();
1233 double top = mMargins.top();
1234 double bottom = mMargins.bottom();
1235
1236 if ( dataDefinedProperties().isActive( QgsCallout::Margins ) )
1237 {
1238 const QVariant value = dataDefinedProperties().value( QgsCallout::Margins, context.expressionContext() );
1239 if ( !QgsVariantUtils::isNull( value ) )
1240 {
1241 if ( value.type() == QVariant::List )
1242 {
1243 const QVariantList list = value.toList();
1244 if ( list.size() == 4 )
1245 {
1246 bool topOk = false;
1247 bool rightOk = false;
1248 bool bottomOk = false;
1249 bool leftOk = false;
1250 const double evaluatedTop = list.at( 0 ).toDouble( &topOk );
1251 const double evaluatedRight = list.at( 1 ).toDouble( &rightOk );
1252 const double evaluatedBottom = list.at( 2 ).toDouble( &bottomOk );
1253 const double evaluatedLeft = list.at( 3 ).toDouble( &leftOk );
1254 if ( topOk && rightOk && bottomOk && leftOk )
1255 {
1256 left = evaluatedLeft;
1257 top = evaluatedTop;
1258 right = evaluatedRight;
1259 bottom = evaluatedBottom;
1260 }
1261 }
1262 }
1263 else
1264 {
1265 const QStringList list = value.toString().trimmed().split( ',' );
1266 if ( list.count() == 4 )
1267 {
1268 bool topOk = false;
1269 bool rightOk = false;
1270 bool bottomOk = false;
1271 bool leftOk = false;
1272 const double evaluatedTop = list.at( 0 ).toDouble( &topOk );
1273 const double evaluatedRight = list.at( 1 ).toDouble( &rightOk );
1274 const double evaluatedBottom = list.at( 2 ).toDouble( &bottomOk );
1275 const double evaluatedLeft = list.at( 3 ).toDouble( &leftOk );
1276 if ( topOk && rightOk && bottomOk && leftOk )
1277 {
1278 left = evaluatedLeft;
1279 top = evaluatedTop;
1280 right = evaluatedRight;
1281 bottom = evaluatedBottom;
1282 }
1283 }
1284 }
1285 }
1286 }
1287
1288 const double marginLeft = context.convertToPainterUnits( left, mMarginUnit );
1289 const double marginRight = context.convertToPainterUnits( right, mMarginUnit );
1290 const double marginTop = context.convertToPainterUnits( top, mMarginUnit );
1291 const double marginBottom = context.convertToPainterUnits( bottom, mMarginUnit );
1292
1293 const QRectF expandedRect = rect.height() < 0 ?
1294 QRectF( rect.left() - marginLeft, rect.top() + marginBottom,
1295 rect.width() + marginLeft + marginRight,
1296 rect.height() - marginTop - marginBottom ) :
1297 QRectF( rect.left() - marginLeft, rect.top() - marginTop,
1298 rect.width() + marginLeft + marginRight,
1299 rect.height() + marginTop + marginBottom );
1300
1301 // IMPORTANT -- check for degenerate height is sometimes >=0, because QRectF are not normalized and we are using painter
1302 // coordinates with descending vertical axis!
1303 if ( expandedRect.width() <= 0 || ( rect.height() < 0 && expandedRect.height() >= 0 ) || ( rect.height() > 0 && expandedRect.height() <= 0 ) )
1304 return QPolygonF();
1305
1306 const QPainterPath path = QgsShapeGenerator::createBalloon( origin, expandedRect, segmentPointWidth, cornerRadius );
1307 const QTransform t = QTransform::fromScale( 100, 100 );
1308 const QTransform ti = t.inverted();
1309 const QPolygonF poly = path.toFillPolygon( t );
1310 return ti.map( poly );
1311}
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.
QgsWkbTypes::Type 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:514
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:222
bool containsAdvancedEffects() const
Returns true if the callout requires advanced effects such as blend modes, which require output in ra...
Definition: qgscallout.cpp:132
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:100
virtual void stopRender(QgsRenderContext &context)
Finalises the callout after a set of rendering operations on the specified render context.
Definition: qgscallout.cpp:127
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:426
void setEnabled(bool enabled)
Sets whether the callout is enabled.
Definition: qgscallout.cpp:187
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:349
static QgsPropertiesDefinition propertyDefinitions()
Returns the definitions for data defined properties available for use in callouts.
Definition: qgscallout.cpp:192
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:148
virtual void restoreProperties(const QDomElement &element, const QgsReadWriteContext &context)
Restores the callout's properties from a DOM element.
Definition: qgscallout.cpp:117
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:238
virtual void startRender(QgsRenderContext &context)
Prepares the callout for rendering on the specified render context.
Definition: qgscallout.cpp:123
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:267
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:299
QgsCallout()
Constructor for QgsCallout.
Definition: qgscallout.cpp:75
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:143
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:202
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:90
virtual QSet< QString > referencedFields(const QgsRenderContext &context) const
Returns the set of attributes referenced by the callout.
Definition: qgscallout.cpp:137
@ 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:79
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:819
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:832
QVariantMap properties(const QgsReadWriteContext &context) const override
Returns the properties describing the callout encoded in a string format.
Definition: qgscallout.cpp:824
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:803
QString type() const override
Returns a unique string representing the callout type.
Definition: qgscallout.cpp:814
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:2753
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:770
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:780
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:763
QgsManhattanLineCallout * clone() const override
Duplicates a callout by creating a deep copy of the callout.
Definition: qgscallout.cpp:775
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 QgsPainting::BlendMode getBlendModeEnum(QPainter::CompositionMode blendMode)
Returns a BlendMode corresponding to a QPainter::CompositionMode.
Definition: qgspainting.cpp:80
static QPainter::CompositionMode getCompositionMode(QgsPainting::BlendMode blendMode)
Returns a QPainter::CompositionMode corresponding to a BlendMode.
Definition: qgspainting.cpp:20
BlendMode
Blending modes enum defining the available composition modes that can be used when rendering a layer.
Definition: qgspainting.h:37
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.
QPainter * painter()
Returns the destination QPainter for the render operation.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
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:621
void setLineSymbol(QgsLineSymbol *symbol)
Sets the line symbol used to render the callout line.
Definition: qgscallout.cpp:648
void stopRender(QgsRenderContext &context) override
Finalises the callout after a set of rendering operations on the specified render context.
Definition: qgscallout.cpp:628
QSet< QString > referencedFields(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the callout.
Definition: qgscallout.cpp:635
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:595
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:653
QString type() const override
Returns a unique string representing the callout type.
Definition: qgscallout.cpp:561
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:743
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:554
QVariantMap properties(const QgsReadWriteContext &context) const override
Returns the properties describing the callout encoded in a string format.
Definition: qgscallout.cpp:571
QgsSimpleLineCallout * clone() const override
Duplicates a callout by creating a deep copy of the callout.
Definition: qgscallout.cpp:566
QgsLineSymbol * lineSymbol()
Returns the line symbol used to render the callout line.
Definition: qgscallout.cpp:643
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 QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
static Q_INVOKABLE QgsUnitTypes::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
static bool isNull(const QVariant &variant)
Returns true if the specified variant should be considered a NULL value.
static GeometryType geometryType(Type type) SIP_HOLDGIL
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
Definition: qgswkbtypes.h:968
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:2527
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