1 /***************************************************************************
2                              qgsannotation.h
3                              ---------------
4     begin                : January 2017
5     copyright            : (C) 2017 by 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 #ifndef QGSANNOTATION_H
19 #define QGSANNOTATION_H
20 
21 #include "qgis_core.h"
22 #include "qgis.h"
23 #include "qgspointxy.h"
24 #include "qgscoordinatereferencesystem.h"
25 #include "qgsmargins.h"
26 #include "qgsmaplayer.h"
27 #include "qgsfeature.h"
28 
29 #include <QPointer>
30 
31 class QgsRenderContext;
32 class QgsMarkerSymbol;
33 class QgsFillSymbol;
34 
35 /**
36  * \ingroup core
37  * \class QgsAnnotation
38  *
39  * \brief Abstract base class for annotation items which are drawn over a map.
40  *
41  * QgsAnnotation is an abstract base class for map annotation items. These annotations can be
42  * drawn within a map, and have either a fixed map position (retrieved using mapPosition())
43  * or are placed relative to the map's frame (retrieved using relativePosition()).
44  * Annotations with a fixed map position also have a corresponding
45  * QgsCoordinateReferenceSystem, which can be determined by calling mapPositionCrs().
46  *
47  * Derived classes should implement their custom painting routines within
48  * a renderAnnotation() override.
49  *
50  * \since QGIS 3.0
51  */
52 
53 class CORE_EXPORT QgsAnnotation : public QObject
54 {
55 
56 #ifdef SIP_RUN
57     SIP_CONVERT_TO_SUBCLASS_CODE
58     if ( dynamic_cast< QgsTextAnnotation * >( sipCpp ) )
59       sipType = sipType_QgsTextAnnotation;
60     else if ( dynamic_cast< QgsSvgAnnotation * >( sipCpp ) )
61       sipType = sipType_QgsSvgAnnotation;
62     else if ( dynamic_cast< QgsHtmlAnnotation * >( sipCpp ) )
63       sipType = sipType_QgsHtmlAnnotation;
64     else
65       sipType = NULL;
66     SIP_END
67 #endif
68 
69 
70     Q_OBJECT
71     Q_PROPERTY( bool visible READ isVisible WRITE setVisible )
72     Q_PROPERTY( bool hasFixedMapPosition READ hasFixedMapPosition WRITE setHasFixedMapPosition )
73     Q_PROPERTY( QgsPointXY mapPosition READ mapPosition WRITE setMapPosition )
74     Q_PROPERTY( QSizeF frameSize READ frameSize WRITE setFrameSize )
75 
76   public:
77 
78     /**
79      * Constructor for QgsAnnotation.
80      */
81     QgsAnnotation( QObject *parent SIP_TRANSFERTHIS = nullptr );
82 
83     ~QgsAnnotation() override;
84 
85     /**
86      * Clones the annotation, returning a new copy of the annotation
87      * reflecting the annotation's current state.
88      */
89     virtual QgsAnnotation *clone() const = 0 SIP_FACTORY;
90 
91     /**
92      * Returns TRUE if the annotation is visible and should be rendered.
93      * \see setVisible()
94      */
isVisible()95     bool isVisible() const { return mVisible; }
96 
97     /**
98      * Sets whether the annotation is visible and should be rendered.
99      * \see isVisible()
100      */
101     void setVisible( bool visible );
102 
103     /**
104      * Returns TRUE if the annotation is attached to a fixed map position, or
105      * FALSE if the annotation uses a position relative to the current map
106      * extent.
107      * \see setHasFixedMapPosition()
108      * \see mapPosition()
109      * \see relativePosition()
110      */
hasFixedMapPosition()111     bool hasFixedMapPosition() const { return mHasFixedMapPosition; }
112 
113     /**
114      * Sets whether the annotation is attached to a fixed map position, or
115      * uses a position relative to the current map extent.
116      * \see hasFixedMapPosition()
117      */
118     void setHasFixedMapPosition( bool fixed );
119 
120     /**
121      * Returns the map position of the annotation, if it is attached to a fixed map
122      * position.
123      * \see setMapPosition()
124      * \see hasFixedMapPosition()
125      * \see mapPositionCrs()
126      */
mapPosition()127     QgsPointXY mapPosition() const { return mMapPosition; }
128 
129     /**
130      * Sets the map position of the annotation, if it is attached to a fixed map
131      * position.
132      * \see mapPosition()
133      */
134     void setMapPosition( const QgsPointXY &position );
135 
136     /**
137      * Returns the CRS of the map position, or an invalid CRS if the annotation does
138      * not have a fixed map position.
139      * \see setMapPositionCrs()
140      */
mapPositionCrs()141     QgsCoordinateReferenceSystem mapPositionCrs() const { return mMapPositionCrs; }
142 
143     /**
144      * Sets the CRS of the map position.
145      * \see mapPositionCrs()
146      */
147     void setMapPositionCrs( const QgsCoordinateReferenceSystem &crs );
148 
149     /**
150      * Returns the relative position of the annotation, if it is not attached to a fixed map
151      * position. The coordinates in the return point should be between 0 and 1, and represent
152      * the relative percentage for the position compared to the map width and height.
153      * \see setRelativePosition()
154      */
relativePosition()155     QPointF relativePosition() const { return mRelativePosition; }
156 
157     /**
158      * Sets the relative position of the annotation, if it is not attached to a fixed map
159      * position. The coordinates in the return point should be between 0 and 1, and represent
160      * the relative percentage for the position compared to the map width and height.
161      * \see relativePosition()
162      */
163     void setRelativePosition( QPointF position );
164 
165     /**
166      * Sets the annotation's frame's offset (in pixels) from the mapPosition() reference point.
167      * \see frameOffsetFromReferencePoint()
168      * \deprecated use setFrameOffsetFromReferencePointMm() instead
169      */
170     Q_DECL_DEPRECATED void setFrameOffsetFromReferencePoint( QPointF offset ) SIP_DEPRECATED;
171 
172     /**
173      * Returns the annotation's frame's offset (in pixels) from the mapPosition() reference point.
174      * \see setFrameOffsetFromReferencePoint()
175      * \deprecated use frameOffsetFromReferencePointMm() instead
176      */
177     Q_DECL_DEPRECATED QPointF frameOffsetFromReferencePoint() const SIP_DEPRECATED;
178 
179     /**
180      * Sets the annotation's frame's offset (in millimeters) from the mapPosition() reference point.
181      * \see frameOffsetFromReferencePointMm()
182      * \since QGIS 3.4.8
183      */
184     void setFrameOffsetFromReferencePointMm( QPointF offset );
185 
186     /**
187      * Returns the annotation's frame's offset (in millimeters) from the mapPosition() reference point.
188      * \see setFrameOffsetFromReferencePointMm()
189      * \since QGIS 3.4.8
190      */
frameOffsetFromReferencePointMm()191     QPointF frameOffsetFromReferencePointMm() const { return mOffsetFromReferencePoint; }
192 
193     /**
194      * Sets the size (in pixels) of the annotation's frame (the main area in which
195      * the annotation's content is drawn).
196      * \see frameSize()
197      * \deprecated use setFrameSizeMm() instead
198      */
199     Q_DECL_DEPRECATED void setFrameSize( QSizeF size ) SIP_DEPRECATED;
200 
201     /**
202      * Returns the size (in pixels) of the annotation's frame (the main area in which
203      * the annotation's content is drawn).
204      * \see setFrameSize()
205      * \deprecated use frameSizeMm() instead
206      */
207     Q_DECL_DEPRECATED QSizeF frameSize() const SIP_DEPRECATED;
208 
209     /**
210      * Sets the size (in millimeters) of the annotation's frame (the main area in which
211      * the annotation's content is drawn).
212      * \see frameSizeMm()
213      * \since QGIS 3.4.8
214      */
215     void setFrameSizeMm( QSizeF size );
216 
217     /**
218      * Returns the size (in millimeters) of the annotation's frame (the main area in which
219      * the annotation's content is drawn).
220      * \see setFrameSizeMm()
221      * \since QGIS 3.4.8
222      */
frameSizeMm()223     QSizeF frameSizeMm() const { return mFrameSize; }
224 
225     /**
226      * Sets the margins (in millimeters) between the outside of the frame and the annotation
227      * content.
228      * \see contentsMargin()
229      */
230     void setContentsMargin( const QgsMargins &margins );
231 
232     /**
233      * Returns the margins (in millimeters) between the outside of the frame and the annotation
234      * content.
235      * \see setContentsMargin()
236      */
contentsMargin()237     QgsMargins contentsMargin() const { return mContentsMargins; }
238 
239     /**
240      * Sets the fill symbol used for rendering the annotation frame. Ownership
241      * of the symbol is transferred to the annotation.
242      * \see fillSymbol()
243      */
244     void setFillSymbol( QgsFillSymbol *symbol SIP_TRANSFER );
245 
246     /**
247      * Returns the symbol that is used for rendering the annotation frame.
248      * \see setFillSymbol()
249      */
250     QgsFillSymbol *fillSymbol() const;
251 
252     /**
253      * Renders the annotation to a target render context.
254      */
255     void render( QgsRenderContext &context ) const;
256 
257     /**
258      * Writes the annotation state to a DOM element. Derived classes should
259      * call _writeXml() within their implementation of this method.
260      * \see readXml()
261      * \see _writeXml()
262      */
263     virtual void writeXml( QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context ) const = 0;
264 
265     /**
266      * Restores the annotation's state from a DOM element. Derived classes should
267      * call _readXml() within their implementation of this method.
268      * \see writeXml()
269      * \see _readXml()
270      */
271     virtual void readXml( const QDomElement &itemElem, const QgsReadWriteContext &context ) = 0;
272 
273     /**
274      * Sets the symbol that is drawn at the annotation's map position. Ownership
275      * of the symbol is transferred to the annotation.
276      * \see markerSymbol()
277      */
278     void setMarkerSymbol( QgsMarkerSymbol *symbol SIP_TRANSFER );
279 
280     /**
281      * Returns the symbol that is drawn at the annotation's map position.
282      * \see setMarkerSymbol()
283      */
markerSymbol()284     QgsMarkerSymbol *markerSymbol() const { return mMarkerSymbol.get(); }
285 
286     /**
287      * Returns the map layer associated with the annotation. Annotations can be
288      * associated with a map layer if their visibility should be synchronized
289      * with the layer's visibility.
290      * \see setMapLayer()
291      */
mapLayer()292     QgsMapLayer *mapLayer() const { return mMapLayer.data(); }
293 
294     /**
295      * Sets the map layer associated with the annotation. Annotations can be
296      * associated with a map layer if their visibility should be synchronized
297      * with the layer's visibility.
298      * \see mapLayer()
299      */
300     void setMapLayer( QgsMapLayer *layer );
301 
302     /**
303      * Returns the feature associated with the annotation, or an invalid
304      * feature if none has been set.
305      * \see setAssociatedFeature()
306      */
associatedFeature()307     QgsFeature associatedFeature() const { return mFeature; }
308 
309     /**
310      * Sets the feature associated with the annotation.
311      * \see associatedFeature()
312      */
313     virtual void setAssociatedFeature( const QgsFeature &feature );
314 
315     /**
316      * Accepts the specified style entity \a visitor, causing it to visit all style entities associated
317      * within the annotation.
318      *
319      * Returns TRUE if the visitor should continue visiting other objects, or FALSE if visiting
320      * should be canceled.
321      *
322      * \since QGIS 3.10
323      */
324     virtual bool accept( QgsStyleEntityVisitorInterface *visitor ) const;
325 
326   signals:
327 
328     //! Emitted whenever the annotation's appearance changes
329     void appearanceChanged();
330 
331     /**
332      * Emitted when the annotation's position has changed and items need
333      * to be moved to reflect this.
334      */
335     void moved();
336 
337     /**
338      * Emitted when the map layer associated with the annotation changes.
339      */
340     void mapLayerChanged();
341 
342   protected:
343 
344     /**
345      * Renders the annotation's contents to a target /a context at the specified /a size.
346      * Derived classes should implement their custom annotation drawing logic here.
347      */
348     virtual void renderAnnotation( QgsRenderContext &context, QSizeF size ) const = 0;
349 
350     /**
351      * Returns the minimum frame size for the annotation. Subclasses should implement this if they
352      * cannot be resized smaller than a certain minimum size.
353      */
354     virtual QSizeF minimumFrameSize() const;
355 
356     /**
357      * Writes common annotation properties to a DOM element.
358      * This method should be called from subclasses in their writeXml method.
359      * \see writeXml()
360      * \see _readXml()
361      */
362     void _writeXml( QDomElement &itemElem, QDomDocument &doc, const QgsReadWriteContext &context ) const;
363 
364     /**
365      * Reads common annotation properties from a DOM element.
366      * This method should be called from subclasses in their readXml method.
367      * \see readXml()
368      * \see _writeXml()
369      */
370     void _readXml( const QDomElement &annotationElem, const QgsReadWriteContext &context );
371 
372     /**
373      * Copies common annotation properties to the \a targe
374      * annotation.
375      * Can be used within QgsAnnotation::clone() implementations
376      * to assist with creating copies.
377      */
378     void copyCommonProperties( QgsAnnotation *target ) const;
379 
380   private:
381 
382     //! Draws the annotation frame to a destination painter
383     void drawFrame( QgsRenderContext &context ) const;
384 
385     //! Draws the map position marker symbol to a destination painter
386     void drawMarkerSymbol( QgsRenderContext &context ) const;
387 
388     bool mVisible = true;
389 
390     //! True if the item stays at the same map position, FALSE if the item stays on same screen position
391     bool mHasFixedMapPosition = true;
392 
393     //! Map position (for fixed position items)
394     QgsPointXY mMapPosition;
395 
396     //! CRS of the map position
397     QgsCoordinateReferenceSystem mMapPositionCrs;
398 
399     //! Relative position (for non-fixed items) (0-1)
400     QPointF mRelativePosition;
401 
402     //! Describes the shift of the item content box to the reference point in mm
403     QPointF mOffsetFromReferencePoint = QPointF( 13, -13 );
404 
405     //! Size of the frame in mm (without balloon)
406     QSizeF mFrameSize;
407 
408     //! Point symbol that is to be drawn at the map reference location
409     std::unique_ptr<QgsMarkerSymbol> mMarkerSymbol;
410 
411     QgsMargins mContentsMargins;
412 
413     //! Fill symbol used for drawing annotation
414     std::unique_ptr<QgsFillSymbol> mFillSymbol;
415 
416     //! Associated layer (or NULLPTR if not attached to a layer)
417     QgsWeakMapLayerPointer mMapLayer;
418 
419     //! Associated feature, or invalid feature if no feature associated
420     QgsFeature mFeature;
421 
422     //! Roughly 10 pixels at 96 dpi, to match earlier version rendering
423     double mSegmentPointWidthMm = 2.64;
424 
425 };
426 
427 #endif // QGSANNOTATION_H
428 
429