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