QGIS API Documentation 3.41.0-Master (45a0abf3bec)
Loading...
Searching...
No Matches
qgsannotationmanager.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsannotationmanager.cpp
3 ------------------------
4 Date : January 2017
5 Copyright : (C) 2017 Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17#include "moc_qgsannotationmanager.cpp"
18#include "qgsproject.h"
19#include "qgsannotation.h"
21#include "qgsapplication.h"
23#include "qgsannotationitem.h"
24#include "qgsannotationlayer.h"
25#include "qgssvgannotation.h"
28#include "qgsmarkersymbol.h"
29#include "qgsfillsymbol.h"
30
32 : QObject( project )
33 , mProject( project )
34{
35
36}
37
42
44{
45 if ( !annotation )
46 return false;
47
48 if ( mAnnotations.contains( annotation ) )
49 return true;
50
51 mAnnotations << annotation;
52 emit annotationAdded( annotation );
53 mProject->setDirty( true );
54 return true;
55}
56
58{
59 if ( !annotation )
60 return false;
61
62 if ( !mAnnotations.contains( annotation ) )
63 return false;
64
65 emit annotationAboutToBeRemoved( annotation );
66 mAnnotations.removeAll( annotation );
67 delete annotation;
68 emit annotationRemoved();
69 mProject->setDirty( true );
70 return true;
71}
72
74{
75 for ( auto *a : std::as_const( mAnnotations ) )
76 {
78 }
79}
80
81QList<QgsAnnotation *> QgsAnnotationManager::annotations() const
82{
83 return mAnnotations;
84}
85
86QList<QgsAnnotation *> QgsAnnotationManager::cloneAnnotations() const
87{
88 QList<QgsAnnotation *> results;
89 for ( const auto *a : std::as_const( mAnnotations ) )
90 {
91 results << a->clone();
92 }
93 return results;
94}
95
96bool QgsAnnotationManager::readXml( const QDomElement &element, const QgsReadWriteContext &context )
97{
98 return readXmlPrivate( element, context, nullptr, QgsCoordinateTransformContext() );
99}
100
102{
103 return readXmlPrivate( element, context, layer, transformContext );
104}
105
106bool QgsAnnotationManager::readXmlPrivate( const QDomElement &element, const QgsReadWriteContext &context, QgsAnnotationLayer *layer, const QgsCoordinateTransformContext &transformContext )
107{
108 clear();
109 //restore each annotation
110 bool result = true;
111
112 auto createAnnotationFromElement = [this, &context, layer, &transformContext]( const QDomElement & element )
113 {
114 std::unique_ptr< QgsAnnotation > annotation( createAnnotationFromXml( element, context ) );
115 if ( !annotation )
116 return;
117
118 if ( layer )
119 {
120 std::unique_ptr< QgsAnnotationItem > annotationItem = convertToAnnotationItem( annotation.get(), layer, transformContext );
121 if ( annotationItem )
122 {
123 layer->addItem( annotationItem.release() );
124 }
125 else
126 {
127 // could not convert to QgsAnnotationItem, just leave as QgsAnnotation
128 addAnnotation( annotation.release() );
129 }
130 }
131 else
132 {
133 addAnnotation( annotation.release() );
134 }
135 };
136
137 QDomElement annotationsElem = element.firstChildElement( QStringLiteral( "Annotations" ) );
138
139 QDomElement annotationElement = annotationsElem.firstChildElement( QStringLiteral( "Annotation" ) );
140 while ( ! annotationElement.isNull() )
141 {
142 createAnnotationFromElement( annotationElement );
143 annotationElement = annotationElement.nextSiblingElement( QStringLiteral( "Annotation" ) );
144 }
145
146 // restore old (pre 3.0) project annotations
147 if ( annotationElement.isNull() )
148 {
149 QDomNodeList oldItemList = element.elementsByTagName( QStringLiteral( "TextAnnotationItem" ) );
150 for ( int i = 0; i < oldItemList.size(); ++i )
151 {
152 createAnnotationFromElement( oldItemList.at( i ).toElement() );
153 }
154 oldItemList = element.elementsByTagName( QStringLiteral( "FormAnnotationItem" ) );
155 for ( int i = 0; i < oldItemList.size(); ++i )
156 {
157 createAnnotationFromElement( oldItemList.at( i ).toElement() );
158 }
159 oldItemList = element.elementsByTagName( QStringLiteral( "HtmlAnnotationItem" ) );
160 for ( int i = 0; i < oldItemList.size(); ++i )
161 {
162 createAnnotationFromElement( oldItemList.at( i ).toElement() );
163 }
164 oldItemList = element.elementsByTagName( QStringLiteral( "SVGAnnotationItem" ) );
165 for ( int i = 0; i < oldItemList.size(); ++i )
166 {
167 createAnnotationFromElement( oldItemList.at( i ).toElement() );
168 }
169 }
170
171 return result;
172}
173
174std::unique_ptr<QgsAnnotationItem> QgsAnnotationManager::convertToAnnotationItem( QgsAnnotation *annotation, QgsAnnotationLayer *layer, const QgsCoordinateTransformContext &transformContext )
175{
176 auto setCommonProperties = [layer, &transformContext]( const QgsAnnotation * source, QgsAnnotationItem * destination ) -> bool
177 {
178 destination->setEnabled( source->isVisible() );
179 if ( source->hasFixedMapPosition() )
180 {
181 QgsPointXY mapPosition = source->mapPosition();
182 QgsCoordinateTransform transform( source->mapPositionCrs(), layer->crs(), transformContext );
183 try
184 {
185 mapPosition = transform.transform( mapPosition );
186 }
187 catch ( QgsCsException & )
188 {
189 QgsDebugError( QStringLiteral( "Error transforming annotation position" ) );
190 return false;
191 }
192
193 destination->setCalloutAnchor( QgsGeometry::fromPointXY( mapPosition ) );
194
195 std::unique_ptr< QgsBalloonCallout > callout = std::make_unique< QgsBalloonCallout >();
196 if ( QgsFillSymbol *fill = source->fillSymbol() )
197 callout->setFillSymbol( fill->clone() );
198
199 if ( QgsMarkerSymbol *marker = source->markerSymbol() )
200 callout->setMarkerSymbol( marker->clone() );
201 callout->setMargins( source->contentsMargin() );
202 callout->setMarginsUnit( Qgis::RenderUnit::Millimeters );
203 destination->setCallout( callout.release() );
204 }
205
206 if ( source->mapLayer() )
207 layer->setLinkedVisibilityLayer( source->mapLayer() );
208
209 return true;
210 };
211
212 if ( const QgsSvgAnnotation *svg = dynamic_cast< const QgsSvgAnnotation *>( annotation ) )
213 {
214 QgsPointXY mapPosition = svg->mapPosition();
215 QgsCoordinateTransform transform( svg->mapPositionCrs(), layer->crs(), transformContext );
216 try
217 {
218 transform.transform( mapPosition );
219 }
220 catch ( QgsCsException & )
221 {
222 QgsDebugError( QStringLiteral( "Error transforming annotation position" ) );
223 }
224
225 std::unique_ptr< QgsAnnotationPictureItem > item = std::make_unique< QgsAnnotationPictureItem >( Qgis::PictureFormat::SVG,
226 svg->filePath(), QgsRectangle::fromCenterAndSize( mapPosition, 1, 1 ) );
227 if ( !setCommonProperties( annotation, item.get() ) )
228 return nullptr;
229
230 const QgsMargins margins = svg->contentsMargin();
231 item->setFixedSize( QSizeF( svg->frameSizeMm().width() - margins.left() - margins.right(),
232 svg->frameSizeMm().height() - margins.top() - margins.bottom() ) );
233 item->setFixedSizeUnit( Qgis::RenderUnit::Millimeters );
234
235 if ( svg->hasFixedMapPosition() )
236 {
237 item->setPlacementMode( Qgis::AnnotationPlacementMode::FixedSize );
238
239 item->setOffsetFromCallout( QSizeF( svg->frameOffsetFromReferencePointMm().x() + margins.left(),
240 svg->frameOffsetFromReferencePointMm().y() + margins.top() ) );
241 item->setOffsetFromCalloutUnit( Qgis::RenderUnit::Millimeters );
242 }
243 else
244 {
246 item->setBounds( QgsRectangle( svg->relativePosition().x(), svg->relativePosition().y(),
247 svg->relativePosition().x(), svg->relativePosition().y() ) );
248 if ( QgsFillSymbol *fill = svg->fillSymbol() )
249 {
250 item->setBackgroundEnabled( true );
251 item->setBackgroundSymbol( fill->clone() );
252 }
253 }
254
255 return item;
256 }
257 else if ( const QgsTextAnnotation *text = dynamic_cast< const QgsTextAnnotation *>( annotation ) )
258 {
259 QgsPointXY mapPosition = text->mapPosition();
260 QgsCoordinateTransform transform( text->mapPositionCrs(), layer->crs(), transformContext );
261 try
262 {
263 transform.transform( mapPosition );
264 }
265 catch ( QgsCsException & )
266 {
267 QgsDebugError( QStringLiteral( "Error transforming annotation position" ) );
268 }
269
270 std::unique_ptr< QgsAnnotationRectangleTextItem > item = std::make_unique< QgsAnnotationRectangleTextItem >( text->document()->toHtml(), QgsRectangle::fromCenterAndSize( mapPosition, 1, 1 ) );
271 if ( !setCommonProperties( annotation, item.get() ) )
272 return nullptr;
273
274 QgsTextFormat format = item->format();
275 format.setAllowHtmlFormatting( true );
276 item->setFormat( format );
277
278 const QgsMargins margins = text->contentsMargin();
279 item->setFixedSize( QSizeF( text->frameSizeMm().width() - margins.left() - margins.right(),
280 text->frameSizeMm().height() - margins.top() - margins.bottom() ) );
281 item->setFixedSizeUnit( Qgis::RenderUnit::Millimeters );
282
283 if ( text->hasFixedMapPosition() )
284 {
285 item->setPlacementMode( Qgis::AnnotationPlacementMode::FixedSize );
286
287 item->setOffsetFromCallout( QSizeF( text->frameOffsetFromReferencePointMm().x() + margins.left(),
288 text->frameOffsetFromReferencePointMm().y() + margins.top() ) );
289 item->setOffsetFromCalloutUnit( Qgis::RenderUnit::Millimeters );
290 item->setBackgroundEnabled( false );
291 item->setFrameEnabled( false );
292 }
293 else
294 {
296 item->setBounds( QgsRectangle( text->relativePosition().x(), text->relativePosition().y(),
297 text->relativePosition().x(), text->relativePosition().y() ) );
298 if ( QgsFillSymbol *fill = text->fillSymbol() )
299 {
300 item->setBackgroundEnabled( true );
301 item->setBackgroundSymbol( fill->clone() );
302 }
303 }
304
305 return item;
306 }
307
308 return nullptr;
309}
310
311QDomElement QgsAnnotationManager::writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const
312{
313 QDomElement annotationsElem = doc.createElement( QStringLiteral( "Annotations" ) );
314 QListIterator<QgsAnnotation *> i( mAnnotations );
315 // save lowermost annotation (at end of list) first
316 i.toBack();
317 while ( i.hasPrevious() )
318 {
319 QgsAnnotation *annotation = i.previous();
320
321 if ( !annotation )
322 {
323 continue;
324 }
325
326 annotation->writeXml( annotationsElem, doc, context );
327 }
328 return annotationsElem;
329}
330
332{
333 if ( mAnnotations.empty() )
334 return true;
335
336 // NOTE: if visitEnter returns false it means "don't visit any annotations", not "abort all further visitations"
337 if ( !visitor->visitEnter( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Annotations, QStringLiteral( "annotations" ), tr( "Annotations" ) ) ) )
338 return true;
339
340 for ( QgsAnnotation *a : mAnnotations )
341 {
342 if ( !a->accept( visitor ) )
343 return false;
344 }
345
346 if ( !visitor->visitExit( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Annotations, QStringLiteral( "annotations" ), tr( "Annotations" ) ) ) )
347 return false;
348
349 return true;
350}
351
352QgsAnnotation *QgsAnnotationManager::createAnnotationFromXml( const QDomElement &element, const QgsReadWriteContext &context )
353{
354 QString type = element.tagName();
355 QgsAnnotation *annotation = QgsApplication::annotationRegistry()->create( type );
356 if ( !annotation )
357 return nullptr;
358
359 annotation->readXml( element, context );
360
361 if ( !annotation->mapPositionCrs().isValid() )
362 {
363 annotation->setMapPositionCrs( mProject->crs() );
364 }
365
366 return annotation;
367}
@ Millimeters
Millimeters.
@ FixedSize
Item is rendered at a fixed size, regardless of map scale. Item's location is georeferenced to a spat...
@ RelativeToMapFrame
Items size and placement is relative to the map's frame, and the item will always be rendered in the ...
Abstract base class for annotation items which are drawn with QgsAnnotationLayers.
Represents a map layer containing a set of georeferenced annotations, e.g.
void setLinkedVisibilityLayer(QgsMapLayer *layer)
Sets a linked layer, where the items in this annotation layer will only be visible when the linked la...
QString addItem(QgsAnnotationItem *item)
Adds an item to the layer.
QList< QgsAnnotation * > annotations() const
Returns a list of all annotations contained in the manager.
QList< QgsAnnotation * > cloneAnnotations() const
Returns a list containing clones of all annotations contained in the manager.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Returns a DOM element representing the state of the manager.
void annotationRemoved()
Emitted when an annotation was removed from the manager.
QgsAnnotationManager(QgsProject *project=nullptr)
Constructor for QgsAnnotationManager.
void annotationAboutToBeRemoved(QgsAnnotation *annotation)
Emitted when an annotation is about to be removed from the manager.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads the manager's state from a DOM element, restoring all annotations present in the XML document.
bool readXmlAndUpgradeToAnnotationLayerItems(const QDomElement &element, const QgsReadWriteContext &context, QgsAnnotationLayer *layer, const QgsCoordinateTransformContext &transformContext)
Reads the manager's state from a DOM element, restoring annotations present in the XML document.
bool removeAnnotation(QgsAnnotation *annotation)
Removes an annotation from the manager.
bool accept(QgsStyleEntityVisitorInterface *visitor) const
Accepts the specified style entity visitor, causing it to visit all style entities associated within ...
void clear()
Removes and deletes all annotations from the manager.
bool addAnnotation(QgsAnnotation *annotation)
Adds an annotation to the manager.
void annotationAdded(QgsAnnotation *annotation)
Emitted when a annotation has been added to the manager.
Abstract base class for annotation items which are drawn over a map.
QgsCoordinateReferenceSystem mapPositionCrs() const
Returns the CRS of the map position, or an invalid CRS if the annotation does not have a fixed map po...
void setMapPositionCrs(const QgsCoordinateReferenceSystem &crs)
Sets the CRS of the map position.
virtual void readXml(const QDomElement &itemElem, const QgsReadWriteContext &context)=0
Restores the annotation's state from a DOM element.
virtual void writeXml(QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context) const =0
Writes the annotation state to a DOM element.
static QgsAnnotationRegistry * annotationRegistry()
Returns the application's annotation registry, used for managing annotation types.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Contains information about the context in which a coordinate transform is executed.
Class for doing transforms between two map coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
static QgsGeometry fromPointXY(const QgsPointXY &point)
Creates a new geometry from a QgsPointXY object.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:83
The QgsMargins class defines the four margins of a rectangle.
Definition qgsmargins.h:37
double top() const
Returns the top margin.
Definition qgsmargins.h:77
double right() const
Returns the right margin.
Definition qgsmargins.h:83
double bottom() const
Returns the bottom margin.
Definition qgsmargins.h:89
double left() const
Returns the left margin.
Definition qgsmargins.h:71
A marker symbol type, for rendering Point and MultiPoint geometries.
A class to represent a 2D point.
Definition qgspointxy.h:60
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:107
QgsCoordinateReferenceSystem crs
Definition qgsproject.h:112
void setDirty(bool b=true)
Flag the project as dirty (modified).
The class is used as a container of context for various read/write operations on other objects.
A rectangle specified with double values.
static QgsRectangle fromCenterAndSize(const QgsPointXY &center, double width, double height)
Creates a new rectangle, given the specified center point and width and height.
An interface for classes which can visit style entity (e.g.
@ Annotations
Annotations collection.
virtual bool visitExit(const QgsStyleEntityVisitorInterface::Node &node)
Called when the visitor stops visiting a node.
virtual bool visitEnter(const QgsStyleEntityVisitorInterface::Node &node)
Called when the visitor starts visiting a node.
An annotation which renders the contents of an SVG file.
An annotation item that displays formatted text from a QTextDocument document.
Container for all settings relating to text rendering.
void setAllowHtmlFormatting(bool allow)
Sets whether text should be treated as a HTML document and HTML tags should be used for formatting th...
#define QgsDebugError(str)
Definition qgslogger.h:38
Contains information relating to a node (i.e.