1 /*************************************************************************** 2 qgscallout.h 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 #ifndef QGSCALLOUT_H 18 #define QGSCALLOUT_H 19 20 #include "qgis_core.h" 21 #include "qgis_sip.h" 22 #include "qgsexpressioncontext.h" 23 #include "qgsreadwritecontext.h" 24 #include "qgspropertycollection.h" 25 #include "qgsmapunitscale.h" 26 #include "qgscalloutposition.h" 27 #include "qgsmargins.h" 28 #include "qgscoordinatetransform.h" 29 30 #include <QPainter> 31 #include <QString> 32 #include <QRectF> 33 #include <memory> 34 35 class QgsLineSymbol; 36 class QgsFillSymbol; 37 class QgsGeometry; 38 class QgsRenderContext; 39 40 class QgsCalloutWidget; //stop sip breaking 41 42 /** 43 * \ingroup core 44 * \brief Abstract base class for callout renderers. 45 * 46 * Implementations of QgsCallout are responsible for performing the actual render of 47 * callouts, including determining the desired shape of the callout and using any 48 * relevant symbology elements to render them. 49 * 50 * \since QGIS 3.10 51 */ 52 class CORE_EXPORT QgsCallout 53 { 54 55 #ifdef SIP_RUN 56 SIP_CONVERT_TO_SUBCLASS_CODE 57 if ( sipCpp->type() == "simple" && dynamic_cast<QgsSimpleLineCallout *>( sipCpp ) != NULL ) 58 { 59 sipType = sipType_QgsSimpleLineCallout; 60 } 61 else if ( sipCpp->type() == "manhattan" && dynamic_cast<QgsManhattanLineCallout *>( sipCpp ) != NULL ) 62 { 63 sipType = sipType_QgsManhattanLineCallout; 64 } 65 else if ( sipCpp->type() == "curved" && dynamic_cast<QgsCurvedLineCallout *>( sipCpp ) != NULL ) 66 { 67 sipType = sipType_QgsCurvedLineCallout; 68 } 69 else if ( sipCpp->type() == "balloon" && dynamic_cast<QgsBalloonCallout *>( sipCpp ) != NULL ) 70 { 71 sipType = sipType_QgsBalloonCallout; 72 } 73 else 74 { 75 sipType = 0; 76 } 77 SIP_END 78 #endif 79 80 public: 81 82 //! Data definable properties. 83 enum Property 84 { 85 MinimumCalloutLength, //!< Minimum length of callouts 86 OffsetFromAnchor, //!< Distance to offset lines from anchor points 87 OffsetFromLabel, //!< Distance to offset lines from label area 88 DrawCalloutToAllParts, //!< Whether callout lines should be drawn to all feature parts 89 AnchorPointPosition, //!< Feature's anchor point position 90 LabelAnchorPointPosition, //!< Label's anchor point position 91 OriginX, //!< X-coordinate of callout origin (label anchor) (since QGIS 3.20) 92 OriginY, //!< Y-coordinate of callout origin (label anchor) (since QGIS 3.20) 93 DestinationX, //!< X-coordinate of callout destination (feature anchor) (since QGIS 3.20) 94 DestinationY, //!< Y-coordinate of callout destination (feature anchor) (since QGIS 3.20) 95 Curvature, //!< Curvature of curved line callouts (since QGIS 3.20) 96 Orientation, //!< Orientation of curved line callouts (since QGIS 3.20) 97 Margins, //!< Margin from text (since QGIS 3.20) 98 WedgeWidth, //!< Balloon callout wedge width (since QGIS 3.20) 99 CornerRadius, //!< Balloon callout corner radius (since QGIS 3.20) 100 BlendMode, //!< Callout blend mode (since QGIS 3.20) 101 }; 102 103 //! Options for draw order (stacking) of callouts 104 enum DrawOrder 105 { 106 OrderBelowAllLabels, //!< Render callouts below all labels 107 OrderBelowIndividualLabels, //!< Render callouts below their individual associated labels, some callouts may be drawn over other labels 108 }; 109 110 //! Feature's anchor point position 111 enum AnchorPoint 112 { 113 PoleOfInaccessibility = 0, //!< The surface's pole of inaccessibility used as anchor for polygon geometries 114 PointOnExterior, //!< A point on the surface's outline closest to the label is used as anchor for polygon geometries 115 PointOnSurface, //!< A point guaranteed to be on the surface is used as anchor for polygon geometries 116 Centroid, //!< The surface's centroid is used as anchor for polygon geometries 117 }; 118 119 /** 120 * Label's anchor point position. 121 * \since QGIS 3.14 122 */ 123 enum LabelAnchorPoint 124 { 125 LabelPointOnExterior, //!< The point on the label's boundary closest to the feature 126 LabelCentroid, //!< The labe's centroid 127 LabelTopLeft, //!< Top left corner of the label's boundary 128 LabelTopMiddle, //!< Top middle of the label's boundary 129 LabelTopRight, //!< Top right corner of the label's boundary 130 LabelMiddleLeft, //!< Middle left of the label's boundary 131 LabelMiddleRight, //!< Middle right of the label's boundary 132 LabelBottomLeft, //!< Bottom left corner of the label's boundary 133 LabelBottomMiddle, //!< Bottom middle of the label's boundary 134 LabelBottomRight, //!< Bottom right corner of the label's boundary 135 }; 136 137 /** 138 * Constructor for QgsCallout. 139 */ 140 QgsCallout(); 141 virtual ~QgsCallout() = default; 142 143 /** 144 * Returns a unique string representing the callout type. 145 */ 146 virtual QString type() const = 0; 147 148 /** 149 * Duplicates a callout by creating a deep copy of the callout. 150 * 151 * Caller takes ownership of the returned object. 152 */ 153 virtual QgsCallout *clone() const = 0 SIP_FACTORY; 154 155 /** 156 * Returns the properties describing the callout encoded in a 157 * string format. 158 * 159 * Subclasses must ensure that they include the base class' properties() 160 * in their returned value. 161 * 162 * \see readProperties() 163 * \see saveProperties() 164 */ 165 virtual QVariantMap properties( const QgsReadWriteContext &context ) const; 166 167 /** 168 * Reads a string map of an callout's properties and restores the callout 169 * to the state described by the properties map. 170 * 171 * Subclasses must ensure that they call the base class' readProperties() 172 * method. 173 * 174 * \see properties() 175 */ 176 virtual void readProperties( const QVariantMap &props, const QgsReadWriteContext &context ); 177 178 /** 179 * Saves the current state of the callout to a DOM \a element. The default 180 * behavior is to save the properties string map returned by 181 * properties(). 182 * \returns TRUE if save was successful 183 * \see readProperties() 184 */ 185 virtual bool saveProperties( QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context ) const; 186 187 /** 188 * Restores the callout's properties from a DOM element. 189 * 190 * The default behavior is the read the DOM contents and call readProperties() on the subclass. 191 * 192 * \see readProperties() 193 */ 194 virtual void restoreProperties( const QDomElement &element, const QgsReadWriteContext &context ); 195 196 /** 197 * Returns TRUE if the callout requires advanced effects such as blend modes, which require 198 * output in raster formats to be fully respected. 199 * \since QGIS 3.20 200 */ 201 bool containsAdvancedEffects() const; 202 203 /** 204 * Prepares the callout for rendering on the specified render \a context. 205 * 206 * \warning This MUST be called prior to calling render() on the callout, and must always 207 * be accompanied by a corresponding call to stopRender(). 208 * 209 * \see stopRender() 210 */ 211 virtual void startRender( QgsRenderContext &context ); 212 213 /** 214 * Finalises the callout after a set of rendering operations on the specified render \a context. 215 * 216 * \warning This MUST be called after to after render() operations on the callout, and must always 217 * be accompanied by a corresponding prior call to startRender(). 218 * 219 * \see startRender() 220 */ 221 virtual void stopRender( QgsRenderContext &context ); 222 223 /** 224 * Returns the set of attributes referenced by the callout. This includes attributes 225 * required by any data defined properties associated with the callout. 226 * 227 * \warning This must only be called after a corresponding call to startRender() with 228 * the same render \a context. 229 */ 230 virtual QSet< QString > referencedFields( const QgsRenderContext &context ) const; 231 232 /** 233 * Returns the desired drawing order (stacking) to use while rendering this callout. 234 * 235 * The default order is QgsCallout::OrderBelowIndividualLabels. 236 */ 237 virtual DrawOrder drawOrder() const; 238 239 /** 240 * \brief Contains additional contextual information about the context in which a callout is 241 * being rendered. 242 * \ingroup core 243 * \since QGIS 3.10 244 */ 245 class CORE_EXPORT QgsCalloutContext 246 { 247 public: 248 //! TRUE if all parts of associated feature were labeled 249 bool allFeaturePartsLabeled = false; 250 251 /** 252 * Contains the CRS of the original feature associated with this callout. 253 * 254 * \since QGIS 3.20 255 */ 256 QgsCoordinateReferenceSystem originalFeatureCrs; 257 258 /** 259 * Returns the coordinate transform to convert from the original layer associated with 260 * the callout to the destination map CRS. 261 * 262 * \since QGIS 3.20 263 */ 264 QgsCoordinateTransform originalFeatureToMapTransform( const QgsRenderContext &renderContext ) const; 265 266 /** 267 * Adds a rendered callout position. 268 * 269 * The position details such as the callout line origin and destination should be populated by the 270 * callout subclass during rendering operations. 271 * 272 * \note the feature ID, layer ID and provider ID of the QgsCalloutPosition will be automatically populated. 273 * 274 * \since QGIS 3.20 275 */ addCalloutPosition(const QgsCalloutPosition & position)276 void addCalloutPosition( const QgsCalloutPosition &position ) { return mPositions.push_back( position ); } 277 278 /** 279 * Returns the list of rendered callout positions. 280 * 281 * \since QGIS 3.20 282 */ positions()283 QList< QgsCalloutPosition > positions() const { return mPositions; } 284 285 private: 286 //! Lazy initialized coordinate transform from original feature CRS to map CRS 287 mutable QgsCoordinateTransform mOriginalFeatureToMapTransform; 288 289 QList< QgsCalloutPosition > mPositions; 290 }; 291 292 /** 293 * Renders the callout onto the specified render \a context. 294 * 295 * The \a rect argument gives the desired size and position of the body of the callout (e.g. the 296 * actual label geometry). The \a angle argument specifies the rotation of the callout body 297 * (in degrees clockwise from horizontal). It is assumed that angle rotation specified via \a angle 298 * is applied around the center of \a rect. 299 * 300 * The \a anchor argument dictates the geometry which the callout should connect to. Depending on the 301 * callout subclass and anchor geometry type, the actual shape of the rendered callout may vary. 302 * E.g. a subclass may prefer to attach to the centroid of the \a anchor, while another subclass may 303 * prefer to attach to the closest point on \a anchor instead. 304 * 305 * Both \a rect and \a anchor must be specified in painter coordinates (i.e. pixels). 306 * 307 * The \a calloutContext argument is used to specify additional contextual information about 308 * how a callout is being rendered. 309 * 310 * \warning A prior call to startRender() must have been made before calling this method, and 311 * after all render() operations are complete a call to stopRender() must be made. 312 */ 313 void render( QgsRenderContext &context, const QRectF &rect, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext ); 314 315 /** 316 * Returns TRUE if the the callout is enabled. 317 * \see setEnabled() 318 */ enabled()319 bool enabled() const { return mEnabled; } 320 321 /** 322 * Sets whether the callout is \a enabled. 323 * \see enabled() 324 */ 325 void setEnabled( bool enabled ); 326 327 /** 328 * Returns a reference to the callout's property collection, used for data defined overrides. 329 * \see setDataDefinedProperties() 330 */ dataDefinedProperties()331 QgsPropertyCollection &dataDefinedProperties() { return mDataDefinedProperties; } 332 333 /** 334 * Returns a reference to the callout's property collection, used for data defined overrides. 335 * \see setDataDefinedProperties() 336 * \see Property 337 * \note not available in Python bindings 338 */ dataDefinedProperties()339 const QgsPropertyCollection &dataDefinedProperties() const SIP_SKIP { return mDataDefinedProperties; } 340 341 /** 342 * Sets the callout's property \a collection, used for data defined overrides. 343 * 344 * Any existing properties will be discarded. 345 * 346 * \see dataDefinedProperties() 347 * \see Property 348 */ setDataDefinedProperties(const QgsPropertyCollection & collection)349 void setDataDefinedProperties( const QgsPropertyCollection &collection ) { mDataDefinedProperties = collection; } 350 351 /** 352 * Returns the definitions for data defined properties available for use in callouts. 353 */ 354 static QgsPropertiesDefinition propertyDefinitions(); 355 356 /** 357 * Returns the feature's anchor point position. 358 * 359 * \see setAnchorPoint() 360 */ anchorPoint()361 AnchorPoint anchorPoint() const { return mAnchorPoint; } 362 363 /** 364 * Sets the feature's \a anchor point position. 365 * 366 * \see anchorPoint() 367 */ setAnchorPoint(AnchorPoint anchor)368 void setAnchorPoint( AnchorPoint anchor ) { mAnchorPoint = anchor; } 369 370 /** 371 * Encodes an \a anchor point to its string representation. 372 * \returns encoded string 373 * \see decodeAnchorPoint() 374 */ 375 static QString encodeAnchorPoint( AnchorPoint anchor ); 376 377 /** 378 * Attempts to decode a string representation of an anchor point name to the corresponding 379 * anchor point. 380 * \param name encoded anchor point name 381 * \param ok if specified, will be set to TRUE if the anchor point was successfully decoded 382 * \returns decoded name 383 * \see encodeAnchorPoint() 384 */ 385 static QgsCallout::AnchorPoint decodeAnchorPoint( const QString &name, bool *ok = nullptr ); 386 387 388 /** 389 * Returns the label's anchor point position. 390 * 391 * \see setLabelAnchorPoint() 392 * \since QGIS 3.14 393 */ labelAnchorPoint()394 LabelAnchorPoint labelAnchorPoint() const { return mLabelAnchorPoint; } 395 396 /** 397 * Sets the label's \a anchor point position. 398 * 399 * \see labelAnchorPoint() 400 * \since QGIS 3.14 401 */ setLabelAnchorPoint(LabelAnchorPoint anchor)402 void setLabelAnchorPoint( LabelAnchorPoint anchor ) { mLabelAnchorPoint = anchor; } 403 404 /** 405 * Encodes a label \a anchor point to its string representation. 406 * \returns encoded string 407 * \see decodeLabelAnchorPoint() 408 * \since QGIS 3.14 409 */ 410 static QString encodeLabelAnchorPoint( LabelAnchorPoint anchor ); 411 412 /** 413 * Attempts to decode a string representation of a label anchor point name to the corresponding 414 * anchor point. 415 * \param name encoded label anchor point name 416 * \param ok if specified, will be set to TRUE if the anchor point was successfully decoded 417 * \returns decoded name 418 * \see encodeLabelAnchorPoint() 419 * \since QGIS 3.14 420 */ 421 static QgsCallout::LabelAnchorPoint decodeLabelAnchorPoint( const QString &name, bool *ok = nullptr ); 422 423 /** 424 * Returns the blending mode used for drawing callouts. 425 * \see setBlendMode() 426 * \since QGIS 3.20 427 */ blendMode()428 QPainter::CompositionMode blendMode() const { return mBlendMode; } 429 430 /** 431 * Sets the blending \a mode used for drawing callouts. 432 * \see blendMode() 433 * \since QGIS 3.20 434 */ setBlendMode(QPainter::CompositionMode mode)435 void setBlendMode( QPainter::CompositionMode mode ) { mBlendMode = mode; } 436 437 protected: 438 439 /** 440 * Performs the actual rendering of the callout implementation onto the specified render \a context. 441 * 442 * The \a bodyBoundingBox argument gives the desired size and position of the body of the callout (e.g. the 443 * actual label geometry). The \a angle argument specifies the rotation of the callout body 444 * (in degrees clockwise from horizontal). It is assumed that angle rotation specified via \a angle 445 * is applied around the center of \a rect. 446 * 447 * The \a anchor argument dictates the geometry which the callout should connect to. Depending on the 448 * callout subclass and anchor geometry type, the actual shape of the rendered callout may vary. 449 * E.g. a subclass may prefer to attach to the centroid of the \a anchor, while another subclass may 450 * prefer to attach to the closest point on \a anchor instead. 451 * 452 * Both \a rect and \a anchor are specified in painter coordinates (i.e. pixels). 453 * 454 * The \a calloutContext argument is used to specify additional contextual information about 455 * how a callout is being rendered. 456 */ 457 virtual void draw( QgsRenderContext &context, const QRectF &bodyBoundingBox, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext ) = 0; 458 459 /** 460 * Returns the anchor point geometry for a label with the given bounding box and \a anchor point mode. 461 * \deprecated QGIS 3.20 use calloutLabelPoint() instead 462 */ 463 Q_DECL_DEPRECATED QgsGeometry labelAnchorGeometry( const QRectF &bodyBoundingBox, const double angle, LabelAnchorPoint anchor ) const SIP_DEPRECATED; 464 465 /** 466 * Returns the anchor point geometry for a label with the given bounding box and \a anchor point mode. 467 * 468 * The \a pinned argument will be set to TRUE if the callout label point is pinned (manually placed). 469 * 470 * \since QGIS 3.20 471 */ 472 QgsGeometry calloutLabelPoint( const QRectF &bodyBoundingBox, double angle, LabelAnchorPoint anchor, QgsRenderContext &context, const QgsCalloutContext &calloutContext, bool &pinned ) const; 473 474 /** 475 * Calculates the direct line from a label geometry to an anchor geometry part, respecting the various 476 * callout settings which influence how the callout end should be placed in the anchor geometry. 477 * 478 * Returns a null geometry if the callout line cannot be calculated. 479 * 480 * The \a pinned argument will be set to TRUE if the callout anchor point is pinned (manually placed). 481 * 482 * \since QGIS 3.20 483 */ 484 QgsGeometry calloutLineToPart( const QgsGeometry &labelGeometry, const QgsAbstractGeometry *partGeometry, QgsRenderContext &context, const QgsCalloutContext &calloutContext, bool &pinned ) const; 485 486 private: 487 488 bool mEnabled = false; 489 490 AnchorPoint mAnchorPoint = PoleOfInaccessibility; 491 LabelAnchorPoint mLabelAnchorPoint = LabelPointOnExterior; 492 493 QPainter::CompositionMode mBlendMode = QPainter::CompositionMode_SourceOver; 494 495 //! Property collection for data defined callout settings 496 QgsPropertyCollection mDataDefinedProperties; 497 498 //! Property definitions 499 static QgsPropertiesDefinition sPropertyDefinitions; 500 501 static void initPropertyDefinitions(); 502 }; 503 504 /** 505 * \ingroup core 506 * \brief A simple direct line callout style. 507 * 508 * \since QGIS 3.10 509 */ 510 class CORE_EXPORT QgsSimpleLineCallout : public QgsCallout 511 { 512 public: 513 514 QgsSimpleLineCallout(); 515 ~QgsSimpleLineCallout() override; 516 517 #ifndef SIP_RUN 518 519 /** 520 * Copy constructor. 521 */ 522 QgsSimpleLineCallout( const QgsSimpleLineCallout &other ); 523 QgsSimpleLineCallout &operator=( const QgsSimpleLineCallout & ) = delete; 524 #endif 525 526 /** 527 * Creates a new QgsSimpleLineCallout, using the settings 528 * serialized in the \a properties map (corresponding to the output from 529 * QgsSimpleLineCallout::properties() ). 530 */ 531 static QgsCallout *create( const QVariantMap &properties = QVariantMap(), const QgsReadWriteContext &context = QgsReadWriteContext() ) SIP_FACTORY; 532 533 QString type() const override; 534 QgsSimpleLineCallout *clone() const override; 535 QVariantMap properties( const QgsReadWriteContext &context ) const override; 536 void readProperties( const QVariantMap &props, const QgsReadWriteContext &context ) override; 537 void startRender( QgsRenderContext &context ) override; 538 void stopRender( QgsRenderContext &context ) override; 539 QSet< QString > referencedFields( const QgsRenderContext &context ) const override; 540 541 /** 542 * Returns the line symbol used to render the callout line. 543 * 544 * Ownership is not transferred. 545 * 546 * \see setLineSymbol() 547 */ 548 QgsLineSymbol *lineSymbol(); 549 550 /** 551 * Sets the line \a symbol used to render the callout line. Ownership of \a symbol is 552 * transferred to the callout. 553 * 554 * \see lineSymbol() 555 */ 556 void setLineSymbol( QgsLineSymbol *symbol SIP_TRANSFER ); 557 558 /** 559 * Returns the minimum length of callout lines. Units are specified through minimumLengthUnits(). 560 * \see setMinimumLength() 561 * \see minimumLengthUnit() 562 */ minimumLength()563 double minimumLength() const { return mMinCalloutLength; } 564 565 /** 566 * Sets the minimum \a length of callout lines. Units are specified through setMinimumLengthUnit(). 567 * \see minimumLength() 568 * \see setMinimumLengthUnit() 569 */ setMinimumLength(double length)570 void setMinimumLength( double length ) { mMinCalloutLength = length; } 571 572 /** 573 * Sets the \a unit for the minimum length of callout lines. 574 * \see minimumLengthUnit() 575 * \see setMinimumLength() 576 */ setMinimumLengthUnit(QgsUnitTypes::RenderUnit unit)577 void setMinimumLengthUnit( QgsUnitTypes::RenderUnit unit ) { mMinCalloutLengthUnit = unit; } 578 579 /** 580 * Returns the units for the minimum length of callout lines. 581 * \see setMinimumLengthUnit() 582 * \see minimumLength() 583 */ minimumLengthUnit()584 QgsUnitTypes::RenderUnit minimumLengthUnit() const { return mMinCalloutLengthUnit; } 585 586 /** 587 * Sets the map unit \a scale for the minimum callout length. 588 * \see minimumLengthMapUnitScale() 589 * \see setMinimumLengthUnit() 590 * \see setMinimumLength() 591 */ setMinimumLengthMapUnitScale(const QgsMapUnitScale & scale)592 void setMinimumLengthMapUnitScale( const QgsMapUnitScale &scale ) { mMinCalloutLengthScale = scale; } 593 594 /** 595 * Returns the map unit scale for the minimum callout length. 596 * \see setMinimumLengthMapUnitScale() 597 * \see minimumLengthUnit() 598 * \see minimumLength() 599 */ minimumLengthMapUnitScale()600 const QgsMapUnitScale &minimumLengthMapUnitScale() const { return mMinCalloutLengthScale; } 601 602 603 /** 604 * Returns the offset distance from the anchor point at which to start the line. Units are specified through offsetFromAnchorUnit(). 605 * \see setOffsetFromAnchor() 606 * \see offsetFromAnchorUnit() 607 */ offsetFromAnchor()608 double offsetFromAnchor() const { return mOffsetFromAnchorDistance; } 609 610 /** 611 * Sets the offset \a distance from the anchor point at which to start the line. Units are specified through setOffsetFromAnchorUnit(). 612 * \see offsetFromAnchor() 613 * \see setOffsetFromAnchorUnit() 614 */ setOffsetFromAnchor(double distance)615 void setOffsetFromAnchor( double distance ) { mOffsetFromAnchorDistance = distance; } 616 617 /** 618 * Sets the \a unit for the offset from anchor distance. 619 * \see offsetFromAnchor() 620 * \see setOffsetFromAnchor() 621 */ setOffsetFromAnchorUnit(QgsUnitTypes::RenderUnit unit)622 void setOffsetFromAnchorUnit( QgsUnitTypes::RenderUnit unit ) { mOffsetFromAnchorUnit = unit; } 623 624 /** 625 * Returns the units for the offset from anchor point. 626 * \see setOffsetFromAnchorUnit() 627 * \see offsetFromAnchor() 628 */ offsetFromAnchorUnit()629 QgsUnitTypes::RenderUnit offsetFromAnchorUnit() const { return mOffsetFromAnchorUnit; } 630 631 /** 632 * Sets the map unit \a scale for the offset from anchor. 633 * \see offsetFromAnchorMapUnitScale() 634 * \see setOffsetFromAnchorUnit() 635 * \see setOffsetFromAnchor() 636 */ setOffsetFromAnchorMapUnitScale(const QgsMapUnitScale & scale)637 void setOffsetFromAnchorMapUnitScale( const QgsMapUnitScale &scale ) { mOffsetFromAnchorScale = scale; } 638 639 /** 640 * Returns the map unit scale for the offset from anchor. 641 * \see setOffsetFromAnchorMapUnitScale() 642 * \see offsetFromAnchorUnit() 643 * \see offsetFromAnchor() 644 */ offsetFromAnchorMapUnitScale()645 const QgsMapUnitScale &offsetFromAnchorMapUnitScale() const { return mOffsetFromAnchorScale; } 646 647 /** 648 * Returns the offset distance from label area at which to end the line. Units are specified through offsetFromLabelUnit(). 649 * \see setOffsetFromLabel() 650 * \see offsetFromLabelUnit() 651 */ offsetFromLabel()652 double offsetFromLabel() const { return mOffsetFromLabelDistance; } 653 654 /** 655 * Sets the offset \a distance from label area at which to end the line. Units are specified through setOffsetFromLabelUnit(). 656 * \see offsetFromLabel() 657 * \see setOffsetFromLabelUnit() 658 */ setOffsetFromLabel(double distance)659 void setOffsetFromLabel( double distance ) { mOffsetFromLabelDistance = distance; } 660 661 /** 662 * Sets the \a unit for the offset from label area distance. 663 * \see offsetFromLabel() 664 * \see setOffsetFromLabel() 665 */ setOffsetFromLabelUnit(QgsUnitTypes::RenderUnit unit)666 void setOffsetFromLabelUnit( QgsUnitTypes::RenderUnit unit ) { mOffsetFromLabelUnit = unit; } 667 668 /** 669 * Returns the units for the offset from label area. 670 * \see setOffsetFromLabelUnit() 671 * \see offsetFromLabel() 672 */ offsetFromLabelUnit()673 QgsUnitTypes::RenderUnit offsetFromLabelUnit() const { return mOffsetFromLabelUnit; } 674 675 /** 676 * Sets the map unit \a scale for the offset from label area. 677 * \see offsetFromLabelMapUnitScale() 678 * \see setOffsetFromLabelUnit() 679 * \see setOffsetFromLabel() 680 */ setOffsetFromLabelMapUnitScale(const QgsMapUnitScale & scale)681 void setOffsetFromLabelMapUnitScale( const QgsMapUnitScale &scale ) { mOffsetFromLabelScale = scale; } 682 683 /** 684 * Returns the map unit scale for the minimum callout length. 685 * \see setOffsetFromLabelMapUnitScale() 686 * \see offsetFromLabelUnit() 687 * \see offsetFromLabel() 688 */ offsetFromLabelMapUnitScale()689 const QgsMapUnitScale &offsetFromLabelMapUnitScale() const { return mOffsetFromLabelScale; } 690 691 /** 692 * Returns TRUE if callout lines should be drawn to all feature parts. 693 * 694 * \see setDrawCalloutToAllParts() 695 */ drawCalloutToAllParts()696 bool drawCalloutToAllParts() const { return mDrawCalloutToAllParts; } 697 698 /** 699 * Sets whether callout lines should be drawn to all feature parts. 700 * 701 * \see drawCalloutToAllParts() 702 */ setDrawCalloutToAllParts(bool drawToAllParts)703 void setDrawCalloutToAllParts( bool drawToAllParts ) { mDrawCalloutToAllParts = drawToAllParts; } 704 705 protected: 706 void draw( QgsRenderContext &context, const QRectF &bodyBoundingBox, const double angle, const QgsGeometry &anchor, QgsCallout::QgsCalloutContext &calloutContext ) override; 707 708 /** 709 * Creates a callout line between \a start and \a end in the desired style. 710 * 711 * The base class method returns a straight line. 712 * 713 * \since QGIS 3.20 714 */ 715 virtual QgsCurve *createCalloutLine( const QgsPoint &start, const QgsPoint &end, QgsRenderContext &context, const QRectF &bodyBoundingBox, const double angle, const QgsGeometry &anchor, QgsCallout::QgsCalloutContext &calloutContext ) const SIP_FACTORY; 716 717 private: 718 719 #ifdef SIP_RUN 720 QgsSimpleLineCallout( const QgsSimpleLineCallout &other ); 721 QgsSimpleLineCallout &operator=( const QgsSimpleLineCallout & ); 722 #endif 723 724 std::unique_ptr< QgsLineSymbol > mLineSymbol; 725 double mMinCalloutLength = 0; 726 QgsUnitTypes::RenderUnit mMinCalloutLengthUnit = QgsUnitTypes::RenderMillimeters; 727 QgsMapUnitScale mMinCalloutLengthScale; 728 729 double mOffsetFromAnchorDistance = 0; 730 QgsUnitTypes::RenderUnit mOffsetFromAnchorUnit = QgsUnitTypes::RenderMillimeters; 731 QgsMapUnitScale mOffsetFromAnchorScale; 732 733 double mOffsetFromLabelDistance = 0; 734 QgsUnitTypes::RenderUnit mOffsetFromLabelUnit = QgsUnitTypes::RenderMillimeters; 735 QgsMapUnitScale mOffsetFromLabelScale; 736 737 bool mDrawCalloutToAllParts = false; 738 }; 739 740 741 /** 742 * \ingroup core 743 * \brief Draws straight (right angled) lines as callouts. 744 * 745 * \since QGIS 3.10 746 */ 747 class CORE_EXPORT QgsManhattanLineCallout : public QgsSimpleLineCallout 748 { 749 public: 750 751 QgsManhattanLineCallout(); 752 753 #ifndef SIP_RUN 754 755 /** 756 * Copy constructor. 757 */ 758 QgsManhattanLineCallout( const QgsManhattanLineCallout &other ); 759 760 QgsManhattanLineCallout &operator=( const QgsManhattanLineCallout & ) = delete; 761 #endif 762 763 /** 764 * Creates a new QgsManhattanLineCallout, using the settings 765 * serialized in the \a properties map (corresponding to the output from 766 * QgsManhattanLineCallout::properties() ). 767 */ 768 static QgsCallout *create( const QVariantMap &properties = QVariantMap(), const QgsReadWriteContext &context = QgsReadWriteContext() ) SIP_FACTORY; 769 770 QString type() const override; 771 QgsManhattanLineCallout *clone() const override; 772 773 protected: 774 QgsCurve *createCalloutLine( const QgsPoint &start, const QgsPoint &end, QgsRenderContext &context, const QRectF &bodyBoundingBox, const double angle, const QgsGeometry &anchor, QgsCallout::QgsCalloutContext &calloutContext ) const override SIP_FACTORY; 775 776 private: 777 #ifdef SIP_RUN 778 QgsManhattanLineCallout( const QgsManhattanLineCallout &other ); 779 QgsManhattanLineCallout &operator=( const QgsManhattanLineCallout & ); 780 #endif 781 }; 782 783 784 /** 785 * \ingroup core 786 * \brief Draws curved lines as callouts. 787 * 788 * \since QGIS 3.20 789 */ 790 class CORE_EXPORT QgsCurvedLineCallout : public QgsSimpleLineCallout 791 { 792 public: 793 794 /** 795 * Curve orientation 796 */ 797 enum Orientation 798 { 799 Automatic, //!< Automatically choose most cartographically pleasing orientation based on label and callout arrangement 800 Clockwise, //!< Curve lines in a clockwise direction 801 CounterClockwise, //!< Curve lines in a counter-clockwise direction 802 }; 803 804 QgsCurvedLineCallout(); 805 806 #ifndef SIP_RUN 807 808 /** 809 * Copy constructor. 810 */ 811 QgsCurvedLineCallout( const QgsCurvedLineCallout &other ); 812 813 QgsCurvedLineCallout &operator=( const QgsCurvedLineCallout & ) = delete; 814 #endif 815 816 /** 817 * Creates a new QgsCurvedLineCallout, using the settings 818 * serialized in the \a properties map (corresponding to the output from 819 * QgsCurvedLineCallout::properties() ). 820 */ 821 static QgsCallout *create( const QVariantMap &properties = QVariantMap(), const QgsReadWriteContext &context = QgsReadWriteContext() ) SIP_FACTORY; 822 823 QString type() const override; 824 QgsCurvedLineCallout *clone() const override; 825 QVariantMap properties( const QgsReadWriteContext &context ) const override; 826 827 /** 828 * Returns the callout line's curvature. 829 * 830 * The curvature is a percentage value (with typical ranges between 0.0 and 1.0), representing the overall curvature of the line. 831 * 832 * \see setCurvature() 833 */ 834 double curvature() const; 835 836 /** 837 * Sets the callout line's \a curvature. 838 * 839 * The \a curvature is a percentage value (with typical ranges between 0.0 and 1.0), representing the overall curvature of the line. 840 * 841 * \see curvature() 842 */ 843 void setCurvature( double curvature ); 844 845 /** 846 * Returns the callout line's curve orientation. 847 * 848 * \see setOrientation() 849 */ 850 Orientation orientation() const; 851 852 /** 853 * Sets the callout line's curve \a orientation. 854 * 855 * \see orientation() 856 */ 857 void setOrientation( Orientation orientation ); 858 859 protected: 860 QgsCurve *createCalloutLine( const QgsPoint &start, const QgsPoint &end, QgsRenderContext &context, const QRectF &bodyBoundingBox, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext ) const override SIP_FACTORY; 861 862 private: 863 #ifdef SIP_RUN 864 QgsCurvedLineCallout( const QgsCurvedLineCallout &other ); 865 QgsCurvedLineCallout &operator=( const QgsCurvedLineCallout & ); 866 #endif 867 868 /** 869 * Decodes a string to an orientation value 870 */ 871 static Orientation decodeOrientation( const QString &string ); 872 873 /** 874 * Encodes an orientation string 875 */ 876 static QString encodeOrientation( Orientation orientation ); 877 878 879 Orientation mOrientation = Automatic; 880 double mCurvature = 0.1; 881 }; 882 883 884 /** 885 * \ingroup core 886 * \brief A cartoon talking bubble callout style. 887 * 888 * \since QGIS 3.20 889 */ 890 class CORE_EXPORT QgsBalloonCallout : public QgsCallout 891 { 892 public: 893 894 QgsBalloonCallout(); 895 ~QgsBalloonCallout() override; 896 897 #ifndef SIP_RUN 898 899 /** 900 * Copy constructor. 901 */ 902 QgsBalloonCallout( const QgsBalloonCallout &other ); 903 QgsBalloonCallout &operator=( const QgsBalloonCallout & ) = delete; 904 #endif 905 906 /** 907 * Creates a new QgsBalloonCallout, using the settings 908 * serialized in the \a properties map (corresponding to the output from 909 * QgsBalloonCallout::properties() ). 910 */ 911 static QgsCallout *create( const QVariantMap &properties = QVariantMap(), const QgsReadWriteContext &context = QgsReadWriteContext() ) SIP_FACTORY; 912 913 QString type() const override; 914 QgsBalloonCallout *clone() const override; 915 QVariantMap properties( const QgsReadWriteContext &context ) const override; 916 void readProperties( const QVariantMap &props, const QgsReadWriteContext &context ) override; 917 void startRender( QgsRenderContext &context ) override; 918 void stopRender( QgsRenderContext &context ) override; 919 QSet< QString > referencedFields( const QgsRenderContext &context ) const override; 920 921 /** 922 * Returns the fill symbol used to render the callout. 923 * 924 * Ownership is not transferred. 925 * 926 * \see setFillSymbol() 927 */ 928 QgsFillSymbol *fillSymbol(); 929 930 /** 931 * Sets the fill \a symbol used to render the callout. Ownership of \a symbol is 932 * transferred to the callout. 933 * 934 * \see fillSymbol() 935 */ 936 void setFillSymbol( QgsFillSymbol *symbol SIP_TRANSFER ); 937 938 /** 939 * Returns the offset distance from the anchor point at which to start the line. Units are specified through offsetFromAnchorUnit(). 940 * \see setOffsetFromAnchor() 941 * \see offsetFromAnchorUnit() 942 */ offsetFromAnchor()943 double offsetFromAnchor() const { return mOffsetFromAnchorDistance; } 944 945 /** 946 * Sets the offset \a distance from the anchor point at which to start the line. Units are specified through setOffsetFromAnchorUnit(). 947 * \see offsetFromAnchor() 948 * \see setOffsetFromAnchorUnit() 949 */ setOffsetFromAnchor(double distance)950 void setOffsetFromAnchor( double distance ) { mOffsetFromAnchorDistance = distance; } 951 952 /** 953 * Sets the \a unit for the offset from anchor distance. 954 * \see offsetFromAnchor() 955 * \see setOffsetFromAnchor() 956 */ setOffsetFromAnchorUnit(QgsUnitTypes::RenderUnit unit)957 void setOffsetFromAnchorUnit( QgsUnitTypes::RenderUnit unit ) { mOffsetFromAnchorUnit = unit; } 958 959 /** 960 * Returns the units for the offset from anchor point. 961 * \see setOffsetFromAnchorUnit() 962 * \see offsetFromAnchor() 963 */ offsetFromAnchorUnit()964 QgsUnitTypes::RenderUnit offsetFromAnchorUnit() const { return mOffsetFromAnchorUnit; } 965 966 /** 967 * Sets the map unit \a scale for the offset from anchor. 968 * \see offsetFromAnchorMapUnitScale() 969 * \see setOffsetFromAnchorUnit() 970 * \see setOffsetFromAnchor() 971 */ setOffsetFromAnchorMapUnitScale(const QgsMapUnitScale & scale)972 void setOffsetFromAnchorMapUnitScale( const QgsMapUnitScale &scale ) { mOffsetFromAnchorScale = scale; } 973 974 /** 975 * Returns the map unit scale for the offset from anchor. 976 * \see setOffsetFromAnchorMapUnitScale() 977 * \see offsetFromAnchorUnit() 978 * \see offsetFromAnchor() 979 */ offsetFromAnchorMapUnitScale()980 const QgsMapUnitScale &offsetFromAnchorMapUnitScale() const { return mOffsetFromAnchorScale; } 981 982 /** 983 * Returns the margins between the outside of the callout frame and the label's bounding rectangle. 984 * 985 * Units are retrieved via marginsUnit() 986 * 987 * \note Negative margins are acceptable. 988 * 989 * \see setMargins() 990 * \see marginsUnit() 991 */ margins()992 const QgsMargins &margins() const { return mMargins; } 993 994 /** 995 * Sets the \a margins between the outside of the callout frame and the label's bounding rectangle. 996 * 997 * Units are set via setMarginsUnit() 998 * 999 * \note Negative margins are acceptable. 1000 * 1001 * \see margins() 1002 * \see setMarginsUnit() 1003 */ setMargins(const QgsMargins & margins)1004 void setMargins( const QgsMargins &margins ) { mMargins = margins; } 1005 1006 /** 1007 * Sets the \a unit for the margins between the outside of the callout frame and the label's bounding rectangle. 1008 * 1009 * \see margins() 1010 * \see marginsUnit() 1011 */ setMarginsUnit(QgsUnitTypes::RenderUnit unit)1012 void setMarginsUnit( QgsUnitTypes::RenderUnit unit ) { mMarginUnit = unit; } 1013 1014 /** 1015 * Returns the units for the margins between the outside of the callout frame and the label's bounding rectangle. 1016 * 1017 * \see setMarginsUnit() 1018 * \see margins() 1019 */ marginsUnit()1020 QgsUnitTypes::RenderUnit marginsUnit() const { return mMarginUnit; } 1021 1022 /** 1023 * Returns the width of the wedge shape at the side it connects with the label. 1024 * 1025 * Units are specified through wedgeWidthUnit(). 1026 * 1027 * \see setWedgeWidth() 1028 * \see wedgeWidthUnit() 1029 */ wedgeWidth()1030 double wedgeWidth() const { return mWedgeWidth; } 1031 1032 /** 1033 * Sets the \a width of the wedge shape at the side it connects with the label. 1034 * 1035 * Units are specified through setWedgeWidthUnit(). 1036 * 1037 * \see wedgeWidth() 1038 * \see setWedgeWidthUnit() 1039 */ setWedgeWidth(double width)1040 void setWedgeWidth( double width ) { mWedgeWidth = width; } 1041 1042 /** 1043 * Sets the \a unit for the wedge width. 1044 * 1045 * \see wedgeWidthUnit() 1046 * \see setWedgeWidth() 1047 */ setWedgeWidthUnit(QgsUnitTypes::RenderUnit unit)1048 void setWedgeWidthUnit( QgsUnitTypes::RenderUnit unit ) { mWedgeWidthUnit = unit; } 1049 1050 /** 1051 * Returns the units for the wedge width. 1052 * 1053 * \see setWedgeWidthUnit() 1054 * \see wedgeWidth() 1055 */ wedgeWidthUnit()1056 QgsUnitTypes::RenderUnit wedgeWidthUnit() const { return mWedgeWidthUnit; } 1057 1058 /** 1059 * Sets the map unit \a scale for the wedge width. 1060 * 1061 * \see wedgeWidthMapUnitScale() 1062 * \see setWedgeWidthUnit() 1063 * \see setWedgeWidth() 1064 */ setWedgeWidthMapUnitScale(const QgsMapUnitScale & scale)1065 void setWedgeWidthMapUnitScale( const QgsMapUnitScale &scale ) { mWedgeWidthScale = scale; } 1066 1067 /** 1068 * Returns the map unit scale for the wedge width. 1069 * 1070 * \see setWedgeWidthMapUnitScale() 1071 * \see wedgeWidthUnit() 1072 * \see wedgeWidth() 1073 */ wedgeWidthMapUnitScale()1074 const QgsMapUnitScale &wedgeWidthMapUnitScale() const { return mWedgeWidthScale; } 1075 1076 /** 1077 * Returns the corner radius of the balloon shapes. 1078 * 1079 * Units are specified through wedgeWidthUnit(). 1080 * 1081 * \see setCornerRadius() 1082 * \see cornerRadiusUnit() 1083 */ cornerRadius()1084 double cornerRadius() const { return mCornerRadius; } 1085 1086 /** 1087 * Sets the \a radius of the corners for the balloon shapes. 1088 * 1089 * Units are specified through setCornerRadiusUnit(). 1090 * 1091 * \see cornerRadius() 1092 * \see setCornerRadiusUnit() 1093 */ setCornerRadius(double radius)1094 void setCornerRadius( double radius ) { mCornerRadius = radius; } 1095 1096 /** 1097 * Sets the \a unit for the corner radius. 1098 * 1099 * \see cornerRadiusUnit() 1100 * \see setCornerRadius() 1101 */ setCornerRadiusUnit(QgsUnitTypes::RenderUnit unit)1102 void setCornerRadiusUnit( QgsUnitTypes::RenderUnit unit ) { mCornerRadiusUnit = unit; } 1103 1104 /** 1105 * Returns the units for the corner radius. 1106 * 1107 * \see setCornerRadiusUnit() 1108 * \see cornerRadius() 1109 */ cornerRadiusUnit()1110 QgsUnitTypes::RenderUnit cornerRadiusUnit() const { return mCornerRadiusUnit; } 1111 1112 /** 1113 * Sets the map unit \a scale for the corner radius. 1114 * 1115 * \see cornerRadiusMapUnitScale() 1116 * \see setCornerRadiusUnit() 1117 * \see setCornerRadius() 1118 */ setCornerRadiusMapUnitScale(const QgsMapUnitScale & scale)1119 void setCornerRadiusMapUnitScale( const QgsMapUnitScale &scale ) { mCornerRadiusScale = scale; } 1120 1121 /** 1122 * Returns the map unit scale for the corner radius. 1123 * 1124 * \see setCornerRadiusMapUnitScale() 1125 * \see cornerRadiusUnit() 1126 * \see cornerRadius() 1127 */ cornerRadiusMapUnitScale()1128 const QgsMapUnitScale &cornerRadiusMapUnitScale() const { return mCornerRadiusScale; } 1129 1130 1131 protected: 1132 void draw( QgsRenderContext &context, const QRectF &bodyBoundingBox, const double angle, const QgsGeometry &anchor, QgsCallout::QgsCalloutContext &calloutContext ) override; 1133 1134 private: 1135 1136 QPolygonF getPoints( QgsRenderContext &context, QgsPointXY origin, QRectF rect ) const; 1137 1138 #ifdef SIP_RUN 1139 QgsBalloonCallout( const QgsBalloonCallout &other ); 1140 QgsBalloonCallout &operator=( const QgsBalloonCallout & ); 1141 #endif 1142 1143 std::unique_ptr< QgsFillSymbol > mFillSymbol; 1144 1145 double mOffsetFromAnchorDistance = 0; 1146 QgsUnitTypes::RenderUnit mOffsetFromAnchorUnit = QgsUnitTypes::RenderMillimeters; 1147 QgsMapUnitScale mOffsetFromAnchorScale; 1148 1149 QgsMargins mMargins; 1150 QgsUnitTypes::RenderUnit mMarginUnit = QgsUnitTypes::RenderMillimeters; 1151 1152 double mWedgeWidth = 2.64; 1153 QgsUnitTypes::RenderUnit mWedgeWidthUnit = QgsUnitTypes::RenderMillimeters; 1154 QgsMapUnitScale mWedgeWidthScale; 1155 1156 double mCornerRadius = 0.0; 1157 QgsUnitTypes::RenderUnit mCornerRadiusUnit = QgsUnitTypes::RenderMillimeters; 1158 QgsMapUnitScale mCornerRadiusScale; 1159 1160 }; 1161 1162 1163 1164 #endif // QGSCALLOUT_H 1165 1166