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