QGIS API Documentation 3.43.0-Master (cd2944e57f9)
qgscallout.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgscallout.cpp
3 ----------------
4 begin : July 2019
5 copyright : (C) 2019 Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "qgscallout.h"
19#include "qgsrendercontext.h"
20#include "qgssymbol.h"
21#include "qgslinesymbollayer.h"
22#include "qgsfillsymbollayer.h"
23#include "qgssymbollayerutils.h"
24#include "qgsxmlutils.h"
25#include "qgslinestring.h"
26#include "qgsvariantutils.h"
27#include "qgsgeos.h"
28#include "qgsgeometryutils.h"
29#include "qgscircularstring.h"
30#include "qgsshapegenerator.h"
31#include "qgspainting.h"
32#include "qgsfillsymbol.h"
33#include "qgslinesymbol.h"
34#include "qgsmarkersymbol.h"
35#include "qgsunittypes.h"
36
37#include <mutex>
38
39QgsPropertiesDefinition QgsCallout::sPropertyDefinitions;
40
41void QgsCallout::initPropertyDefinitions()
42{
43 const QString origin = QStringLiteral( "callouts" );
44
45 sPropertyDefinitions = QgsPropertiesDefinition
46 {
47 { static_cast< int >( QgsCallout::Property::MinimumCalloutLength ), QgsPropertyDefinition( "MinimumCalloutLength", QObject::tr( "Minimum callout length" ), QgsPropertyDefinition::DoublePositive, origin ) },
48 { static_cast< int >( QgsCallout::Property::OffsetFromAnchor ), QgsPropertyDefinition( "OffsetFromAnchor", QObject::tr( "Offset from feature" ), QgsPropertyDefinition::DoublePositive, origin ) },
49 { static_cast< int >( QgsCallout::Property::OffsetFromLabel ), QgsPropertyDefinition( "OffsetFromLabel", QObject::tr( "Offset from label" ), QgsPropertyDefinition::DoublePositive, origin ) },
50 { static_cast< int >( QgsCallout::Property::DrawCalloutToAllParts ), QgsPropertyDefinition( "DrawCalloutToAllParts", QObject::tr( "Draw lines to all feature parts" ), QgsPropertyDefinition::Boolean, origin ) },
51 { static_cast< int >( QgsCallout::Property::AnchorPointPosition ), QgsPropertyDefinition( "AnchorPointPosition", QgsPropertyDefinition::DataTypeString, QObject::tr( "Feature's anchor point position" ), QObject::tr( "string " ) + "[<b>pole_of_inaccessibility</b>|<b>point_on_exterior</b>|<b>point_on_surface</b>|<b>centroid</b>]", origin ) },
52 {
53 static_cast< int >( QgsCallout::Property::LabelAnchorPointPosition ), QgsPropertyDefinition( "LabelAnchorPointPosition", QgsPropertyDefinition::DataTypeString, QObject::tr( "Label's anchor point position" ), QObject::tr( "string " ) + "[<b>point_on_exterior</b>|<b>centroid</b>|<b>TL</b>=Top left|<b>T</b>=Top middle|"
54 "<b>TR</b>=Top right|<br>"
55 "<b>L</b>=Left|<b>R</b>=Right|<br>"
56 "<b>BL</b>=Bottom left|<b>B</b>=Bottom middle|"
57 "<b>BR</b>=Bottom right]", origin )
58 },
59 { static_cast< int >( QgsCallout::Property::OriginX ), QgsPropertyDefinition( "OriginX", QObject::tr( "Callout origin (X)" ), QgsPropertyDefinition::Double, origin ) },
60 { static_cast< int >( QgsCallout::Property::OriginY ), QgsPropertyDefinition( "OriginY", QObject::tr( "Callout origin (Y)" ), QgsPropertyDefinition::Double, origin ) },
61 { static_cast< int >( QgsCallout::Property::DestinationX ), QgsPropertyDefinition( "DestinationX", QObject::tr( "Callout destination (X)" ), QgsPropertyDefinition::Double, origin ) },
62 { static_cast< int >( QgsCallout::Property::DestinationY ), QgsPropertyDefinition( "DestinationY", QObject::tr( "Callout destination (Y)" ), QgsPropertyDefinition::Double, origin ) },
63 { static_cast< int >( QgsCallout::Property::Curvature ), QgsPropertyDefinition( "Curvature", QObject::tr( "Callout line curvature" ), QgsPropertyDefinition::Double, origin ) },
64 {
65 static_cast< int >( QgsCallout::Property::Orientation ), QgsPropertyDefinition( "Orientation", QgsPropertyDefinition::DataTypeString, QObject::tr( "Callout curve orientation" ), QObject::tr( "string " ) + "[<b>auto</b>|<b>clockwise</b>|<b>counterclockwise</b>]", origin )
66 },
67 {
68 static_cast< int >( QgsCallout::Property::Margins ), QgsPropertyDefinition( "Margins", QgsPropertyDefinition::DataTypeString, QObject::tr( "Margins" ), QObject::tr( "string of four doubles '<b>top,right,bottom,left</b>' or array of doubles <b>[top, right, bottom, left]</b>" ) )
69 },
70 { static_cast< int >( QgsCallout::Property::WedgeWidth ), QgsPropertyDefinition( "WedgeWidth", QObject::tr( "Wedge width" ), QgsPropertyDefinition::DoublePositive, origin ) },
71 { static_cast< int >( QgsCallout::Property::CornerRadius ), QgsPropertyDefinition( "CornerRadius", QObject::tr( "Corner radius" ), QgsPropertyDefinition::DoublePositive, origin ) },
72 { static_cast< int >( QgsCallout::Property::BlendMode ), QgsPropertyDefinition( "BlendMode", QObject::tr( "Callout blend mode" ), QgsPropertyDefinition::BlendMode, origin ) },
73 };
74}
75
76
80
81QVariantMap QgsCallout::properties( const QgsReadWriteContext & ) const
82{
83 QVariantMap props;
84 props.insert( QStringLiteral( "enabled" ), mEnabled ? "1" : "0" );
85 props.insert( QStringLiteral( "anchorPoint" ), encodeAnchorPoint( mAnchorPoint ) );
86 props.insert( QStringLiteral( "labelAnchorPoint" ), encodeLabelAnchorPoint( mLabelAnchorPoint ) );
87 props.insert( QStringLiteral( "blendMode" ), static_cast< int >( QgsPainting::getBlendModeEnum( mBlendMode ) ) );
88 props.insert( QStringLiteral( "ddProperties" ), mDataDefinedProperties.toVariant( propertyDefinitions() ) );
89 return props;
90}
91
92void QgsCallout::readProperties( const QVariantMap &props, const QgsReadWriteContext & )
93{
94 mEnabled = props.value( QStringLiteral( "enabled" ), QStringLiteral( "0" ) ).toInt();
95 mAnchorPoint = decodeAnchorPoint( props.value( QStringLiteral( "anchorPoint" ), QString() ).toString() );
96 mLabelAnchorPoint = decodeLabelAnchorPoint( props.value( QStringLiteral( "labelAnchorPoint" ), QString() ).toString() );
98 static_cast< Qgis::BlendMode >( props.value( QStringLiteral( "blendMode" ), QString::number( static_cast< int >( Qgis::BlendMode::Normal ) ) ).toUInt() ) );
99 mDataDefinedProperties.loadVariant( props.value( QStringLiteral( "ddProperties" ) ), propertyDefinitions() );
100}
101
102bool QgsCallout::saveProperties( QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context ) const
103{
104 if ( element.isNull() )
105 {
106 return false;
107 }
108
109 const QDomElement calloutPropsElement = QgsXmlUtils::writeVariant( properties( context ), doc );
110
111 QDomElement calloutElement = doc.createElement( QStringLiteral( "callout" ) );
112 calloutElement.setAttribute( QStringLiteral( "type" ), type() );
113 calloutElement.appendChild( calloutPropsElement );
114
115 element.appendChild( calloutElement );
116 return true;
117}
118
119void QgsCallout::restoreProperties( const QDomElement &element, const QgsReadWriteContext &context )
120{
121 const QVariantMap props = QgsXmlUtils::readVariant( element.firstChildElement() ).toMap();
122 readProperties( props, context );
123}
124
133
135{
136 return mBlendMode != QPainter::CompositionMode_SourceOver || dataDefinedProperties().isActive( QgsCallout::Property::BlendMode );
137}
138
139QSet<QString> QgsCallout::referencedFields( const QgsRenderContext &context ) const
140{
141 mDataDefinedProperties.prepare( context.expressionContext() );
142 return mDataDefinedProperties.referencedFields( context.expressionContext() );
143}
144
149
150void QgsCallout::render( QgsRenderContext &context, const QRectF &rect, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext )
151{
152 QPainter *painter = context.painter();
154 {
155 const QPainter::CompositionMode blendMode = mBlendMode;
157 {
158 context.expressionContext().setOriginalValueVariable( 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, initPropertyDefinitions );
197 return sPropertyDefinitions;
198}
199
201{
202 if ( ok )
203 *ok = true;
204 const QString cleaned = name.toLower().trimmed();
205
206 if ( cleaned == QLatin1String( "pole_of_inaccessibility" ) )
208 else if ( cleaned == QLatin1String( "point_on_exterior" ) )
209 return PointOnExterior;
210 else if ( cleaned == QLatin1String( "point_on_surface" ) )
211 return PointOnSurface;
212 else if ( cleaned == QLatin1String( "centroid" ) )
213 return Centroid;
214
215 if ( ok )
216 *ok = false;
218}
219
221{
222 switch ( anchor )
223 {
225 return QStringLiteral( "pole_of_inaccessibility" );
226 case PointOnExterior:
227 return QStringLiteral( "point_on_exterior" );
228 case PointOnSurface:
229 return QStringLiteral( "point_on_surface" );
230 case Centroid:
231 return QStringLiteral( "centroid" );
232 }
233 return QString();
234}
235
237{
238 switch ( anchor )
239 {
241 return QStringLiteral( "point_on_exterior" );
242 case LabelCentroid:
243 return QStringLiteral( "centroid" );
244 case LabelTopLeft:
245 return QStringLiteral( "tl" );
246 case LabelTopMiddle:
247 return QStringLiteral( "t" );
248 case LabelTopRight:
249 return QStringLiteral( "tr" );
250 case LabelMiddleLeft:
251 return QStringLiteral( "l" );
252 case LabelMiddleRight:
253 return QStringLiteral( "r" );
254 case LabelBottomLeft:
255 return QStringLiteral( "bl" );
257 return QStringLiteral( "b" );
258 case LabelBottomRight:
259 return QStringLiteral( "br" );
260 }
261
262 return QString();
263}
264
266{
267 if ( ok )
268 *ok = true;
269 const QString cleaned = name.toLower().trimmed();
270
271 if ( cleaned == QLatin1String( "point_on_exterior" ) )
273 else if ( cleaned == QLatin1String( "centroid" ) )
274 return LabelCentroid;
275 else if ( cleaned == QLatin1String( "tl" ) )
276 return LabelTopLeft;
277 else if ( cleaned == QLatin1String( "t" ) )
278 return LabelTopMiddle;
279 else if ( cleaned == QLatin1String( "tr" ) )
280 return LabelTopRight;
281 else if ( cleaned == QLatin1String( "l" ) )
282 return LabelMiddleLeft;
283 else if ( cleaned == QLatin1String( "r" ) )
284 return LabelMiddleRight;
285 else if ( cleaned == QLatin1String( "bl" ) )
286 return LabelBottomLeft;
287 else if ( cleaned == QLatin1String( "b" ) )
288 return LabelBottomMiddle;
289 else if ( cleaned == QLatin1String( "br" ) )
290 return LabelBottomRight;
291
292 if ( ok )
293 *ok = false;
295}
296
297QgsGeometry QgsCallout::labelAnchorGeometry( const QRectF &rect, const double angle, LabelAnchorPoint anchor ) const
298{
299 QgsGeometry label;
300 switch ( anchor )
301 {
303 label = QgsGeometry::fromRect( rect );
304 break;
305
306 case LabelCentroid:
307 label = QgsGeometry::fromRect( rect ).centroid();
308 break;
309
310 case LabelTopLeft:
311 label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomLeft() ) );
312 break;
313
314 case LabelTopMiddle:
315 label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.bottom() ) );
316 break;
317
318 case LabelTopRight:
319 label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomRight() ) );
320 break;
321
322 case LabelMiddleLeft:
323 label = QgsGeometry::fromPointXY( QgsPointXY( rect.left(), ( rect.top() + rect.bottom() ) / 2.0 ) );
324 break;
325
326 case LabelMiddleRight:
327 label = QgsGeometry::fromPointXY( QgsPointXY( rect.right(), ( rect.top() + rect.bottom() ) / 2.0 ) );
328 break;
329
330 case LabelBottomLeft:
331 label = QgsGeometry::fromPointXY( QgsPointXY( rect.topLeft() ) );
332 break;
333
335 label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.top() ) );
336 break;
337
338 case LabelBottomRight:
339 label = QgsGeometry::fromPointXY( QgsPointXY( rect.topRight() ) );
340 break;
341 }
342
343 label.rotate( angle, rect.topLeft() );
344 return label;
345}
346
347QgsGeometry QgsCallout::calloutLabelPoint( const QRectF &rect, const double angle, QgsCallout::LabelAnchorPoint anchor, QgsRenderContext &context, const QgsCallout::QgsCalloutContext &calloutContext, bool &pinned ) const
348{
349 pinned = false;
351 {
352 bool ok = false;
354 if ( ok )
355 {
357 if ( ok )
358 {
359 pinned = true;
360 // data defined label point, use it directly
361 QgsGeometry labelPoint = QgsGeometry::fromPointXY( QgsPointXY( x, y ) );
362 try
363 {
364 labelPoint.transform( calloutContext.originalFeatureToMapTransform( context ) );
365 labelPoint.transform( context.mapToPixel().transform() );
366 }
367 catch ( QgsCsException & )
368 {
369 return QgsGeometry();
370 }
371 return labelPoint;
372 }
373 }
374 }
375
376 QgsGeometry label;
377 switch ( anchor )
378 {
380 label = QgsGeometry::fromRect( rect );
381 break;
382
383 case LabelCentroid:
384 label = QgsGeometry::fromRect( rect ).centroid();
385 break;
386
387 case LabelTopLeft:
388 label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomLeft() ) );
389 break;
390
391 case LabelTopMiddle:
392 label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.bottom() ) );
393 break;
394
395 case LabelTopRight:
396 label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomRight() ) );
397 break;
398
399 case LabelMiddleLeft:
400 label = QgsGeometry::fromPointXY( QgsPointXY( rect.left(), ( rect.top() + rect.bottom() ) / 2.0 ) );
401 break;
402
403 case LabelMiddleRight:
404 label = QgsGeometry::fromPointXY( QgsPointXY( rect.right(), ( rect.top() + rect.bottom() ) / 2.0 ) );
405 break;
406
407 case LabelBottomLeft:
408 label = QgsGeometry::fromPointXY( QgsPointXY( rect.topLeft() ) );
409 break;
410
412 label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.top() ) );
413 break;
414
415 case LabelBottomRight:
416 label = QgsGeometry::fromPointXY( QgsPointXY( rect.topRight() ) );
417 break;
418 }
419
420 label.rotate( angle, rect.topLeft() );
421 return label;
422}
423
424QgsGeometry QgsCallout::calloutLineToPart( const QgsGeometry &labelGeometry, const QgsAbstractGeometry *partGeometry, QgsRenderContext &context, const QgsCalloutContext &calloutContext, bool &pinned ) const
425{
426 pinned = false;
427 AnchorPoint anchor = anchorPoint();
428 const QgsAbstractGeometry *evaluatedPartAnchor = partGeometry;
429 std::unique_ptr< QgsAbstractGeometry > tempPartAnchor;
430
432 {
433 bool ok = false;
435 if ( ok )
436 {
438 if ( ok )
439 {
440 pinned = true;
441 tempPartAnchor = std::make_unique< QgsPoint >( Qgis::WkbType::Point, x, y );
442 evaluatedPartAnchor = tempPartAnchor.get();
443 try
444 {
445 tempPartAnchor->transform( calloutContext.originalFeatureToMapTransform( context ) );
446 tempPartAnchor->transform( context.mapToPixel().transform() );
447 }
448 catch ( QgsCsException & )
449 {
450 evaluatedPartAnchor = partGeometry;
451 }
452 }
453 }
454 }
455
457 {
458 const QString encodedAnchor = encodeAnchorPoint( anchor );
459 context.expressionContext().setOriginalValueVariable( encodedAnchor );
460 anchor = decodeAnchorPoint( dataDefinedProperties().valueAsString( QgsCallout::Property::AnchorPointPosition, context.expressionContext(), encodedAnchor ) );
461 }
462
463 QgsGeometry line;
464 const QgsGeos labelGeos( labelGeometry.constGet() );
465
466 switch ( QgsWkbTypes::geometryType( evaluatedPartAnchor->wkbType() ) )
467 {
470 {
471 line = QgsGeometry( labelGeos.shortestLine( evaluatedPartAnchor ) );
472 break;
473 }
474
476 {
477 if ( labelGeos.intersects( evaluatedPartAnchor ) )
478 return QgsGeometry();
479
480 // ideally avoid this unwanted clone in future. For now we need it because poleOfInaccessibility/pointOnSurface are
481 // only available to QgsGeometry objects
482 const QgsGeometry evaluatedPartAnchorGeom( evaluatedPartAnchor->clone() );
483 switch ( anchor )
484 {
486 line = QgsGeometry( labelGeos.shortestLine( evaluatedPartAnchorGeom.poleOfInaccessibility( std::max( evaluatedPartAnchor->boundingBox().width(), evaluatedPartAnchor->boundingBox().height() ) / 20.0 ) ) ); // really rough (but quick) pole of inaccessibility
487 break;
489 line = QgsGeometry( labelGeos.shortestLine( evaluatedPartAnchorGeom.pointOnSurface() ) );
490 break;
492 line = QgsGeometry( labelGeos.shortestLine( evaluatedPartAnchor ) );
493 break;
495 line = QgsGeometry( labelGeos.shortestLine( evaluatedPartAnchorGeom.centroid() ) );
496 break;
497 }
498 break;
499 }
500
503 return QgsGeometry(); // shouldn't even get here..
504 }
505 return line;
506}
507
508//
509// QgsCallout::QgsCalloutContext
510//
511
513{
514 if ( !mOriginalFeatureToMapTransform.isValid() )
515 {
516 // lazy initialization, only create if needed...
517 mOriginalFeatureToMapTransform = QgsCoordinateTransform( originalFeatureCrs, renderContext.coordinateTransform().destinationCrs(), renderContext.transformContext() );
518 }
519 return mOriginalFeatureToMapTransform;
520}
521
522
523//
524// QgsSimpleLineCallout
525//
526
528{
529 mLineSymbol = std::make_unique< QgsLineSymbol >( QgsSymbolLayerList() << new QgsSimpleLineSymbolLayer( QColor( 60, 60, 60 ), .3 ) );
530
531}
532
534
536 : QgsCallout( other )
537 , mLineSymbol( other.mLineSymbol ? other.mLineSymbol->clone() : nullptr )
538 , mMinCalloutLength( other.mMinCalloutLength )
539 , mMinCalloutLengthUnit( other.mMinCalloutLengthUnit )
540 , mMinCalloutLengthScale( other.mMinCalloutLengthScale )
541 , mOffsetFromAnchorDistance( other.mOffsetFromAnchorDistance )
542 , mOffsetFromAnchorUnit( other.mOffsetFromAnchorUnit )
543 , mOffsetFromAnchorScale( other.mOffsetFromAnchorScale )
544 , mOffsetFromLabelDistance( other.mOffsetFromLabelDistance )
545 , mOffsetFromLabelUnit( other.mOffsetFromLabelUnit )
546 , mOffsetFromLabelScale( other.mOffsetFromLabelScale )
547 , mDrawCalloutToAllParts( other.mDrawCalloutToAllParts )
548{
549
550}
551
552QgsCallout *QgsSimpleLineCallout::create( const QVariantMap &properties, const QgsReadWriteContext &context )
553{
554 auto callout = std::make_unique< QgsSimpleLineCallout >();
555 callout->readProperties( properties, context );
556 return callout.release();
557}
558
560{
561 return QStringLiteral( "simple" );
562}
563
568
569QVariantMap QgsSimpleLineCallout::properties( const QgsReadWriteContext &context ) const
570{
571 QVariantMap props = QgsCallout::properties( context );
572
573 if ( mLineSymbol )
574 {
575 props[ QStringLiteral( "lineSymbol" ) ] = QgsSymbolLayerUtils::symbolProperties( mLineSymbol.get() );
576 }
577 props[ QStringLiteral( "minLength" ) ] = mMinCalloutLength;
578 props[ QStringLiteral( "minLengthUnit" ) ] = QgsUnitTypes::encodeUnit( mMinCalloutLengthUnit );
579 props[ QStringLiteral( "minLengthMapUnitScale" ) ] = QgsSymbolLayerUtils::encodeMapUnitScale( mMinCalloutLengthScale );
580
581 props[ QStringLiteral( "offsetFromAnchor" ) ] = mOffsetFromAnchorDistance;
582 props[ QStringLiteral( "offsetFromAnchorUnit" ) ] = QgsUnitTypes::encodeUnit( mOffsetFromAnchorUnit );
583 props[ QStringLiteral( "offsetFromAnchorMapUnitScale" ) ] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetFromAnchorScale );
584 props[ QStringLiteral( "offsetFromLabel" ) ] = mOffsetFromLabelDistance;
585 props[ QStringLiteral( "offsetFromLabelUnit" ) ] = QgsUnitTypes::encodeUnit( mOffsetFromLabelUnit );
586 props[ QStringLiteral( "offsetFromLabelMapUnitScale" ) ] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetFromLabelScale );
587
588 props[ QStringLiteral( "drawToAllParts" ) ] = mDrawCalloutToAllParts;
589
590 return props;
591}
592
593void QgsSimpleLineCallout::readProperties( const QVariantMap &props, const QgsReadWriteContext &context )
594{
595 QgsCallout::readProperties( props, context );
596
597 const QString lineSymbolDef = props.value( QStringLiteral( "lineSymbol" ) ).toString();
598 QDomDocument doc( QStringLiteral( "symbol" ) );
599 doc.setContent( lineSymbolDef );
600 const QDomElement symbolElem = doc.firstChildElement( QStringLiteral( "symbol" ) );
601 std::unique_ptr< QgsLineSymbol > lineSymbol( QgsSymbolLayerUtils::loadSymbol< QgsLineSymbol >( symbolElem, context ) );
602 if ( lineSymbol )
603 mLineSymbol = std::move( lineSymbol );
604
605 mMinCalloutLength = props.value( QStringLiteral( "minLength" ), 0 ).toDouble();
606 mMinCalloutLengthUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "minLengthUnit" ) ).toString() );
607 mMinCalloutLengthScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "minLengthMapUnitScale" ) ).toString() );
608
609 mOffsetFromAnchorDistance = props.value( QStringLiteral( "offsetFromAnchor" ), 0 ).toDouble();
610 mOffsetFromAnchorUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "offsetFromAnchorUnit" ) ).toString() );
611 mOffsetFromAnchorScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "offsetFromAnchorMapUnitScale" ) ).toString() );
612 mOffsetFromLabelDistance = props.value( QStringLiteral( "offsetFromLabel" ), 0 ).toDouble();
613 mOffsetFromLabelUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "offsetFromLabelUnit" ) ).toString() );
614 mOffsetFromLabelScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "offsetFromLabelMapUnitScale" ) ).toString() );
615
616 mDrawCalloutToAllParts = props.value( QStringLiteral( "drawToAllParts" ), false ).toBool();
617}
618
620{
621 QgsCallout::startRender( context );
622 if ( mLineSymbol )
623 mLineSymbol->startRender( context );
624}
625
627{
628 QgsCallout::stopRender( context );
629 if ( mLineSymbol )
630 mLineSymbol->stopRender( context );
631}
632
633QSet<QString> QgsSimpleLineCallout::referencedFields( const QgsRenderContext &context ) const
634{
635 QSet<QString> fields = QgsCallout::referencedFields( context );
636 if ( mLineSymbol )
637 fields.unite( mLineSymbol->usedAttributes( context ) );
638 return fields;
639}
640
642{
643 return mLineSymbol.get();
644}
645
647{
648 mLineSymbol.reset( symbol );
649}
650
651void QgsSimpleLineCallout::draw( QgsRenderContext &context, const QRectF &rect, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext )
652{
653 LabelAnchorPoint labelAnchor = labelAnchorPoint();
655 {
656 const QString encodedAnchor = encodeLabelAnchorPoint( labelAnchor );
657 context.expressionContext().setOriginalValueVariable( encodedAnchor );
658 labelAnchor = decodeLabelAnchorPoint( dataDefinedProperties().valueAsString( QgsCallout::Property::LabelAnchorPointPosition, context.expressionContext(), encodedAnchor ) );
659 }
660
661 bool originPinned = false;
662 const QgsGeometry label = calloutLabelPoint( rect, angle, labelAnchor, context, calloutContext, originPinned );
663 if ( label.isNull() )
664 return;
665
666 auto drawCalloutLine = [this, &context, &calloutContext, &label, &rect, angle, &anchor, originPinned]( const QgsAbstractGeometry * partAnchor )
667 {
668 bool destinationPinned = false;
669 const QgsGeometry line = calloutLineToPart( label, partAnchor, context, calloutContext, destinationPinned );
670 if ( line.isEmpty() )
671 return;
672
673 const double lineLength = line.length();
674 if ( qgsDoubleNear( lineLength, 0 ) )
675 return;
676
677 double minLength = mMinCalloutLength;
679 {
680 context.expressionContext().setOriginalValueVariable( minLength );
682 }
683 const double minLengthPixels = context.convertToPainterUnits( minLength, mMinCalloutLengthUnit, mMinCalloutLengthScale );
684 if ( minLengthPixels > 0 && lineLength < minLengthPixels )
685 return; // too small!
686
687 std::unique_ptr< QgsCurve > calloutCurve( createCalloutLine( qgsgeometry_cast< const QgsLineString * >( line.constGet() )->startPoint(),
688 qgsgeometry_cast< const QgsLineString * >( line.constGet() )->endPoint(), context, rect, angle, anchor, calloutContext ) );
689
690 double offsetFromAnchor = mOffsetFromAnchorDistance;
692 {
695 }
696 const double offsetFromAnchorPixels = context.convertToPainterUnits( offsetFromAnchor, mOffsetFromAnchorUnit, mOffsetFromAnchorScale );
697
698 double offsetFromLabel = mOffsetFromLabelDistance;
700 {
703 }
704 const double offsetFromLabelPixels = context.convertToPainterUnits( offsetFromLabel, mOffsetFromLabelUnit, mOffsetFromLabelScale );
705 if ( offsetFromAnchorPixels > 0 || offsetFromLabelPixels > 0 )
706 {
707 calloutCurve.reset( calloutCurve->curveSubstring( offsetFromLabelPixels, calloutCurve->length() - offsetFromAnchorPixels ) );
708 }
709
710 const QPolygonF points = calloutCurve->asQPolygonF();
711
712 if ( points.empty() )
713 return;
714
715 QgsCalloutPosition position;
716 position.setOrigin( context.mapToPixel().toMapCoordinates( points.at( 0 ).x(), points.at( 0 ).y() ).toQPointF() );
717 position.setOriginIsPinned( originPinned );
718 position.setDestination( context.mapToPixel().toMapCoordinates( points.constLast().x(), points.constLast().y() ).toQPointF() );
719 position.setDestinationIsPinned( destinationPinned );
720 calloutContext.addCalloutPosition( position );
721
722 mLineSymbol->renderPolyline( points, nullptr, context );
723 };
724
725 bool toAllParts = mDrawCalloutToAllParts;
727 {
728 context.expressionContext().setOriginalValueVariable( toAllParts );
730 }
731
732 if ( calloutContext.allFeaturePartsLabeled || !toAllParts )
733 drawCalloutLine( anchor.constGet() );
734 else
735 {
736 for ( auto it = anchor.const_parts_begin(); it != anchor.const_parts_end(); ++it )
737 drawCalloutLine( *it );
738 }
739}
740
741QgsCurve *QgsSimpleLineCallout::createCalloutLine( const QgsPoint &start, const QgsPoint &end, QgsRenderContext &, const QRectF &, const double, const QgsGeometry &, QgsCallout::QgsCalloutContext & ) const
742{
743 return new QgsLineString( start, end );
744}
745
746//
747// QgsManhattanLineCallout
748//
749
753
759
760
761QgsCallout *QgsManhattanLineCallout::create( const QVariantMap &properties, const QgsReadWriteContext &context ) // cppcheck-suppress duplInheritedMember
762{
763 auto callout = std::make_unique< QgsManhattanLineCallout >();
764 callout->readProperties( properties, context );
765 return callout.release();
766}
767
769{
770 return QStringLiteral( "manhattan" );
771}
772
777
778QgsCurve *QgsManhattanLineCallout::createCalloutLine( const QgsPoint &start, const QgsPoint &end, QgsRenderContext &, const QRectF &, const double, const QgsGeometry &, QgsCallout::QgsCalloutContext & ) const
779{
780 const QgsPoint mid1 = QgsPoint( start.x(), end.y() );
781 return new QgsLineString( QVector< QgsPoint >() << start << mid1 << end );
782}
783
784
785//
786// QgsCurvedLineCallout
787//
788
792
794 : QgsSimpleLineCallout( other )
795 , mOrientation( other.mOrientation )
796 , mCurvature( other.mCurvature )
797{
798
799}
800
801QgsCallout *QgsCurvedLineCallout::create( const QVariantMap &properties, const QgsReadWriteContext &context ) // cppcheck-suppress duplInheritedMember
802{
803 auto callout = std::make_unique< QgsCurvedLineCallout >();
804 callout->readProperties( properties, context );
805
806 callout->setCurvature( properties.value( QStringLiteral( "curvature" ), 0.1 ).toDouble() );
807 callout->setOrientation( decodeOrientation( properties.value( QStringLiteral( "orientation" ), QStringLiteral( "auto" ) ).toString() ) );
808
809 return callout.release();
810}
811
813{
814 return QStringLiteral( "curved" );
815}
816
821
822QVariantMap QgsCurvedLineCallout::properties( const QgsReadWriteContext &context ) const
823{
824 QVariantMap props = QgsSimpleLineCallout::properties( context );
825 props.insert( QStringLiteral( "curvature" ), mCurvature );
826 props.insert( QStringLiteral( "orientation" ), encodeOrientation( mOrientation ) );
827 return props;
828}
829
830QgsCurve *QgsCurvedLineCallout::createCalloutLine( const QgsPoint &start, const QgsPoint &end, QgsRenderContext &context, const QRectF &rect, const double, const QgsGeometry &, QgsCallout::QgsCalloutContext & ) const
831{
832 double curvature = mCurvature * 100;
834 {
837 }
838
839 Orientation orientation = mOrientation;
841 {
842 bool ok = false;
843 const QString orientationString = dataDefinedProperties().property( QgsCallout::Property::Orientation ).valueAsString( context.expressionContext(), QString(), &ok );
844 if ( ok )
845 {
846 orientation = decodeOrientation( orientationString );
847 }
848 }
849
850 if ( orientation == Automatic )
851 {
852 // to calculate automatically the best curve orientation, we first check which side of the label bounding box
853 // the callout origin is nearest to
854 switch ( QgsGeometryUtilsBase::closestSideOfRectangle( rect.right(), rect.bottom(), rect.left(), rect.top(), start.x(), start.y() ) )
855 {
856 case 1:
857 // closest to bottom
858 if ( qgsDoubleNear( end.x(), start.x() ) )
859 {
860 // if vertical line, we bend depending on whether the line sits towards the left or right side of the label
861 if ( start.x() < ( rect.left() + 0.5 * rect.width() ) )
863 else
865 }
866 else if ( end.x() > start.x() )
868 else
870 break;
871
872 case 2:
873 // closest to bottom-right
874 if ( end.x() < start.x() )
876 else if ( end.y() < start.y() )
878 else if ( end.x() - start.x() < end.y() - start.y() )
880 else
882 break;
883
884 case 3:
885 // closest to right
886 if ( qgsDoubleNear( end.y(), start.y() ) )
887 {
888 // if horizontal line, we bend depending on whether the line sits towards the top or bottom side of the label
889 if ( start.y() < ( rect.top() + 0.5 * rect.height() ) )
891 else
893 }
894 else if ( end.y() < start.y() )
896 else
898 break;
899
900 case 4:
901 // closest to top-right
902 if ( end.x() < start.x() )
904 else if ( end.y() > start.y() )
906 else if ( end.x() - start.x() < start.y() - end.y() )
908 else
910 break;
911
912 case 5:
913 // closest to top
914 if ( qgsDoubleNear( end.x(), start.x() ) )
915 {
916 // if vertical line, we bend depending on whether the line sits towards the left or right side of the label
917 if ( start.x() < ( rect.left() + 0.5 * rect.width() ) )
919 else
921 }
922 else if ( end.x() < start.x() )
924 else
926 break;
927
928 case 6:
929 // closest to top-left
930 if ( end.x() > start.x() )
932 else if ( end.y() > start.y() )
934 else if ( start.x() - end.x() < start.y() - end.y() )
936 else
938 break;
939
940 case 7:
941 //closest to left
942 if ( qgsDoubleNear( end.y(), start.y() ) )
943 {
944 // if horizontal line, we bend depending on whether the line sits towards the top or bottom side of the label
945 if ( start.y() < ( rect.top() + 0.5 * rect.height() ) )
947 else
949 }
950 else if ( end.y() > start.y() )
952 else
954 break;
955
956 case 8:
957 //closest to bottom-left
958 if ( end.x() > start.x() )
960 else if ( end.y() < start.y() )
962 else if ( start.x() - end.x() < end.y() - start.y() )
964 else
966 break;
967 }
968 }
969
970 // turn the line into a curved line. We do this by creating a circular string from the callout line's
971 // start to end point, where the curve point is in the middle of the callout line and perpendicularly offset
972 // by a proportion of the overall callout line length
973 const double distance = ( orientation == Clockwise ? 1 : -1 ) * start.distance( end ) * curvature / 100.0;
974 double midX, midY;
975 QgsGeometryUtilsBase::perpendicularOffsetPointAlongSegment( start.x(), start.y(), end.x(), end.y(), 0.5, distance, &midX, &midY );
976
977 return new QgsCircularString( start, QgsPoint( midX, midY ), end );
978}
979
980QgsCurvedLineCallout::Orientation QgsCurvedLineCallout::decodeOrientation( const QString &string )
981{
982 const QString cleaned = string.toLower().trimmed();
983 if ( cleaned == QLatin1String( "auto" ) )
984 return Automatic;
985 if ( cleaned == QLatin1String( "clockwise" ) )
986 return Clockwise;
987 if ( cleaned == QLatin1String( "counterclockwise" ) )
988 return CounterClockwise;
989 return Automatic;
990}
991
992QString QgsCurvedLineCallout::encodeOrientation( QgsCurvedLineCallout::Orientation orientation )
993{
994 switch ( orientation )
995 {
997 return QStringLiteral( "auto" );
999 return QStringLiteral( "clockwise" );
1001 return QStringLiteral( "counterclockwise" );
1002 }
1003 return QString();
1004}
1005
1007{
1008 return mOrientation;
1009}
1010
1012{
1013 mOrientation = orientation;
1014}
1015
1017{
1018 return mCurvature;
1019}
1020
1022{
1023 mCurvature = curvature;
1024}
1025
1026
1027
1028//
1029// QgsBalloonCallout
1030//
1031
1033{
1034 mFillSymbol = std::make_unique< QgsFillSymbol >( QgsSymbolLayerList() << new QgsSimpleFillSymbolLayer( QColor( 255, 200, 60 ) ) );
1035}
1036
1038
1040 : QgsCallout( other )
1041 , mFillSymbol( other.mFillSymbol ? other.mFillSymbol->clone() : nullptr )
1042 , mMarkerSymbol( other.mMarkerSymbol ? other.mMarkerSymbol->clone() : nullptr )
1043 , mOffsetFromAnchorDistance( other.mOffsetFromAnchorDistance )
1044 , mOffsetFromAnchorUnit( other.mOffsetFromAnchorUnit )
1045 , mOffsetFromAnchorScale( other.mOffsetFromAnchorScale )
1046 , mMargins( other.mMargins )
1047 , mMarginUnit( other.mMarginUnit )
1048 , mWedgeWidth( other.mWedgeWidth )
1049 , mWedgeWidthUnit( other.mWedgeWidthUnit )
1050 , mWedgeWidthScale( other.mWedgeWidthScale )
1051 , mCornerRadius( other.mCornerRadius )
1052 , mCornerRadiusUnit( other.mCornerRadiusUnit )
1053 , mCornerRadiusScale( other.mCornerRadiusScale )
1054{
1055
1056}
1057
1058QgsCallout *QgsBalloonCallout::create( const QVariantMap &properties, const QgsReadWriteContext &context )
1059{
1060 auto callout = std::make_unique< QgsBalloonCallout >();
1061 callout->readProperties( properties, context );
1062 return callout.release();
1063}
1064
1066{
1067 return QStringLiteral( "balloon" );
1068}
1069
1071{
1072 return new QgsBalloonCallout( *this );
1073}
1074
1075QVariantMap QgsBalloonCallout::properties( const QgsReadWriteContext &context ) const
1076{
1077 QVariantMap props = QgsCallout::properties( context );
1078
1079 if ( mFillSymbol )
1080 {
1081 props[ QStringLiteral( "fillSymbol" ) ] = QgsSymbolLayerUtils::symbolProperties( mFillSymbol.get() );
1082 }
1083
1084 if ( mMarkerSymbol )
1085 {
1086 props[ QStringLiteral( "markerSymbol" ) ] = QgsSymbolLayerUtils::symbolProperties( mMarkerSymbol.get() );
1087 }
1088
1089 props[ QStringLiteral( "offsetFromAnchor" ) ] = mOffsetFromAnchorDistance;
1090 props[ QStringLiteral( "offsetFromAnchorUnit" ) ] = QgsUnitTypes::encodeUnit( mOffsetFromAnchorUnit );
1091 props[ QStringLiteral( "offsetFromAnchorMapUnitScale" ) ] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetFromAnchorScale );
1092
1093 props[ QStringLiteral( "margins" ) ] = mMargins.toString();
1094 props[ QStringLiteral( "marginsUnit" ) ] = QgsUnitTypes::encodeUnit( mMarginUnit );
1095
1096 props[ QStringLiteral( "wedgeWidth" ) ] = mWedgeWidth;
1097 props[ QStringLiteral( "wedgeWidthUnit" ) ] = QgsUnitTypes::encodeUnit( mWedgeWidthUnit );
1098 props[ QStringLiteral( "wedgeWidthMapUnitScale" ) ] = QgsSymbolLayerUtils::encodeMapUnitScale( mWedgeWidthScale );
1099
1100 props[ QStringLiteral( "cornerRadius" ) ] = mCornerRadius;
1101 props[ QStringLiteral( "cornerRadiusUnit" ) ] = QgsUnitTypes::encodeUnit( mCornerRadiusUnit );
1102 props[ QStringLiteral( "cornerRadiusMapUnitScale" ) ] = QgsSymbolLayerUtils::encodeMapUnitScale( mCornerRadiusScale );
1103
1104 return props;
1105}
1106
1107void QgsBalloonCallout::readProperties( const QVariantMap &props, const QgsReadWriteContext &context )
1108{
1109 QgsCallout::readProperties( props, context );
1110
1111 {
1112 const QString fillSymbolDef = props.value( QStringLiteral( "fillSymbol" ) ).toString();
1113 QDomDocument doc( QStringLiteral( "symbol" ) );
1114 doc.setContent( fillSymbolDef );
1115 const QDomElement symbolElem = doc.firstChildElement( QStringLiteral( "symbol" ) );
1116 std::unique_ptr< QgsFillSymbol > fillSymbol( QgsSymbolLayerUtils::loadSymbol< QgsFillSymbol >( symbolElem, context ) );
1117 if ( fillSymbol )
1118 mFillSymbol = std::move( fillSymbol );
1119 }
1120
1121 {
1122 const QString markerSymbolDef = props.value( QStringLiteral( "markerSymbol" ) ).toString();
1123 QDomDocument doc( QStringLiteral( "symbol" ) );
1124 doc.setContent( markerSymbolDef );
1125 const QDomElement symbolElem = doc.firstChildElement( QStringLiteral( "symbol" ) );
1126 std::unique_ptr< QgsMarkerSymbol > markerSymbol( QgsSymbolLayerUtils::loadSymbol< QgsMarkerSymbol >( symbolElem, context ) );
1127 if ( markerSymbol )
1128 mMarkerSymbol = std::move( markerSymbol );
1129 }
1130
1131 mOffsetFromAnchorDistance = props.value( QStringLiteral( "offsetFromAnchor" ), 0 ).toDouble();
1132 mOffsetFromAnchorUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "offsetFromAnchorUnit" ) ).toString() );
1133 mOffsetFromAnchorScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "offsetFromAnchorMapUnitScale" ) ).toString() );
1134
1135 mMargins = QgsMargins::fromString( props.value( QStringLiteral( "margins" ) ).toString() );
1136 mMarginUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "marginsUnit" ) ).toString() );
1137
1138 mWedgeWidth = props.value( QStringLiteral( "wedgeWidth" ), 2.64 ).toDouble();
1139 mWedgeWidthUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "wedgeWidthUnit" ) ).toString() );
1140 mWedgeWidthScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "wedgeWidthMapUnitScale" ) ).toString() );
1141
1142 mCornerRadius = props.value( QStringLiteral( "cornerRadius" ), 0 ).toDouble();
1143 mCornerRadiusUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "cornerRadiusUnit" ) ).toString() );
1144 mCornerRadiusScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "cornerRadiusMapUnitScale" ) ).toString() );
1145}
1146
1148{
1149 QgsCallout::startRender( context );
1150 if ( mFillSymbol )
1151 mFillSymbol->startRender( context );
1152 if ( mMarkerSymbol )
1153 mMarkerSymbol->startRender( context );
1154}
1155
1157{
1158 QgsCallout::stopRender( context );
1159 if ( mFillSymbol )
1160 mFillSymbol->stopRender( context );
1161 if ( mMarkerSymbol )
1162 mMarkerSymbol->stopRender( context );
1163}
1164
1165QSet<QString> QgsBalloonCallout::referencedFields( const QgsRenderContext &context ) const
1166{
1167 QSet<QString> fields = QgsCallout::referencedFields( context );
1168 if ( mFillSymbol )
1169 fields.unite( mFillSymbol->usedAttributes( context ) );
1170 if ( mMarkerSymbol )
1171 fields.unite( mMarkerSymbol->usedAttributes( context ) );
1172 return fields;
1173}
1174
1176{
1177 return mFillSymbol.get();
1178}
1179
1181{
1182 mFillSymbol.reset( symbol );
1183}
1184
1186{
1187 return mMarkerSymbol.get();
1188}
1189
1191{
1192 mMarkerSymbol.reset( symbol );
1193}
1194
1195void QgsBalloonCallout::draw( QgsRenderContext &context, const QRectF &rect, const double, const QgsGeometry &anchor, QgsCalloutContext &calloutContext )
1196{
1197 bool destinationIsPinned = false;
1198 QgsGeometry line = calloutLineToPart( QgsGeometry::fromRect( rect ), anchor.constGet(), context, calloutContext, destinationIsPinned );
1199
1200 if ( mMarkerSymbol )
1201 {
1202 if ( const QgsLineString *ls = qgsgeometry_cast< const QgsLineString * >( line.constGet() ) )
1203 {
1204 QgsPoint anchorPoint = ls->endPoint();
1205 mMarkerSymbol->renderPoint( anchorPoint.toQPointF(), nullptr, context );
1206 }
1207 }
1208
1209 double offsetFromAnchor = mOffsetFromAnchorDistance;
1211 {
1214 }
1215 const double offsetFromAnchorPixels = context.convertToPainterUnits( offsetFromAnchor, mOffsetFromAnchorUnit, mOffsetFromAnchorScale );
1216
1217 if ( offsetFromAnchorPixels > 0 )
1218 {
1219 if ( const QgsLineString *ls = qgsgeometry_cast< const QgsLineString * >( line.constGet() ) )
1220 {
1221 line = QgsGeometry( ls->curveSubstring( 0, ls->length() - offsetFromAnchorPixels ) );
1222 }
1223 }
1224
1225 QgsPointXY destination;
1226 QgsPointXY origin;
1227 if ( const QgsLineString *ls = qgsgeometry_cast< const QgsLineString * >( line.constGet() ) )
1228 {
1229 origin = ls->startPoint();
1230 destination = ls->endPoint();
1231 }
1232 else
1233 {
1234 destination = QgsPointXY( rect.center() );
1235 }
1236
1237 const QPolygonF points = getPoints( context, destination, rect );
1238 if ( points.empty() )
1239 return;
1240
1241 if ( !origin.isEmpty() )
1242 {
1243 QgsCalloutPosition position;
1244 position.setOrigin( context.mapToPixel().toMapCoordinates( origin.x(), origin.y() ).toQPointF() );
1245 position.setOriginIsPinned( false );
1246 position.setDestination( context.mapToPixel().toMapCoordinates( destination.x(), destination.y() ).toQPointF() );
1247 position.setDestinationIsPinned( destinationIsPinned );
1248 calloutContext.addCalloutPosition( position );
1249 }
1250
1251 mFillSymbol->renderPolygon( points, nullptr, nullptr, context );
1252}
1253
1254QPolygonF QgsBalloonCallout::getPoints( QgsRenderContext &context, QgsPointXY origin, QRectF rect ) const
1255{
1256 double segmentPointWidth = mWedgeWidth;
1258 {
1259 context.expressionContext().setOriginalValueVariable( segmentPointWidth );
1260 segmentPointWidth = dataDefinedProperties().valueAsDouble( QgsCallout::Property::WedgeWidth, context.expressionContext(), segmentPointWidth );
1261 }
1262 segmentPointWidth = context.convertToPainterUnits( segmentPointWidth, mWedgeWidthUnit, mWedgeWidthScale );
1263
1264 double cornerRadius = mCornerRadius;
1266 {
1269 }
1270 cornerRadius = context.convertToPainterUnits( cornerRadius, mCornerRadiusUnit, mCornerRadiusScale );
1271
1272 double left = mMargins.left();
1273 double right = mMargins.right();
1274 double top = mMargins.top();
1275 double bottom = mMargins.bottom();
1276
1278 {
1279 const QVariant value = dataDefinedProperties().value( QgsCallout::Property::Margins, context.expressionContext() );
1280 if ( !QgsVariantUtils::isNull( value ) )
1281 {
1282 if ( value.userType() == QMetaType::Type::QVariantList )
1283 {
1284 const QVariantList list = value.toList();
1285 if ( list.size() == 4 )
1286 {
1287 bool topOk = false;
1288 bool rightOk = false;
1289 bool bottomOk = false;
1290 bool leftOk = false;
1291 const double evaluatedTop = list.at( 0 ).toDouble( &topOk );
1292 const double evaluatedRight = list.at( 1 ).toDouble( &rightOk );
1293 const double evaluatedBottom = list.at( 2 ).toDouble( &bottomOk );
1294 const double evaluatedLeft = list.at( 3 ).toDouble( &leftOk );
1295 if ( topOk && rightOk && bottomOk && leftOk )
1296 {
1297 left = evaluatedLeft;
1298 top = evaluatedTop;
1299 right = evaluatedRight;
1300 bottom = evaluatedBottom;
1301 }
1302 }
1303 }
1304 else
1305 {
1306 const QStringList list = value.toString().trimmed().split( ',' );
1307 if ( list.count() == 4 )
1308 {
1309 bool topOk = false;
1310 bool rightOk = false;
1311 bool bottomOk = false;
1312 bool leftOk = false;
1313 const double evaluatedTop = list.at( 0 ).toDouble( &topOk );
1314 const double evaluatedRight = list.at( 1 ).toDouble( &rightOk );
1315 const double evaluatedBottom = list.at( 2 ).toDouble( &bottomOk );
1316 const double evaluatedLeft = list.at( 3 ).toDouble( &leftOk );
1317 if ( topOk && rightOk && bottomOk && leftOk )
1318 {
1319 left = evaluatedLeft;
1320 top = evaluatedTop;
1321 right = evaluatedRight;
1322 bottom = evaluatedBottom;
1323 }
1324 }
1325 }
1326 }
1327 }
1328
1329 const double marginLeft = context.convertToPainterUnits( left, mMarginUnit );
1330 const double marginRight = context.convertToPainterUnits( right, mMarginUnit );
1331 const double marginTop = context.convertToPainterUnits( top, mMarginUnit );
1332 const double marginBottom = context.convertToPainterUnits( bottom, mMarginUnit );
1333
1334 const QRectF expandedRect = rect.height() < 0 ?
1335 QRectF( rect.left() - marginLeft, rect.top() + marginBottom,
1336 rect.width() + marginLeft + marginRight,
1337 rect.height() - marginTop - marginBottom ) :
1338 QRectF( rect.left() - marginLeft, rect.top() - marginTop,
1339 rect.width() + marginLeft + marginRight,
1340 rect.height() + marginTop + marginBottom );
1341
1342 // IMPORTANT -- check for degenerate height is sometimes >=0, because QRectF are not normalized and we are using painter
1343 // coordinates with descending vertical axis!
1344 if ( expandedRect.width() <= 0 || ( rect.height() < 0 && expandedRect.height() >= 0 ) || ( rect.height() > 0 && expandedRect.height() <= 0 ) )
1345 return QPolygonF();
1346
1347 const QPainterPath path = QgsShapeGenerator::createBalloon( origin, expandedRect, segmentPointWidth, cornerRadius );
1348 const QTransform t = QTransform::fromScale( 100, 100 );
1349 const QTransform ti = t.inverted();
1350 const QPolygonF poly = path.toFillPolygon( t );
1351 return ti.map( poly );
1352}
@ ForceVector
Always force vector-based rendering, even when the result will be visually different to a raster-base...
BlendMode
Blending modes defining the available composition modes that can be used when painting.
Definition qgis.h:4790
@ Polygon
Polygons.
@ Unknown
Unknown types.
@ Null
No geometry.
Abstract base class for all geometries.
virtual QgsRectangle boundingBox() const
Returns the minimal bounding box for the geometry.
Qgis::WkbType wkbType() const
Returns the WKB type of the geometry.
virtual void transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection d=Qgis::TransformDirection::Forward, bool transformZ=false)=0
Transforms the geometry using a coordinate transform.
virtual QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
bool valueAsBool(int key, const QgsExpressionContext &context, bool defaultValue=false, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as an boolean.
double valueAsDouble(int key, const QgsExpressionContext &context, double defaultValue=0.0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a double.
A cartoon talking bubble callout style.
Definition qgscallout.h:880
QgsFillSymbol * fillSymbol()
Returns the fill symbol used to render the callout.
double cornerRadius() const
Returns the corner radius of the balloon shapes.
QString type() const override
Returns a unique string representing the callout type.
QgsMarkerSymbol * markerSymbol()
Returns the marker symbol used to render the callout endpoint.
void startRender(QgsRenderContext &context) override
Prepares the callout for rendering on the specified render context.
QSet< QString > referencedFields(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the callout.
void stopRender(QgsRenderContext &context) override
Finalises the callout after a set of rendering operations on the specified render context.
QVariantMap properties(const QgsReadWriteContext &context) const override
Returns the properties describing the callout encoded in a string format.
void setFillSymbol(QgsFillSymbol *symbol)
Sets the fill symbol used to render the callout.
void draw(QgsRenderContext &context, const QRectF &bodyBoundingBox, const double angle, const QgsGeometry &anchor, QgsCallout::QgsCalloutContext &calloutContext) override
Performs the actual rendering of the callout implementation onto the specified render context.
void setMarkerSymbol(QgsMarkerSymbol *symbol)
Sets the marker symbol used to render the callout endpoint.
~QgsBalloonCallout() override
QgsBalloonCallout * clone() const override
Duplicates a callout by creating a deep copy of the callout.
static QgsCallout * create(const QVariantMap &properties=QVariantMap(), const QgsReadWriteContext &context=QgsReadWriteContext())
Creates a new QgsBalloonCallout, using the settings serialized in the properties map (corresponding t...
double offsetFromAnchor() const
Returns the offset distance from the anchor point at which to start the line.
Definition qgscallout.h:955
void readProperties(const QVariantMap &props, const QgsReadWriteContext &context) override
Reads a string map of an callout's properties and restores the callout to the state described by the ...
Represents the calculated placement of a map label callout line.
void setDestination(const QPointF &destination)
Sets the destination of the callout line, in map coordinates.
void setOrigin(const QPointF &origin)
Sets the origin of the callout line, in map coordinates.
void setDestinationIsPinned(bool pinned)
Sets whether the destination of the callout has pinned (manually placed).
void setOriginIsPinned(bool pinned)
Sets whether the origin of the callout has pinned (manually placed).
Contains additional contextual information about the context in which a callout is being rendered.
Definition qgscallout.h:249
QgsCoordinateTransform originalFeatureToMapTransform(const QgsRenderContext &renderContext) const
Returns the coordinate transform to convert from the original layer associated with the callout to th...
void addCalloutPosition(const QgsCalloutPosition &position)
Adds a rendered callout position.
Definition qgscallout.h:279
bool allFeaturePartsLabeled
true if all parts of associated feature were labeled
Definition qgscallout.h:252
QgsCoordinateReferenceSystem originalFeatureCrs
Contains the CRS of the original feature associated with this callout.
Definition qgscallout.h:259
Abstract base class for callout renderers.
Definition qgscallout.h:54
static QString encodeAnchorPoint(AnchorPoint anchor)
Encodes an anchor point to its string representation.
bool containsAdvancedEffects() const
Returns true if the callout requires advanced effects such as blend modes, which require output in ra...
virtual bool saveProperties(QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context) const
Saves the current state of the callout to a DOM element.
virtual void stopRender(QgsRenderContext &context)
Finalises the callout after a set of rendering operations on the specified render context.
QgsGeometry calloutLineToPart(const QgsGeometry &labelGeometry, const QgsAbstractGeometry *partGeometry, QgsRenderContext &context, const QgsCalloutContext &calloutContext, bool &pinned) const
Calculates the direct line from a label geometry to an anchor geometry part, respecting the various c...
void setEnabled(bool enabled)
Sets whether the callout is enabled.
virtual QString type() const =0
Returns a unique string representing the callout type.
virtual QgsCallout * clone() const =0
Duplicates a callout by creating a deep copy of the callout.
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the callout's property collection, used for data defined overrides.
Definition qgscallout.h:334
QgsGeometry calloutLabelPoint(const QRectF &bodyBoundingBox, double angle, LabelAnchorPoint anchor, QgsRenderContext &context, const QgsCalloutContext &calloutContext, bool &pinned) const
Returns the anchor point geometry for a label with the given bounding box and anchor point mode.
static QgsPropertiesDefinition propertyDefinitions()
Returns the definitions for data defined properties available for use in callouts.
AnchorPoint anchorPoint() const
Returns the feature's anchor point position.
Definition qgscallout.h:364
LabelAnchorPoint labelAnchorPoint() const
Returns the label's anchor point position.
Definition qgscallout.h:397
AnchorPoint
Feature's anchor point position.
Definition qgscallout.h:115
@ PointOnExterior
A point on the surface's outline closest to the label is used as anchor for polygon geometries.
Definition qgscallout.h:117
@ Centroid
The surface's centroid is used as anchor for polygon geometries.
Definition qgscallout.h:119
@ PointOnSurface
A point guaranteed to be on the surface is used as anchor for polygon geometries.
Definition qgscallout.h:118
@ PoleOfInaccessibility
The surface's pole of inaccessibility used as anchor for polygon geometries.
Definition qgscallout.h:116
void render(QgsRenderContext &context, const QRectF &rect, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext)
Renders the callout onto the specified render context.
virtual void restoreProperties(const QDomElement &element, const QgsReadWriteContext &context)
Restores the callout's properties from a DOM element.
virtual void draw(QgsRenderContext &context, const QRectF &bodyBoundingBox, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext)=0
Performs the actual rendering of the callout implementation onto the specified render context.
static QString encodeLabelAnchorPoint(LabelAnchorPoint anchor)
Encodes a label anchor point to its string representation.
virtual void startRender(QgsRenderContext &context)
Prepares the callout for rendering on the specified render context.
static QgsCallout::LabelAnchorPoint decodeLabelAnchorPoint(const QString &name, bool *ok=nullptr)
Attempts to decode a string representation of a label anchor point name to the corresponding anchor p...
Q_DECL_DEPRECATED QgsGeometry labelAnchorGeometry(const QRectF &bodyBoundingBox, const double angle, LabelAnchorPoint anchor) const
Returns the anchor point geometry for a label with the given bounding box and anchor point mode.
QgsCallout()
Constructor for QgsCallout.
DrawOrder
Options for draw order (stacking) of callouts.
Definition qgscallout.h:108
@ OrderBelowAllLabels
Render callouts below all labels.
Definition qgscallout.h:109
virtual DrawOrder drawOrder() const
Returns the desired drawing order (stacking) to use while rendering this callout.
QPainter::CompositionMode blendMode() const
Returns the blending mode used for drawing callouts.
Definition qgscallout.h:431
LabelAnchorPoint
Label's anchor point position.
Definition qgscallout.h:127
@ LabelPointOnExterior
The point on the label's boundary closest to the feature.
Definition qgscallout.h:128
@ LabelBottomLeft
Bottom left corner of the label's boundary.
Definition qgscallout.h:135
@ LabelBottomMiddle
Bottom middle of the label's boundary.
Definition qgscallout.h:136
@ LabelMiddleLeft
Middle left of the label's boundary.
Definition qgscallout.h:133
@ LabelBottomRight
Bottom right corner of the label's boundary.
Definition qgscallout.h:137
@ LabelMiddleRight
Middle right of the label's boundary.
Definition qgscallout.h:134
@ LabelTopMiddle
Top middle of the label's boundary.
Definition qgscallout.h:131
@ LabelTopLeft
Top left corner of the label's boundary.
Definition qgscallout.h:130
@ LabelCentroid
The labe's centroid.
Definition qgscallout.h:129
@ LabelTopRight
Top right corner of the label's boundary.
Definition qgscallout.h:132
static QgsCallout::AnchorPoint decodeAnchorPoint(const QString &name, bool *ok=nullptr)
Attempts to decode a string representation of an anchor point name to the corresponding anchor point.
virtual void readProperties(const QVariantMap &props, const QgsReadWriteContext &context)
Reads a string map of an callout's properties and restores the callout to the state described by the ...
virtual QSet< QString > referencedFields(const QgsRenderContext &context) const
Returns the set of attributes referenced by the callout.
@ MinimumCalloutLength
Minimum length of callouts.
@ LabelAnchorPointPosition
Label's anchor point position.
@ CornerRadius
Balloon callout corner radius.
@ OffsetFromAnchor
Distance to offset lines from anchor points.
@ OffsetFromLabel
Distance to offset lines from label area.
@ DestinationX
X-coordinate of callout destination (feature anchor)
@ DestinationY
Y-coordinate of callout destination (feature anchor)
@ WedgeWidth
Balloon callout wedge width.
@ AnchorPointPosition
Feature's anchor point position.
@ OriginX
X-coordinate of callout origin (label anchor)
@ OriginY
Y-coordinate of callout origin (label anchor)
@ Curvature
Curvature of curved line callouts.
@ BlendMode
Callout blend mode.
@ Orientation
Orientation of curved line callouts.
@ Margins
Margin from text.
@ DrawCalloutToAllParts
Whether callout lines should be drawn to all feature parts.
virtual QVariantMap properties(const QgsReadWriteContext &context) const
Returns the properties describing the callout encoded in a string format.
bool enabled() const
Returns true if the the callout is enabled.
Definition qgscallout.h:322
Circular string geometry type.
Handles coordinate transforms between two coordinate systems.
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system, which the transform will transform coordinates t...
Custom exception class for Coordinate Reference System related exceptions.
Abstract base class for curved geometry type.
Definition qgscurve.h:35
Draws curved lines as callouts.
Definition qgscallout.h:785
QgsCurvedLineCallout * clone() const override
Duplicates a callout by creating a deep copy of the callout.
QgsCurve * createCalloutLine(const QgsPoint &start, const QgsPoint &end, QgsRenderContext &context, const QRectF &bodyBoundingBox, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext) const override
Creates a callout line between start and end in the desired style.
QVariantMap properties(const QgsReadWriteContext &context) const override
Returns the properties describing the callout encoded in a string format.
void setOrientation(Orientation orientation)
Sets the callout line's curve orientation.
void setCurvature(double curvature)
Sets the callout line's curvature.
static QgsCallout * create(const QVariantMap &properties=QVariantMap(), const QgsReadWriteContext &context=QgsReadWriteContext())
Creates a new QgsCurvedLineCallout, using the settings serialized in the properties map (correspondin...
QString type() const override
Returns a unique string representing the callout type.
double curvature() const
Returns the callout line's curvature.
Orientation
Curve orientation.
Definition qgscallout.h:792
@ Clockwise
Curve lines in a clockwise direction.
Definition qgscallout.h:794
@ CounterClockwise
Curve lines in a counter-clockwise direction.
Definition qgscallout.h:795
@ Automatic
Automatically choose most cartographically pleasing orientation based on label and callout arrangemen...
Definition qgscallout.h:793
Orientation orientation() const
Returns the callout line's curve orientation.
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for the context.
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
static int closestSideOfRectangle(double right, double bottom, double left, double top, double x, double y)
Returns a number representing the closest side of a rectangle defined by /a right,...
static void perpendicularOffsetPointAlongSegment(double x1, double y1, double x2, double y2, double proportion, double offset, double *x, double *y)
Calculates a point a certain proportion of the way along the segment from (x1, y1) to (x2,...
A geometry is the spatial representation of a feature.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
double length() const
Returns the planar, 2-dimensional length of geometry.
QgsGeometry poleOfInaccessibility(double precision, double *distanceToBoundary=nullptr) const
Calculates the approximate pole of inaccessibility for a surface, which is the most distant internal ...
QgsAbstractGeometry::const_part_iterator const_parts_begin() const
Returns STL-style const iterator pointing to the first part of the geometry.
QgsGeometry pointOnSurface() const
Returns a point guaranteed to lie on the surface of a geometry.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static QgsGeometry fromPointXY(const QgsPointXY &point)
Creates a new geometry from a QgsPointXY object.
QgsGeometry centroid() const
Returns the center of mass of a geometry.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
QgsAbstractGeometry::const_part_iterator const_parts_end() const
Returns STL-style iterator pointing to the imaginary part after the last part of the geometry.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Qgis::GeometryOperationResult rotate(double rotation, const QgsPointXY &center)
Rotate this geometry around the Z axis.
Does vector analysis using the GEOS library and handles import, export, and exception handling.
Definition qgsgeos.h:139
std::unique_ptr< QgsAbstractGeometry > shortestLine(const QgsGeometry &other, QString *errorMsg=nullptr) const
Returns the shortest line joining this geometry to the other geometry.
Definition qgsgeos.cpp:3117
bool intersects(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom intersects this.
Definition qgsgeos.cpp:849
Line string geometry type, with support for z-dimension and m-values.
A line symbol type, for rendering LineString and MultiLineString geometries.
Draws straight (right angled) lines as callouts.
Definition qgscallout.h:747
QString type() const override
Returns a unique string representing the callout type.
QgsCurve * createCalloutLine(const QgsPoint &start, const QgsPoint &end, QgsRenderContext &context, const QRectF &bodyBoundingBox, const double angle, const QgsGeometry &anchor, QgsCallout::QgsCalloutContext &calloutContext) const override
Creates a callout line between start and end in the desired style.
static QgsCallout * create(const QVariantMap &properties=QVariantMap(), const QgsReadWriteContext &context=QgsReadWriteContext())
Creates a new QgsManhattanLineCallout, using the settings serialized in the properties map (correspon...
QgsManhattanLineCallout * clone() const override
Duplicates a callout by creating a deep copy of the callout.
QgsPointXY toMapCoordinates(int x, int y) const
Transforms device coordinates to map (world) coordinates.
QgsPointXY transform(const QgsPointXY &p) const
Transforms a point p from map (world) coordinates to device coordinates.
double top() const
Returns the top margin.
Definition qgsmargins.h:77
static QgsMargins fromString(const QString &string)
Returns a QgsMargins object decoded from a string, or a null QgsMargins if the string could not be in...
double right() const
Returns the right margin.
Definition qgsmargins.h:83
double bottom() const
Returns the bottom margin.
Definition qgsmargins.h:89
QString toString() const
Returns the margins encoded to a string.
double left() const
Returns the left margin.
Definition qgsmargins.h:71
A marker symbol type, for rendering Point and MultiPoint geometries.
static Qgis::BlendMode getBlendModeEnum(QPainter::CompositionMode blendMode)
Returns a Qgis::BlendMode corresponding to a QPainter::CompositionMode.
static QPainter::CompositionMode getCompositionMode(Qgis::BlendMode blendMode)
Returns a QPainter::CompositionMode corresponding to a Qgis::BlendMode.
Represents a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
bool isEmpty() const
Returns true if the geometry is empty.
Definition qgspointxy.h:242
QPointF toQPointF() const
Converts a point to a QPointF.
Definition qgspointxy.h:165
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
double x
Definition qgspoint.h:52
double distance(double x, double y) const
Returns the Cartesian 2D distance between this point and a specified x, y coordinate.
Definition qgspoint.h:387
double y
Definition qgspoint.h:53
QVariant toVariant(const QgsPropertiesDefinition &definitions) const final
Saves this property collection to a QVariantMap, wrapped in a QVariant.
QVariant value(int key, const QgsExpressionContext &context, const QVariant &defaultValue=QVariant()) const final
Returns the calculated value of the property with the specified key from within the collection.
bool isActive(int key) const final
Returns true if the collection contains an active property with the specified key.
bool loadVariant(const QVariant &configuration, const QgsPropertiesDefinition &definitions) final
Loads this property collection from a QVariantMap, wrapped in a QVariant.
bool prepare(const QgsExpressionContext &context=QgsExpressionContext()) const final
Prepares the collection against a specified expression context.
QgsProperty property(int key) const final
Returns a matching property from the collection, if one exists.
QSet< QString > referencedFields(const QgsExpressionContext &context=QgsExpressionContext(), bool ignoreContext=false) const final
Returns the set of any fields referenced by the active properties from the collection.
Definition for a property.
Definition qgsproperty.h:45
@ Double
Double value (including negative values)
Definition qgsproperty.h:55
@ BlendMode
Blend mode.
Definition qgsproperty.h:65
@ Boolean
Boolean value.
Definition qgsproperty.h:51
@ DoublePositive
Positive double value (including 0)
Definition qgsproperty.h:56
@ DataTypeString
Property requires a string value.
Definition qgsproperty.h:90
QString valueAsString(const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property and interprets it as a string.
A container for the context for various read/write operations on objects.
QRectF toRectF() const
Returns a QRectF with same coordinates as the rectangle.
QgsRectangle buffered(double width) const
Gets rectangle enlarged by buffer.
Contains information about the context of a rendering operation.
double convertToPainterUnits(double size, Qgis::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QPainter * painter()
Returns the destination QPainter for the render operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
QgsCoordinateTransformContext transformContext() const
Returns the context's coordinate transform context, which stores various information regarding which ...
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
Qgis::RasterizedRenderingPolicy rasterizedRenderingPolicy() const
Returns the policy controlling when rasterisation of content during renders is permitted.
QgsCoordinateTransform coordinateTransform() const
Returns the current coordinate transform for the context.
static QPolygonF createBalloon(const QgsPointXY &origin, const QRectF &rect, double wedgeWidth)
Generates a "balloon"/"talking bubble" style shape (as a QPolygonF).
Renders polygons using a single fill and stroke color.
A simple direct line callout style.
Definition qgscallout.h:514
void startRender(QgsRenderContext &context) override
Prepares the callout for rendering on the specified render context.
void setLineSymbol(QgsLineSymbol *symbol)
Sets the line symbol used to render the callout line.
void stopRender(QgsRenderContext &context) override
Finalises the callout after a set of rendering operations on the specified render context.
QSet< QString > referencedFields(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the callout.
double offsetFromLabel() const
Returns the offset distance from label area at which to end the line.
Definition qgscallout.h:651
void readProperties(const QVariantMap &props, const QgsReadWriteContext &context) override
Reads a string map of an callout's properties and restores the callout to the state described by the ...
void draw(QgsRenderContext &context, const QRectF &bodyBoundingBox, const double angle, const QgsGeometry &anchor, QgsCallout::QgsCalloutContext &calloutContext) override
Performs the actual rendering of the callout implementation onto the specified render context.
QString type() const override
Returns a unique string representing the callout type.
double offsetFromAnchor() const
Returns the offset distance from the anchor point at which to start the line.
Definition qgscallout.h:607
~QgsSimpleLineCallout() override
virtual QgsCurve * createCalloutLine(const QgsPoint &start, const QgsPoint &end, QgsRenderContext &context, const QRectF &bodyBoundingBox, const double angle, const QgsGeometry &anchor, QgsCallout::QgsCalloutContext &calloutContext) const
Creates a callout line between start and end in the desired style.
static QgsCallout * create(const QVariantMap &properties=QVariantMap(), const QgsReadWriteContext &context=QgsReadWriteContext())
Creates a new QgsSimpleLineCallout, using the settings serialized in the properties map (correspondin...
QVariantMap properties(const QgsReadWriteContext &context) const override
Returns the properties describing the callout encoded in a string format.
QgsSimpleLineCallout * clone() const override
Duplicates a callout by creating a deep copy of the callout.
QgsLineSymbol * lineSymbol()
Returns the line symbol used to render the callout line.
A simple line symbol layer, which renders lines using a line in a variety of styles (e....
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
static QPainter::CompositionMode decodeBlendMode(const QString &s)
static QString symbolProperties(QgsSymbol *symbol)
Returns a string representing the symbol.
static Q_INVOKABLE Qgis::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
static QDomElement writeVariant(const QVariant &value, QDomDocument &doc)
Write a QVariant to a QDomElement.
static QVariant readVariant(const QDomElement &element)
Read a QVariant from a QDomElement.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:6352
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition qgssymbol.h:30
record about vertex coordinates and index of anchor to which it is snapped