QGIS API Documentation 3.39.0-Master (d85f3c2a281)
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 "qgsproject.h"
18#include "qgsannotation.h"
20#include "qgsapplication.h"
22#include "qgsannotationitem.h"
23#include "qgsannotationlayer.h"
24#include "qgssvgannotation.h"
27#include "qgsmarkersymbol.h"
28#include "qgsfillsymbol.h"
29
31 : QObject( project )
32 , mProject( project )
33{
34
35}
36
41
43{
44 if ( !annotation )
45 return false;
46
47 if ( mAnnotations.contains( annotation ) )
48 return true;
49
50 mAnnotations << annotation;
51 emit annotationAdded( annotation );
52 mProject->setDirty( true );
53 return true;
54}
55
57{
58 if ( !annotation )
59 return false;
60
61 if ( !mAnnotations.contains( annotation ) )
62 return false;
63
64 emit annotationAboutToBeRemoved( annotation );
65 mAnnotations.removeAll( annotation );
66 delete annotation;
67 emit annotationRemoved();
68 mProject->setDirty( true );
69 return true;
70}
71
73{
74 for ( auto *a : std::as_const( mAnnotations ) )
75 {
77 }
78}
79
80QList<QgsAnnotation *> QgsAnnotationManager::annotations() const
81{
82 return mAnnotations;
83}
84
85QList<QgsAnnotation *> QgsAnnotationManager::cloneAnnotations() const
86{
87 QList<QgsAnnotation *> results;
88 for ( const auto *a : std::as_const( mAnnotations ) )
89 {
90 results << a->clone();
91 }
92 return results;
93}
94
95bool QgsAnnotationManager::readXml( const QDomElement &element, const QgsReadWriteContext &context )
96{
97 return readXmlPrivate( element, context, nullptr, QgsCoordinateTransformContext() );
98}
99
101{
102 return readXmlPrivate( element, context, layer, transformContext );
103}
104
105bool QgsAnnotationManager::readXmlPrivate( const QDomElement &element, const QgsReadWriteContext &context, QgsAnnotationLayer *layer, const QgsCoordinateTransformContext &transformContext )
106{
107 clear();
108 //restore each annotation
109 bool result = true;
110
111 auto createAnnotationFromElement = [this, &context, layer, &transformContext]( const QDomElement & element )
112 {
113 std::unique_ptr< QgsAnnotation > annotation( createAnnotationFromXml( element, context ) );
114 if ( !annotation )
115 return;
116
117 if ( layer )
118 {
119 std::unique_ptr< QgsAnnotationItem > annotationItem = convertToAnnotationItem( annotation.get(), layer, transformContext );
120 if ( annotationItem )
121 {
122 layer->addItem( annotationItem.release() );
123 }
124 else
125 {
126 // could not convert to QgsAnnotationItem, just leave as QgsAnnotation
127 addAnnotation( annotation.release() );
128 }
129 }
130 else
131 {
132 addAnnotation( annotation.release() );
133 }
134 };
135
136 QDomElement annotationsElem = element.firstChildElement( QStringLiteral( "Annotations" ) );
137
138 QDomElement annotationElement = annotationsElem.firstChildElement( QStringLiteral( "Annotation" ) );
139 while ( ! annotationElement.isNull() )
140 {
141 createAnnotationFromElement( annotationElement );
142 annotationElement = annotationElement.nextSiblingElement( QStringLiteral( "Annotation" ) );
143 }
144
145 // restore old (pre 3.0) project annotations
146 if ( annotationElement.isNull() )
147 {
148 QDomNodeList oldItemList = element.elementsByTagName( QStringLiteral( "TextAnnotationItem" ) );
149 for ( int i = 0; i < oldItemList.size(); ++i )
150 {
151 createAnnotationFromElement( oldItemList.at( i ).toElement() );
152 }
153 oldItemList = element.elementsByTagName( QStringLiteral( "FormAnnotationItem" ) );
154 for ( int i = 0; i < oldItemList.size(); ++i )
155 {
156 createAnnotationFromElement( oldItemList.at( i ).toElement() );
157 }
158 oldItemList = element.elementsByTagName( QStringLiteral( "HtmlAnnotationItem" ) );
159 for ( int i = 0; i < oldItemList.size(); ++i )
160 {
161 createAnnotationFromElement( oldItemList.at( i ).toElement() );
162 }
163 oldItemList = element.elementsByTagName( QStringLiteral( "SVGAnnotationItem" ) );
164 for ( int i = 0; i < oldItemList.size(); ++i )
165 {
166 createAnnotationFromElement( oldItemList.at( i ).toElement() );
167 }
168 }
169
170 return result;
171}
172
173std::unique_ptr<QgsAnnotationItem> QgsAnnotationManager::convertToAnnotationItem( QgsAnnotation *annotation, QgsAnnotationLayer *layer, const QgsCoordinateTransformContext &transformContext )
174{
175 auto setCommonProperties = [layer, &transformContext]( const QgsAnnotation * source, QgsAnnotationItem * destination ) -> bool
176 {
177 destination->setEnabled( source->isVisible() );
178 if ( source->hasFixedMapPosition() )
179 {
180 QgsPointXY mapPosition = source->mapPosition();
181 QgsCoordinateTransform transform( source->mapPositionCrs(), layer->crs(), transformContext );
182 try
183 {
184 mapPosition = transform.transform( mapPosition );
185 }
186 catch ( QgsCsException & )
187 {
188 QgsDebugError( QStringLiteral( "Error transforming annotation position" ) );
189 return false;
190 }
191
192 destination->setCalloutAnchor( QgsGeometry::fromPointXY( mapPosition ) );
193
194 std::unique_ptr< QgsBalloonCallout > callout = std::make_unique< QgsBalloonCallout >();
195 if ( QgsFillSymbol *fill = source->fillSymbol() )
196 callout->setFillSymbol( fill->clone() );
197
198 if ( QgsMarkerSymbol *marker = source->markerSymbol() )
199 callout->setMarkerSymbol( marker->clone() );
200 callout->setMargins( source->contentsMargin() );
201 callout->setMarginsUnit( Qgis::RenderUnit::Millimeters );
202 destination->setCallout( callout.release() );
203 }
204
205 if ( source->mapLayer() )
206 layer->setLinkedVisibilityLayer( source->mapLayer() );
207
208 return true;
209 };
210
211 if ( const QgsSvgAnnotation *svg = dynamic_cast< const QgsSvgAnnotation *>( annotation ) )
212 {
213 QgsPointXY mapPosition = svg->mapPosition();
214 QgsCoordinateTransform transform( svg->mapPositionCrs(), layer->crs(), transformContext );
215 try
216 {
217 transform.transform( mapPosition );
218 }
219 catch ( QgsCsException & )
220 {
221 QgsDebugError( QStringLiteral( "Error transforming annotation position" ) );
222 }
223
224 std::unique_ptr< QgsAnnotationPictureItem > item = std::make_unique< QgsAnnotationPictureItem >( Qgis::PictureFormat::SVG,
225 svg->filePath(), QgsRectangle::fromCenterAndSize( mapPosition, 1, 1 ) );
226 if ( !setCommonProperties( annotation, item.get() ) )
227 return nullptr;
228
229 const QgsMargins margins = svg->contentsMargin();
230 item->setFixedSize( QSizeF( svg->frameSizeMm().width() - margins.left() - margins.right(),
231 svg->frameSizeMm().height() - margins.top() - margins.bottom() ) );
232 item->setFixedSizeUnit( Qgis::RenderUnit::Millimeters );
233
234 if ( svg->hasFixedMapPosition() )
235 {
236 item->setPlacementMode( Qgis::AnnotationPlacementMode::FixedSize );
237
238 item->setOffsetFromCallout( QSizeF( svg->frameOffsetFromReferencePointMm().x() + margins.left(),
239 svg->frameOffsetFromReferencePointMm().y() + margins.top() ) );
240 item->setOffsetFromCalloutUnit( Qgis::RenderUnit::Millimeters );
241 }
242 else
243 {
245 item->setBounds( QgsRectangle( svg->relativePosition().x(), svg->relativePosition().y(),
246 svg->relativePosition().x(), svg->relativePosition().y() ) );
247 if ( QgsFillSymbol *fill = svg->fillSymbol() )
248 {
249 item->setBackgroundEnabled( true );
250 item->setBackgroundSymbol( fill->clone() );
251 }
252 }
253
254 return item;
255 }
256 else if ( const QgsTextAnnotation *text = dynamic_cast< const QgsTextAnnotation *>( annotation ) )
257 {
258 QgsPointXY mapPosition = text->mapPosition();
259 QgsCoordinateTransform transform( text->mapPositionCrs(), layer->crs(), transformContext );
260 try
261 {
262 transform.transform( mapPosition );
263 }
264 catch ( QgsCsException & )
265 {
266 QgsDebugError( QStringLiteral( "Error transforming annotation position" ) );
267 }
268
269 std::unique_ptr< QgsAnnotationRectangleTextItem > item = std::make_unique< QgsAnnotationRectangleTextItem >( text->document()->toHtml(), QgsRectangle::fromCenterAndSize( mapPosition, 1, 1 ) );
270 if ( !setCommonProperties( annotation, item.get() ) )
271 return nullptr;
272
273 QgsTextFormat format = item->format();
274 format.setAllowHtmlFormatting( true );
275 item->setFormat( format );
276
277 const QgsMargins margins = text->contentsMargin();
278 item->setFixedSize( QSizeF( text->frameSizeMm().width() - margins.left() - margins.right(),
279 text->frameSizeMm().height() - margins.top() - margins.bottom() ) );
280 item->setFixedSizeUnit( Qgis::RenderUnit::Millimeters );
281
282 if ( text->hasFixedMapPosition() )
283 {
284 item->setPlacementMode( Qgis::AnnotationPlacementMode::FixedSize );
285
286 item->setOffsetFromCallout( QSizeF( text->frameOffsetFromReferencePointMm().x() + margins.left(),
287 text->frameOffsetFromReferencePointMm().y() + margins.top() ) );
288 item->setOffsetFromCalloutUnit( Qgis::RenderUnit::Millimeters );
289 item->setBackgroundEnabled( false );
290 item->setFrameEnabled( false );
291 }
292 else
293 {
295 item->setBounds( QgsRectangle( text->relativePosition().x(), text->relativePosition().y(),
296 text->relativePosition().x(), text->relativePosition().y() ) );
297 if ( QgsFillSymbol *fill = text->fillSymbol() )
298 {
299 item->setBackgroundEnabled( true );
300 item->setBackgroundSymbol( fill->clone() );
301 }
302 }
303
304 return item;
305 }
306
307 return nullptr;
308}
309
310QDomElement QgsAnnotationManager::writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const
311{
312 QDomElement annotationsElem = doc.createElement( QStringLiteral( "Annotations" ) );
313 QListIterator<QgsAnnotation *> i( mAnnotations );
314 // save lowermost annotation (at end of list) first
315 i.toBack();
316 while ( i.hasPrevious() )
317 {
318 QgsAnnotation *annotation = i.previous();
319
320 if ( !annotation )
321 {
322 continue;
323 }
324
325 annotation->writeXml( annotationsElem, doc, context );
326 }
327 return annotationsElem;
328}
329
331{
332 if ( mAnnotations.empty() )
333 return true;
334
335 // NOTE: if visitEnter returns false it means "don't visit any annotations", not "abort all further visitations"
336 if ( !visitor->visitEnter( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Annotations, QStringLiteral( "annotations" ), tr( "Annotations" ) ) ) )
337 return true;
338
339 for ( QgsAnnotation *a : mAnnotations )
340 {
341 if ( !a->accept( visitor ) )
342 return false;
343 }
344
345 if ( !visitor->visitExit( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Annotations, QStringLiteral( "annotations" ), tr( "Annotations" ) ) ) )
346 return false;
347
348 return true;
349}
350
351QgsAnnotation *QgsAnnotationManager::createAnnotationFromXml( const QDomElement &element, const QgsReadWriteContext &context )
352{
353 QString type = element.tagName();
354 QgsAnnotation *annotation = QgsApplication::annotationRegistry()->create( type );
355 if ( !annotation )
356 return nullptr;
357
358 annotation->readXml( element, context );
359
360 if ( !annotation->mapPositionCrs().isValid() )
361 {
362 annotation->setMapPositionCrs( mProject->crs() );
363 }
364
365 return annotation;
366}
@ 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.