QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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 "qgslogger.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 
37 QgsPropertiesDefinition QgsCallout::sPropertyDefinitions;
38 
39 void 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 
79 QVariantMap 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 
90 void 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 
100 bool 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 
117 void 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 
137 QSet<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 
148 void 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;
155  if ( dataDefinedProperties().isActive( QgsCallout::BlendMode ) )
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 
187 void 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" ) )
209  return PoleOfInaccessibility;
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;
219  return PoleOfInaccessibility;
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" );
258  case LabelBottomMiddle:
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" ) )
274  return LabelPointOnExterior;
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;
296  return LabelPointOnExterior;
297 }
298 
299 QgsGeometry 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 
336  case LabelBottomMiddle:
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 
349 QgsGeometry 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 
413  case LabelBottomMiddle:
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 
426 QgsGeometry 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 
554 QgsCallout *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 
571 QVariantMap 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 
595 void 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 
635 QSet<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 
653 void 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 
743 QgsCurve *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 
763 QgsCallout *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 
780 QgsCurve *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 
803 QgsCallout *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 
824 QVariantMap 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 
832 QgsCurve *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;
835  if ( dataDefinedProperties().isActive( QgsCallout::Curvature ) )
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 
982 QgsCurvedLineCallout::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 
994 QString 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 
1023 void QgsCurvedLineCallout::setCurvature( double curvature )
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 
1059 QgsCallout *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 
1076 QVariantMap 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 
1103 void 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 
1145 QSet<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 
1163 void 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 
1213 QPolygonF QgsBalloonCallout::getPoints( QgsRenderContext &context, QgsPointXY origin, QRectF rect ) const
1214 {
1215  double segmentPointWidth = mWedgeWidth;
1216  if ( dataDefinedProperties().isActive( QgsCallout::WedgeWidth ) )
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;
1224  if ( dataDefinedProperties().isActive( QgsCallout::CornerRadius ) )
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 ( !value.isNull() )
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 QgsAbstractGeometry * clone() const =0
Clones the geometry by performing a deep copy.
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.
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.
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 QgsCallout * clone() const =0
Duplicates a callout by creating a deep copy of the callout.
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
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the callout's property collection, used for data defined overrides.
Definition: qgscallout.h:331
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:125
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:127
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:104
QgsGeometry shortestLine(const QgsGeometry &other, QString *errorMsg=nullptr) const
Returns the shortest line joining this geometry to the other geometry.
Definition: qgsgeos.cpp:2558
bool intersects(const QgsAbstractGeometry *geom, QString *errorMsg=nullptr) const override
Checks if geom intersects this.
Definition: qgsgeos.cpp:630
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:44
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:47
@ Double
Double value (including negative values)
Definition: qgsproperty.h:57
@ BlendMode
Blend mode.
Definition: qgsproperty.h:67
@ Boolean
Boolean value.
Definition: qgsproperty.h:53
@ DoublePositive
Positive double value (including 0)
Definition: qgsproperty.h:58
@ DataTypeString
Property requires a string value.
Definition: qgsproperty.h:92
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.
QgsExpressionContext & expressionContext()
Gets the expression context.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
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).
QgsCoordinateTransformContext transformContext() const
Returns the context's coordinate transform context, which stores various information regarding which ...
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 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:1578
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition: qgssymbol.h:27
record about vertex coordinates and index of anchor to which it is snapped