QGIS API Documentation  3.17.0-Master (a84647cf30)
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 "qgssymbollayerutils.h"
23 #include "qgsxmlutils.h"
24 #include "qgslinestring.h"
25 #include "qgslogger.h"
26 #include <QPainter>
27 #include <mutex>
28 
29 QgsPropertiesDefinition QgsCallout::sPropertyDefinitions;
30 
31 void QgsCallout::initPropertyDefinitions()
32 {
33  const QString origin = QStringLiteral( "callouts" );
34 
35  sPropertyDefinitions = QgsPropertiesDefinition
36  {
37  { QgsCallout::MinimumCalloutLength, QgsPropertyDefinition( "MinimumCalloutLength", QObject::tr( "Minimum callout length" ), QgsPropertyDefinition::DoublePositive, origin ) },
38  { QgsCallout::OffsetFromAnchor, QgsPropertyDefinition( "OffsetFromAnchor", QObject::tr( "Offset from feature" ), QgsPropertyDefinition::DoublePositive, origin ) },
39  { QgsCallout::OffsetFromLabel, QgsPropertyDefinition( "OffsetFromLabel", QObject::tr( "Offset from label" ), QgsPropertyDefinition::DoublePositive, origin ) },
40  { QgsCallout::DrawCalloutToAllParts, QgsPropertyDefinition( "DrawCalloutToAllParts", QObject::tr( "Draw lines to all feature parts" ), QgsPropertyDefinition::Boolean, origin ) },
41  { 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 ) },
42  {
43  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|"
44  "<b>TR</b>=Top right|<br>"
45  "<b>L</b>=Left|<b>R</b>=Right|<br>"
46  "<b>BL</b>=Bottom left|<b>B</b>=Bottom middle|"
47  "<b>BR</b>=Bottom right]", origin )
48  },
49  };
50 }
51 
52 
54 {
55 }
56 
57 QVariantMap QgsCallout::properties( const QgsReadWriteContext & ) const
58 {
59  QVariantMap props;
60  props.insert( QStringLiteral( "enabled" ), mEnabled ? "1" : "0" );
61  props.insert( QStringLiteral( "anchorPoint" ), encodeAnchorPoint( mAnchorPoint ) );
62  props.insert( QStringLiteral( "labelAnchorPoint" ), encodeLabelAnchorPoint( mLabelAnchorPoint ) );
63  props.insert( QStringLiteral( "ddProperties" ), mDataDefinedProperties.toVariant( propertyDefinitions() ) );
64  return props;
65 }
66 
67 void QgsCallout::readProperties( const QVariantMap &props, const QgsReadWriteContext & )
68 {
69  mEnabled = props.value( QStringLiteral( "enabled" ), QStringLiteral( "0" ) ).toInt();
70  mAnchorPoint = decodeAnchorPoint( props.value( QStringLiteral( "anchorPoint" ), QString() ).toString() );
71  mLabelAnchorPoint = decodeLabelAnchorPoint( props.value( QStringLiteral( "labelAnchorPoint" ), QString() ).toString() );
72  mDataDefinedProperties.loadVariant( props.value( QStringLiteral( "ddProperties" ) ), propertyDefinitions() );
73 }
74 
75 bool QgsCallout::saveProperties( QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context ) const
76 {
77  if ( element.isNull() )
78  {
79  return false;
80  }
81 
82  QDomElement calloutPropsElement = QgsXmlUtils::writeVariant( properties( context ), doc );
83 
84  QDomElement calloutElement = doc.createElement( QStringLiteral( "callout" ) );
85  calloutElement.setAttribute( QStringLiteral( "type" ), type() );
86  calloutElement.appendChild( calloutPropsElement );
87 
88  element.appendChild( calloutElement );
89  return true;
90 }
91 
92 void QgsCallout::restoreProperties( const QDomElement &element, const QgsReadWriteContext &context )
93 {
94  const QVariantMap props = QgsXmlUtils::readVariant( element.firstChildElement() ).toMap();
95  readProperties( props, context );
96 }
97 
99 {
100 
101 }
103 {
104 
105 }
106 
107 QSet<QString> QgsCallout::referencedFields( const QgsRenderContext &context ) const
108 {
109  mDataDefinedProperties.prepare( context.expressionContext() );
110  return mDataDefinedProperties.referencedFields( context.expressionContext() );
111 }
112 
114 {
115  return OrderBelowAllLabels;
116 }
117 
118 void QgsCallout::render( QgsRenderContext &context, QRectF rect, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext )
119 {
120  if ( !mEnabled )
121  return;
122 
123 #if 0 // for debugging
124  QPainter *painter = context.painter();
125  painter->save();
126  painter->setRenderHint( QPainter::Antialiasing, false );
127  painter->translate( rect.center() );
128  painter->rotate( -angle );
129 
130  painter->setBrush( QColor( 255, 0, 0, 100 ) );
131  painter->setPen( QColor( 255, 0, 0, 150 ) );
132 
133  painter->drawRect( rect.width() * -0.5, rect.height() * -0.5, rect.width(), rect.height() );
134  painter->restore();
135 
136  painter->setBrush( QColor( 0, 255, 0, 100 ) );
137  painter->setPen( QColor( 0, 255, 0, 150 ) );
138 
139  painter->drawRect( anchor.boundingBox( ).buffered( 30 ).toRectF() );
140 #endif
141 
142  draw( context, rect, angle, anchor, calloutContext );
143 }
144 
146 {
147  mEnabled = enabled;
148 }
149 
151 {
152  static std::once_flag initialized;
153  std::call_once( initialized, [ = ]( )
154  {
155  initPropertyDefinitions();
156  } );
157  return sPropertyDefinitions;
158 }
159 
161 {
162  if ( ok )
163  *ok = true;
164  QString cleaned = name.toLower().trimmed();
165 
166  if ( cleaned == QLatin1String( "pole_of_inaccessibility" ) )
167  return PoleOfInaccessibility;
168  else if ( cleaned == QLatin1String( "point_on_exterior" ) )
169  return PointOnExterior;
170  else if ( cleaned == QLatin1String( "point_on_surface" ) )
171  return PointOnSurface;
172  else if ( cleaned == QLatin1String( "centroid" ) )
173  return Centroid;
174 
175  if ( ok )
176  *ok = false;
177  return PoleOfInaccessibility;
178 }
179 
181 {
182  switch ( anchor )
183  {
185  return QStringLiteral( "pole_of_inaccessibility" );
186  case PointOnExterior:
187  return QStringLiteral( "point_on_exterior" );
188  case PointOnSurface:
189  return QStringLiteral( "point_on_surface" );
190  case Centroid:
191  return QStringLiteral( "centroid" );
192  }
193  return QString();
194 }
195 
197 {
198  switch ( anchor )
199  {
201  return QStringLiteral( "point_on_exterior" );
202  case LabelCentroid:
203  return QStringLiteral( "centroid" );
204  case LabelTopLeft:
205  return QStringLiteral( "tl" );
206  case LabelTopMiddle:
207  return QStringLiteral( "t" );
208  case LabelTopRight:
209  return QStringLiteral( "tr" );
210  case LabelMiddleLeft:
211  return QStringLiteral( "l" );
212  case LabelMiddleRight:
213  return QStringLiteral( "r" );
214  case LabelBottomLeft:
215  return QStringLiteral( "bl" );
216  case LabelBottomMiddle:
217  return QStringLiteral( "b" );
218  case LabelBottomRight:
219  return QStringLiteral( "br" );
220  }
221 
222  return QString();
223 }
224 
226 {
227  if ( ok )
228  *ok = true;
229  QString cleaned = name.toLower().trimmed();
230 
231  if ( cleaned == QLatin1String( "point_on_exterior" ) )
232  return LabelPointOnExterior;
233  else if ( cleaned == QLatin1String( "centroid" ) )
234  return LabelCentroid;
235  else if ( cleaned == QLatin1String( "tl" ) )
236  return LabelTopLeft;
237  else if ( cleaned == QLatin1String( "t" ) )
238  return LabelTopMiddle;
239  else if ( cleaned == QLatin1String( "tr" ) )
240  return LabelTopRight;
241  else if ( cleaned == QLatin1String( "l" ) )
242  return LabelMiddleLeft;
243  else if ( cleaned == QLatin1String( "r" ) )
244  return LabelMiddleRight;
245  else if ( cleaned == QLatin1String( "bl" ) )
246  return LabelBottomLeft;
247  else if ( cleaned == QLatin1String( "b" ) )
248  return LabelBottomMiddle;
249  else if ( cleaned == QLatin1String( "br" ) )
250  return LabelBottomRight;
251 
252  if ( ok )
253  *ok = false;
254  return LabelPointOnExterior;
255 }
256 
257 QgsGeometry QgsCallout::labelAnchorGeometry( QRectF rect, const double angle, LabelAnchorPoint anchor ) const
258 {
259  QgsGeometry label;
260  switch ( anchor )
261  {
263  label = QgsGeometry::fromRect( rect );
264  break;
265 
266  case LabelCentroid:
267  label = QgsGeometry::fromRect( rect ).centroid();
268  break;
269 
270  case LabelTopLeft:
271  label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomLeft() ) );
272  break;
273 
274  case LabelTopMiddle:
275  label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.bottom() ) );
276  break;
277 
278  case LabelTopRight:
279  label = QgsGeometry::fromPointXY( QgsPointXY( rect.bottomRight() ) );
280  break;
281 
282  case LabelMiddleLeft:
283  label = QgsGeometry::fromPointXY( QgsPointXY( rect.left(), ( rect.top() + rect.bottom() ) / 2.0 ) );
284  break;
285 
286  case LabelMiddleRight:
287  label = QgsGeometry::fromPointXY( QgsPointXY( rect.right(), ( rect.top() + rect.bottom() ) / 2.0 ) );
288  break;
289 
290  case LabelBottomLeft:
291  label = QgsGeometry::fromPointXY( QgsPointXY( rect.topLeft() ) );
292  break;
293 
294  case LabelBottomMiddle:
295  label = QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.top() ) );
296  break;
297 
298  case LabelBottomRight:
299  label = QgsGeometry::fromPointXY( QgsPointXY( rect.topRight() ) );
300  break;
301  }
302 
303  label.rotate( angle, rect.topLeft() );
304  return label;
305 }
306 
307 //
308 // QgsSimpleLineCallout
309 //
310 
312 {
313  mLineSymbol = qgis::make_unique< QgsLineSymbol >( QgsSymbolLayerList() << new QgsSimpleLineSymbolLayer( QColor( 60, 60, 60 ), .3 ) );
314 
315 }
316 
318 
320  : QgsCallout( other )
321  , mLineSymbol( other.mLineSymbol ? other.mLineSymbol->clone() : nullptr )
322  , mMinCalloutLength( other.mMinCalloutLength )
323  , mMinCalloutLengthUnit( other.mMinCalloutLengthUnit )
324  , mMinCalloutLengthScale( other.mMinCalloutLengthScale )
325  , mOffsetFromAnchorDistance( other.mOffsetFromAnchorDistance )
326  , mOffsetFromAnchorUnit( other.mOffsetFromAnchorUnit )
327  , mOffsetFromAnchorScale( other.mOffsetFromAnchorScale )
328  , mOffsetFromLabelDistance( other.mOffsetFromLabelDistance )
329  , mOffsetFromLabelUnit( other.mOffsetFromLabelUnit )
330  , mOffsetFromLabelScale( other.mOffsetFromLabelScale )
331  , mDrawCalloutToAllParts( other.mDrawCalloutToAllParts )
332 {
333 
334 }
335 
337 {
338  std::unique_ptr< QgsSimpleLineCallout > callout = qgis::make_unique< QgsSimpleLineCallout >();
339  callout->readProperties( properties, context );
340  return callout.release();
341 }
342 
344 {
345  return QStringLiteral( "simple" );
346 }
347 
349 {
350  return new QgsSimpleLineCallout( *this );
351 }
352 
353 QVariantMap QgsSimpleLineCallout::properties( const QgsReadWriteContext &context ) const
354 {
355  QVariantMap props = QgsCallout::properties( context );
356 
357  if ( mLineSymbol )
358  {
359  props[ QStringLiteral( "lineSymbol" ) ] = QgsSymbolLayerUtils::symbolProperties( mLineSymbol.get() );
360  }
361  props[ QStringLiteral( "minLength" ) ] = mMinCalloutLength;
362  props[ QStringLiteral( "minLengthUnit" ) ] = QgsUnitTypes::encodeUnit( mMinCalloutLengthUnit );
363  props[ QStringLiteral( "minLengthMapUnitScale" ) ] = QgsSymbolLayerUtils::encodeMapUnitScale( mMinCalloutLengthScale );
364 
365  props[ QStringLiteral( "offsetFromAnchor" ) ] = mOffsetFromAnchorDistance;
366  props[ QStringLiteral( "offsetFromAnchorUnit" ) ] = QgsUnitTypes::encodeUnit( mOffsetFromAnchorUnit );
367  props[ QStringLiteral( "offsetFromAnchorMapUnitScale" ) ] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetFromAnchorScale );
368  props[ QStringLiteral( "offsetFromLabel" ) ] = mOffsetFromLabelDistance;
369  props[ QStringLiteral( "offsetFromLabelUnit" ) ] = QgsUnitTypes::encodeUnit( mOffsetFromLabelUnit );
370  props[ QStringLiteral( "offsetFromLabelMapUnitScale" ) ] = QgsSymbolLayerUtils::encodeMapUnitScale( mOffsetFromLabelScale );
371 
372  props[ QStringLiteral( "drawToAllParts" ) ] = mDrawCalloutToAllParts;
373 
374  return props;
375 }
376 
377 void QgsSimpleLineCallout::readProperties( const QVariantMap &props, const QgsReadWriteContext &context )
378 {
379  QgsCallout::readProperties( props, context );
380 
381  const QString lineSymbolDef = props.value( QStringLiteral( "lineSymbol" ) ).toString();
382  QDomDocument doc( QStringLiteral( "symbol" ) );
383  doc.setContent( lineSymbolDef );
384  QDomElement symbolElem = doc.firstChildElement( QStringLiteral( "symbol" ) );
385  std::unique_ptr< QgsLineSymbol > lineSymbol( QgsSymbolLayerUtils::loadSymbol< QgsLineSymbol >( symbolElem, context ) );
386  if ( lineSymbol )
387  mLineSymbol = std::move( lineSymbol );
388 
389  mMinCalloutLength = props.value( QStringLiteral( "minLength" ), 0 ).toDouble();
390  mMinCalloutLengthUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "minLengthUnit" ) ).toString() );
391  mMinCalloutLengthScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "minLengthMapUnitScale" ) ).toString() );
392 
393  mOffsetFromAnchorDistance = props.value( QStringLiteral( "offsetFromAnchor" ), 0 ).toDouble();
394  mOffsetFromAnchorUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "offsetFromAnchorUnit" ) ).toString() );
395  mOffsetFromAnchorScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "offsetFromAnchorMapUnitScale" ) ).toString() );
396  mOffsetFromLabelDistance = props.value( QStringLiteral( "offsetFromLabel" ), 0 ).toDouble();
397  mOffsetFromLabelUnit = QgsUnitTypes::decodeRenderUnit( props.value( QStringLiteral( "offsetFromLabelUnit" ) ).toString() );
398  mOffsetFromLabelScale = QgsSymbolLayerUtils::decodeMapUnitScale( props.value( QStringLiteral( "offsetFromLabelMapUnitScale" ) ).toString() );
399 
400  mDrawCalloutToAllParts = props.value( QStringLiteral( "drawToAllParts" ), false ).toBool();
401 }
402 
404 {
405  QgsCallout::startRender( context );
406  if ( mLineSymbol )
407  mLineSymbol->startRender( context );
408 }
409 
411 {
412  QgsCallout::stopRender( context );
413  if ( mLineSymbol )
414  mLineSymbol->stopRender( context );
415 }
416 
417 QSet<QString> QgsSimpleLineCallout::referencedFields( const QgsRenderContext &context ) const
418 {
419  QSet<QString> fields = QgsCallout::referencedFields( context );
420  if ( mLineSymbol )
421  fields.unite( mLineSymbol->usedAttributes( context ) );
422  return fields;
423 }
424 
426 {
427  return mLineSymbol.get();
428 }
429 
431 {
432  mLineSymbol.reset( symbol );
433 }
434 
435 void QgsSimpleLineCallout::draw( QgsRenderContext &context, QRectF rect, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext )
436 {
437  LabelAnchorPoint labelAnchor = labelAnchorPoint();
439  {
440  QString encodedAnchor = encodeLabelAnchorPoint( labelAnchor );
441  context.expressionContext().setOriginalValueVariable( encodedAnchor );
442  labelAnchor = decodeLabelAnchorPoint( dataDefinedProperties().valueAsString( QgsCallout::LabelAnchorPointPosition, context.expressionContext(), encodedAnchor ) );
443  }
444  QgsGeometry label = labelAnchorGeometry( rect, angle, labelAnchor );
445 
446  auto drawCalloutLine = [this, &context, &label]( const QgsGeometry & partAnchor )
447  {
448  QgsGeometry line;
449  AnchorPoint anchor = anchorPoint();
451  {
452  QString encodedAnchor = encodeAnchorPoint( anchor );
453  context.expressionContext().setOriginalValueVariable( encodedAnchor );
454  anchor = decodeAnchorPoint( dataDefinedProperties().valueAsString( QgsCallout::AnchorPointPosition, context.expressionContext(), encodedAnchor ) );
455  }
456  switch ( partAnchor.type() )
457  {
459  line = label.shortestLine( partAnchor );
460  break;
461 
463  line = label.shortestLine( partAnchor );
464  break;
465 
467  if ( label.intersects( partAnchor ) )
468  return;
469 
470  switch ( anchor )
471  {
473  line = label.shortestLine( partAnchor.poleOfInaccessibility( std::max( partAnchor.boundingBox().width(), partAnchor.boundingBox().height() ) / 20.0 ) ); // really rough (but quick) pole of inaccessibility
474  break;
476  line = label.shortestLine( partAnchor.pointOnSurface() );
477  break;
479  line = label.shortestLine( partAnchor );
480  break;
482  line = label.shortestLine( partAnchor.centroid() );
483  break;
484  }
485  break;
486 
489  return; // shouldn't even get here..
490  }
491 
492  if ( qgsDoubleNear( line.length(), 0 ) )
493  return;
494 
495  double minLength = mMinCalloutLength;
497  {
498  context.expressionContext().setOriginalValueVariable( minLength );
500  }
501  double minLengthPixels = context.convertToPainterUnits( minLength, mMinCalloutLengthUnit, mMinCalloutLengthScale );
502  if ( minLengthPixels > 0 && line.length() < minLengthPixels )
503  return; // too small!
504 
505  double offsetFromAnchor = mOffsetFromAnchorDistance;
507  {
508  context.expressionContext().setOriginalValueVariable( offsetFromAnchor );
510  }
511  const double offsetFromAnchorPixels = context.convertToPainterUnits( offsetFromAnchor, mOffsetFromAnchorUnit, mOffsetFromAnchorScale );
512 
513  double offsetFromLabel = mOffsetFromLabelDistance;
515  {
516  context.expressionContext().setOriginalValueVariable( offsetFromLabel );
518  }
519  const double offsetFromLabelPixels = context.convertToPainterUnits( offsetFromLabel, mOffsetFromLabelUnit, mOffsetFromLabelScale );
520  if ( offsetFromAnchorPixels > 0 || offsetFromLabelPixels > 0 )
521  {
522  if ( QgsLineString *ls = qgsgeometry_cast< QgsLineString * >( line.get() ) )
523  {
524  line = QgsGeometry( ls->curveSubstring( offsetFromLabelPixels, ls->length() - offsetFromAnchorPixels ) );
525  }
526  }
527 
528  mLineSymbol->renderPolyline( line.asQPolygonF(), nullptr, context );
529  };
530 
531  bool toAllParts = mDrawCalloutToAllParts;
533  {
534  context.expressionContext().setOriginalValueVariable( toAllParts );
536  }
537 
538  if ( calloutContext.allFeaturePartsLabeled || !toAllParts )
539  drawCalloutLine( anchor );
540  else
541  {
542  const QVector< QgsGeometry > parts = anchor.asGeometryCollection();
543  for ( const QgsGeometry &part : parts )
544  drawCalloutLine( part );
545  }
546 }
547 
548 
549 
550 //
551 // QgsManhattanLineCallout
552 //
553 
555 {
556 }
557 
559  : QgsSimpleLineCallout( other )
560 {
561 
562 }
563 
564 
566 {
567  std::unique_ptr< QgsManhattanLineCallout > callout = qgis::make_unique< QgsManhattanLineCallout >();
568  callout->readProperties( properties, context );
569  return callout.release();
570 }
571 
573 {
574  return QStringLiteral( "manhattan" );
575 }
576 
578 {
579  return new QgsManhattanLineCallout( *this );
580 }
581 
582 void QgsManhattanLineCallout::draw( QgsRenderContext &context, QRectF rect, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext )
583 {
584  LabelAnchorPoint labelAnchor = labelAnchorPoint();
586  {
587  QString encodedAnchor = encodeLabelAnchorPoint( labelAnchor );
588  context.expressionContext().setOriginalValueVariable( encodedAnchor );
589  labelAnchor = decodeLabelAnchorPoint( dataDefinedProperties().valueAsString( QgsCallout::LabelAnchorPointPosition, context.expressionContext(), encodedAnchor ) );
590  }
591  QgsGeometry label = labelAnchorGeometry( rect, angle, labelAnchor );
592 
593  auto drawCalloutLine = [this, &context, &label]( const QgsGeometry & partAnchor )
594  {
595  QgsGeometry line;
596  AnchorPoint anchor = anchorPoint();
598  {
599  QString encodedAnchor = encodeAnchorPoint( anchor );
600  context.expressionContext().setOriginalValueVariable( encodedAnchor );
601  anchor = decodeAnchorPoint( dataDefinedProperties().valueAsString( QgsCallout::AnchorPointPosition, context.expressionContext(), encodedAnchor ) );
602  }
603  switch ( partAnchor.type() )
604  {
606  line = label.shortestLine( partAnchor );
607  break;
608 
610  line = label.shortestLine( partAnchor );
611  break;
612 
614  if ( label.intersects( partAnchor ) )
615  return;
616 
617  switch ( anchor )
618  {
620  line = label.shortestLine( partAnchor.poleOfInaccessibility( std::max( partAnchor.boundingBox().width(), partAnchor.boundingBox().height() ) / 20.0 ) ); // really rough (but quick) pole of inaccessibility
621  break;
623  line = label.shortestLine( partAnchor.pointOnSurface() );
624  break;
626  line = label.shortestLine( partAnchor );
627  break;
629  line = label.shortestLine( partAnchor.centroid() );
630  break;
631  }
632  break;
633 
636  return; // shouldn't even get here..
637  }
638 
639  if ( qgsDoubleNear( line.length(), 0 ) )
640  return;
641 
642  double minLength = minimumLength();
644  {
646  }
647  double minLengthPixels = context.convertToPainterUnits( minLength, minimumLengthUnit(), minimumLengthMapUnitScale() );
648  if ( minLengthPixels > 0 && line.length() < minLengthPixels )
649  return; // too small!
650 
651  const QgsPoint start = qgsgeometry_cast< const QgsLineString * >( line.constGet() )->startPoint();
652  const QgsPoint end = qgsgeometry_cast< const QgsLineString * >( line.constGet() )->endPoint();
653  QgsPoint mid1 = QgsPoint( start.x(), end.y() );
654 
655  line = QgsGeometry::fromPolyline( QgsPolyline() << start << mid1 << end );
656  double offsetFromAnchorDist = offsetFromAnchor();
658  {
659  offsetFromAnchorDist = dataDefinedProperties().valueAsDouble( QgsCallout::OffsetFromAnchor, context.expressionContext(), offsetFromAnchorDist );
660  }
661  const double offsetFromAnchorPixels = context.convertToPainterUnits( offsetFromAnchorDist, offsetFromAnchorUnit(), offsetFromAnchorMapUnitScale() );
662 
663  double offsetFromLabelDist = offsetFromLabel();
665  {
666  offsetFromLabelDist = dataDefinedProperties().valueAsDouble( QgsCallout::OffsetFromLabel, context.expressionContext(), offsetFromLabelDist );
667  }
668  const double offsetFromLabelPixels = context.convertToPainterUnits( offsetFromLabelDist, offsetFromAnchorUnit(), offsetFromAnchorMapUnitScale() );
669 
670  if ( offsetFromAnchorPixels > 0 || offsetFromLabelPixels > 0 )
671  {
672  if ( QgsLineString *ls = qgsgeometry_cast< QgsLineString * >( line.get() ) )
673  {
674  line = QgsGeometry( ls->curveSubstring( offsetFromLabelPixels, ls->length() - offsetFromAnchorPixels ) );
675  }
676  }
677 
678  lineSymbol()->renderPolyline( line.asQPolygonF(), nullptr, context );
679  };
680 
681  bool toAllParts = drawCalloutToAllParts();
683  {
684  context.expressionContext().setOriginalValueVariable( toAllParts );
686  }
687 
688  if ( calloutContext.allFeaturePartsLabeled || !toAllParts )
689  drawCalloutLine( anchor );
690  else
691  {
692  const QVector< QgsGeometry > parts = anchor.asGeometryCollection();
693  for ( const QgsGeometry &part : parts )
694  drawCalloutLine( part );
695  }
696 }
QgsUnitTypes::RenderUnit minimumLengthUnit() const
Returns the units for the minimum length of callout lines.
Definition: qgscallout.h:475
void draw(QgsRenderContext &context, 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:582
The class is used as a container of context for various read/write operations on other objects...
double offsetFromLabel() const
Returns the offset distance from label area at which to end the line.
Definition: qgscallout.h:543
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:565
QgsPointSequence QgsPolyline
Polyline as represented as a vector of points.
Definition: qgsgeometry.h:71
virtual void restoreProperties(const QDomElement &element, const QgsReadWriteContext &context)
Restores the callout&#39;s properties from a DOM element.
Definition: qgscallout.cpp:92
double y
Definition: qgspoint.h:42
Abstract base class for callout renderers.
Definition: qgscallout.h:46
const QgsMapUnitScale & offsetFromAnchorMapUnitScale() const
Returns the map unit scale for the offset from anchor.
Definition: qgscallout.h:536
static QgsGeometry fromPolyline(const QgsPolyline &polyline)
Creates a new LineString geometry from a list of QgsPoint points.
Minimum length of callouts.
Definition: qgscallout.h:71
The labe&#39;s centroid.
Definition: qgscallout.h:102
virtual void stopRender(QgsRenderContext &context)
Finalises the callout after a set of rendering operations on the specified render context...
Definition: qgscallout.cpp:102
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...
void render(QgsRenderContext &context, QRectF rect, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext)
Renders the callout onto the specified render context.
Definition: qgscallout.cpp:118
Distance to offset lines from anchor points.
Definition: qgscallout.h:72
QgsUnitTypes::RenderUnit offsetFromAnchorUnit() const
Returns the units for the offset from anchor point.
Definition: qgscallout.h:520
Contains additional contextual information about the context in which a callout is being rendered...
Definition: qgscallout.h:214
static QgsGeometry fromPointXY(const QgsPointXY &point) SIP_HOLDGIL
Creates a new geometry from a QgsPointXY object.
virtual void draw(QgsRenderContext &context, QRectF bodyBoundingBox, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext)=0
Performs the actual rendering of the callout implementation onto the specified render context...
The point on the label&#39;s boundary closest to the feature.
Definition: qgscallout.h:101
void startRender(QgsRenderContext &context) override
Prepares the callout for rendering on the specified render context.
Definition: qgscallout.cpp:403
A class to represent a 2D point.
Definition: qgspointxy.h:43
A simple line symbol layer, which renders lines using a line in a variety of styles (e...
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:316
OperationResult rotate(double rotation, const QgsPointXY &center)
Rotate this geometry around the Z axis.
virtual QgsCallout * clone() const =0
Duplicates a callout by creating a deep copy of the callout.
void draw(QgsRenderContext &context, 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:435
void renderPolyline(const QPolygonF &points, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
Renders the symbol along the line joining points, using the given render context. ...
Definition: qgssymbol.cpp:2206
LabelAnchorPoint
Label&#39;s anchor point position.
Definition: qgscallout.h:99
QString type() const override
Returns a unique string representing the callout type.
Definition: qgscallout.cpp:343
The surface&#39;s centroid is used as anchor for polygon geometries.
Definition: qgscallout.h:92
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:123
QgsCallout()
Constructor for QgsCallout.
Definition: qgscallout.cpp:53
QgsGeometry centroid() const
Returns the center of mass of a geometry.
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:160
Feature&#39;s anchor point position.
Definition: qgscallout.h:75
static QString encodeLabelAnchorPoint(LabelAnchorPoint anchor)
Encodes a label anchor point to its string representation.
Definition: qgscallout.cpp:196
virtual DrawOrder drawOrder() const
Returns the desired drawing order (stacking) to use while rendering this callout. ...
Definition: qgscallout.cpp:113
Positive double value (including 0)
Definition: qgsproperty.h:59
const QgsMapUnitScale & minimumLengthMapUnitScale() const
Returns the map unit scale for the minimum callout length.
Definition: qgscallout.h:491
The surface&#39;s pole of inaccessibility used as anchor for polygon geometries.
Definition: qgscallout.h:89
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
A line symbol type, for rendering LineString and MultiLineString geometries.
Definition: qgssymbol.h:1192
Bottom middle of the label&#39;s boundary.
Definition: qgscallout.h:109
bool isActive(int key) const override
Returns true if the collection contains an active property with the specified key.
Bottom left corner of the label&#39;s boundary.
Definition: qgscallout.h:108
QPolygonF asQPolygonF() const SIP_HOLDGIL
Returns contents of the geometry as a QPolygonF.
bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
QVector< QgsGeometry > asGeometryCollection() const
Returns contents of the geometry as a list of geometries.
QgsManhattanLineCallout * clone() const override
Duplicates a callout by creating a deep copy of the callout.
Definition: qgscallout.cpp:577
Middle right of the label&#39;s boundary.
Definition: qgscallout.h:107
bool prepare(const QgsExpressionContext &context=QgsExpressionContext()) const override
Prepares the collection against a specified expression context.
QgsRectangle buffered(double width) const
Gets rectangle enlarged by buffer.
Definition: qgsrectangle.h:304
void stopRender(QgsRenderContext &context) override
Finalises the callout after a set of rendering operations on the specified render context...
Definition: qgscallout.cpp:410
virtual QSet< QString > referencedFields(const QgsRenderContext &context) const
Returns the set of attributes referenced by the callout.
Definition: qgscallout.cpp:107
static QVariant readVariant(const QDomElement &element)
Read a QVariant from a QDomElement.
QgsLineSymbol * lineSymbol()
Returns the line symbol used to render the callout line.
Definition: qgscallout.cpp:425
Middle left of the label&#39;s boundary.
Definition: qgscallout.h:106
static QgsPropertiesDefinition propertyDefinitions()
Returns the definitions for data defined properties available for use in callouts.
Definition: qgscallout.cpp:150
Label&#39;s anchor point position.
Definition: qgscallout.h:76
bool loadVariant(const QVariant &configuration, const QgsPropertiesDefinition &definitions) override
Loads this property collection from a QVariantMap, wrapped in a QVariant.
~QgsSimpleLineCallout() override
T qgsgeometry_cast(const QgsAbstractGeometry *geom)
bool allFeaturePartsLabeled
true if all parts of associated feature were labeled
Definition: qgscallout.h:217
bool drawCalloutToAllParts() const
Returns true if callout lines should be drawn to all feature parts.
Definition: qgscallout.h:587
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition: qgssymbol.h:54
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:75
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static Q_INVOKABLE QgsUnitTypes::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
QgsGeometry labelAnchorGeometry(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:257
Render callouts below all labels.
Definition: qgscallout.h:82
virtual void startRender(QgsRenderContext &context)
Prepares the callout for rendering on the specified render context.
Definition: qgscallout.cpp:98
Draws straight (right angled) lines as callouts.
Definition: qgscallout.h:629
QString type() const override
Returns a unique string representing the callout type.
Definition: qgscallout.cpp:572
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
Top left corner of the label&#39;s boundary.
Definition: qgscallout.h:103
double length() const
Returns the planar, 2-dimensional length of geometry.
Distance to offset lines from label area.
Definition: qgscallout.h:73
Definition for a property.
Definition: qgsproperty.h:47
A simple direct line callout style.
Definition: qgscallout.h:401
void setLineSymbol(QgsLineSymbol *symbol)
Sets the line symbol used to render the callout line.
Definition: qgscallout.cpp:430
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
QgsAbstractGeometry * get()
Returns a modifiable (non-const) reference to the underlying abstract geometry primitive.
A point on the surface&#39;s outline closest to the label is used as anchor for polygon geometries...
Definition: qgscallout.h:90
QgsExpressionContext & expressionContext()
Gets the expression context.
virtual QString type() const =0
Returns a unique string representing the callout type.
static QString symbolProperties(QgsSymbol *symbol)
Returns a string representing the symbol.
Whether callout lines should be drawn to all feature parts.
Definition: qgscallout.h:74
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 offsetFromAnchor() const
Returns the offset distance from the anchor point at which to start the line.
Definition: qgscallout.h:499
QgsPropertyCollection & dataDefinedProperties()
Returns a reference to the callout&#39;s property collection, used for data defined overrides.
Definition: qgscallout.h:259
Contains information about the context of a rendering operation.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
bool enabled() const
Returns true if the the callout is enabled.
Definition: qgscallout.h:247
QPainter * painter()
Returns the destination QPainter for the render operation.
static QgsGeometry fromRect(const QgsRectangle &rect) SIP_HOLDGIL
Creates a new geometry from a QgsRectangle.
QVariant toVariant(const QgsPropertiesDefinition &definitions) const override
Saves this property collection to a QVariantMap, wrapped in a QVariant.
QRectF toRectF() const
Returns a QRectF with same coordinates as the rectangle.
Definition: qgsrectangle.h:468
Property requires a string value.
Definition: qgsproperty.h:93
QVariantMap properties(const QgsReadWriteContext &context) const override
Returns the properties describing the callout encoded in a string format.
Definition: qgscallout.cpp:353
Bottom right corner of the label&#39;s boundary.
Definition: qgscallout.h:110
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:43
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
QgsGeometry shortestLine(const QgsGeometry &other) const
Returns the shortest line joining this geometry to another geometry.
LabelAnchorPoint labelAnchorPoint() const
Returns the label&#39;s anchor point position.
Definition: qgscallout.h:322
double minimumLength() const
Returns the minimum length of callout lines.
Definition: qgscallout.h:454
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.
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
AnchorPoint
Feature&#39;s anchor point position.
Definition: qgscallout.h:87
QSet< QString > referencedFields(const QgsRenderContext &context) const override
Returns the set of attributes referenced by the callout.
Definition: qgscallout.cpp:417
void readProperties(const QVariantMap &props, const QgsReadWriteContext &context) override
Reads a string map of an callout&#39;s properties and restores the callout to the state described by the ...
Definition: qgscallout.cpp:377
Top right corner of the label&#39;s boundary.
Definition: qgscallout.h:105
virtual void readProperties(const QVariantMap &props, const QgsReadWriteContext &context)
Reads a string map of an callout&#39;s properties and restores the callout to the state described by the ...
Definition: qgscallout.cpp:67
AnchorPoint anchorPoint() const
Returns the feature&#39;s anchor point position.
Definition: qgscallout.h:289
static QString encodeAnchorPoint(AnchorPoint anchor)
Encodes an anchor point to its string representation.
Definition: qgscallout.cpp:180
DrawOrder
Options for draw order (stacking) of callouts.
Definition: qgscallout.h:80
virtual QVariantMap properties(const QgsReadWriteContext &context) const
Returns the properties describing the callout encoded in a string format.
Definition: qgscallout.cpp:57
A point guaranteed to be on the surface is used as anchor for polygon geometries. ...
Definition: qgscallout.h:91
void setOriginalValueVariable(const QVariant &value)
Sets the original value variable value for the context.
static QDomElement writeVariant(const QVariant &value, QDomDocument &doc)
Write a QVariant to a QDomElement.
Top middle of the label&#39;s boundary.
Definition: qgscallout.h:104
void setEnabled(bool enabled)
Sets whether the callout is enabled.
Definition: qgscallout.cpp:145
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:336
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:225
QgsSimpleLineCallout * clone() const override
Duplicates a callout by creating a deep copy of the callout.
Definition: qgscallout.cpp:348
double x
Definition: qgspoint.h:41
record about vertex coordinates and index of anchor to which it is snapped