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