QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
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
46
48{
49 if ( !annotation )
50 return false;
51
52 if ( mAnnotations.contains( annotation ) )
53 return true;
54
55 mAnnotations << annotation;
56 emit annotationAdded( annotation );
57 mProject->setDirty( true );
58 return true;
59}
60
62{
63 if ( !annotation )
64 return false;
65
66 if ( !mAnnotations.contains( annotation ) )
67 return false;
68
69 emit annotationAboutToBeRemoved( annotation );
70 mAnnotations.removeAll( annotation );
71 delete annotation;
72 emit annotationRemoved();
73 mProject->setDirty( true );
74 return true;
75}
76
78{
79 for ( auto *a : std::as_const( mAnnotations ) )
80 {
82 }
83}
84
85QList<QgsAnnotation *> QgsAnnotationManager::annotations() const
86{
87 return mAnnotations;
88}
89
90QList<QgsAnnotation *> QgsAnnotationManager::cloneAnnotations() const
91{
92 QList<QgsAnnotation *> results;
93 for ( const auto *a : std::as_const( mAnnotations ) )
94 {
95 results << a->clone();
96 }
97 return results;
98}
99
100bool QgsAnnotationManager::readXml( const QDomElement &element, const QgsReadWriteContext &context )
101{
102 return readXmlPrivate( element, context, nullptr, QgsCoordinateTransformContext() );
103}
104
106 const QDomElement &element, const QgsReadWriteContext &context, QgsAnnotationLayer *layer, const QgsCoordinateTransformContext &transformContext
107)
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 std::unique_ptr< QgsAnnotation > annotation( createAnnotationFromXml( element, context ) );
120 if ( !annotation )
121 return;
122
123 if ( layer )
124 {
125 std::unique_ptr< QgsAnnotationItem > annotationItem = convertToAnnotationItem( annotation.get(), layer, transformContext );
126 if ( annotationItem )
127 {
128 layer->addItem( annotationItem.release() );
129 }
130 else
131 {
132 // could not convert to QgsAnnotationItem, just leave as QgsAnnotation
133 addAnnotation( annotation.release() );
134 }
135 }
136 else
137 {
138 addAnnotation( annotation.release() );
139 }
140 };
141
142 QDomElement annotationsElem = element.firstChildElement( u"Annotations"_s );
143
144 QDomElement annotationElement = annotationsElem.firstChildElement( u"Annotation"_s );
145 while ( !annotationElement.isNull() )
146 {
147 createAnnotationFromElement( annotationElement );
148 annotationElement = annotationElement.nextSiblingElement( u"Annotation"_s );
149 }
150
151 // restore old (pre 3.0) project annotations
152 if ( annotationElement.isNull() )
153 {
154 QDomNodeList oldItemList = element.elementsByTagName( u"TextAnnotationItem"_s );
155 for ( int i = 0; i < oldItemList.size(); ++i )
156 {
157 createAnnotationFromElement( oldItemList.at( i ).toElement() );
158 }
159 oldItemList = element.elementsByTagName( u"FormAnnotationItem"_s );
160 for ( int i = 0; i < oldItemList.size(); ++i )
161 {
162 createAnnotationFromElement( oldItemList.at( i ).toElement() );
163 }
164 oldItemList = element.elementsByTagName( u"HtmlAnnotationItem"_s );
165 for ( int i = 0; i < oldItemList.size(); ++i )
166 {
167 createAnnotationFromElement( oldItemList.at( i ).toElement() );
168 }
169 oldItemList = element.elementsByTagName( u"SVGAnnotationItem"_s );
170 for ( int i = 0; i < oldItemList.size(); ++i )
171 {
172 createAnnotationFromElement( oldItemList.at( i ).toElement() );
173 }
174 }
175
176 return result;
177}
178
179std::unique_ptr<QgsAnnotationItem> QgsAnnotationManager::convertToAnnotationItem( QgsAnnotation *annotation, QgsAnnotationLayer *layer, const QgsCoordinateTransformContext &transformContext )
180{
181 auto setCommonProperties = [layer, &transformContext]( const QgsAnnotation *source, QgsAnnotationItem *destination ) -> bool {
182 destination->setEnabled( source->isVisible() );
183 if ( source->hasFixedMapPosition() )
184 {
185 QgsPointXY mapPosition = source->mapPosition();
186 QgsCoordinateTransform transform( source->mapPositionCrs(), layer->crs(), transformContext );
187 try
188 {
189 mapPosition = transform.transform( mapPosition );
190 }
191 catch ( QgsCsException & )
192 {
193 QgsDebugError( u"Error transforming annotation position"_s );
194 return false;
195 }
196
197 destination->setCalloutAnchor( QgsGeometry::fromPointXY( mapPosition ) );
198
199 auto callout = std::make_unique< QgsBalloonCallout >();
200 if ( QgsFillSymbol *fill = source->fillSymbol() )
201 callout->setFillSymbol( fill->clone() );
202
203 if ( QgsMarkerSymbol *marker = source->markerSymbol() )
204 callout->setMarkerSymbol( marker->clone() );
205 callout->setMargins( source->contentsMargin() );
206 callout->setMarginsUnit( Qgis::RenderUnit::Millimeters );
207 destination->setCallout( callout.release() );
208 }
209
210 if ( source->mapLayer() )
211 layer->setLinkedVisibilityLayer( source->mapLayer() );
212
213 return true;
214 };
215
216 if ( const QgsSvgAnnotation *svg = dynamic_cast< const QgsSvgAnnotation *>( annotation ) )
217 {
218 QgsPointXY mapPosition = svg->mapPosition();
219 QgsCoordinateTransform transform( svg->mapPositionCrs(), layer->crs(), transformContext );
220 try
221 {
222 transform.transform( mapPosition );
223 }
224 catch ( QgsCsException & )
225 {
226 QgsDebugError( u"Error transforming annotation position"_s );
227 }
228
229 auto item = std::make_unique< QgsAnnotationPictureItem >( Qgis::PictureFormat::SVG, svg->filePath(), QgsRectangle::fromCenterAndSize( mapPosition, 1, 1 ) );
230 if ( !setCommonProperties( annotation, item.get() ) )
231 return nullptr;
232
233 const QgsMargins margins = svg->contentsMargin();
234 item->setFixedSize( QSizeF( svg->frameSizeMm().width() - margins.left() - margins.right(), 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(), svg->frameOffsetFromReferencePointMm().y() + margins.top() ) );
242 item->setOffsetFromCalloutUnit( Qgis::RenderUnit::Millimeters );
243 }
244 else
245 {
247 item->setBounds( QgsRectangle( svg->relativePosition().x(), svg->relativePosition().y(), 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( u"Error transforming annotation position"_s );
268 }
269
270 auto 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(), 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(), text->frameOffsetFromReferencePointMm().y() + margins.top() ) );
287 item->setOffsetFromCalloutUnit( Qgis::RenderUnit::Millimeters );
288 item->setBackgroundEnabled( false );
289 item->setFrameEnabled( false );
290 }
291 else
292 {
294 item->setBounds( QgsRectangle( text->relativePosition().x(), text->relativePosition().y(), text->relativePosition().x(), text->relativePosition().y() ) );
295 if ( QgsFillSymbol *fill = text->fillSymbol() )
296 {
297 item->setBackgroundEnabled( true );
298 item->setBackgroundSymbol( fill->clone() );
299 }
300 }
301
302 return item;
303 }
304
305 return nullptr;
306}
307
308QDomElement QgsAnnotationManager::writeXml( QDomDocument &doc, const QgsReadWriteContext &context ) const
309{
310 QDomElement annotationsElem = doc.createElement( u"Annotations"_s );
311 QListIterator<QgsAnnotation *> i( mAnnotations );
312 // save lowermost annotation (at end of list) first
313 i.toBack();
314 while ( i.hasPrevious() )
315 {
316 QgsAnnotation *annotation = i.previous();
317
318 if ( !annotation )
319 {
320 continue;
321 }
322
323 annotation->writeXml( annotationsElem, doc, context );
324 }
325 return annotationsElem;
326}
327
329{
330 if ( mAnnotations.empty() )
331 return true;
332
333 // NOTE: if visitEnter returns false it means "don't visit any annotations", not "abort all further visitations"
334 if ( !visitor->visitEnter( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Annotations, u"annotations"_s, tr( "Annotations" ) ) ) )
335 return true;
336
337 for ( QgsAnnotation *a : mAnnotations )
338 {
339 if ( !a->accept( visitor ) )
340 return false;
341 }
342
343 if ( !visitor->visitExit( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Annotations, u"annotations"_s, tr( "Annotations" ) ) ) )
344 return false;
345
346 return true;
347}
348
349QgsAnnotation *QgsAnnotationManager::createAnnotationFromXml( const QDomElement &element, const QgsReadWriteContext &context )
350{
351 QString type = element.tagName();
352 QgsAnnotation *annotation = QgsApplication::annotationRegistry()->create( type );
353 if ( !annotation )
354 return nullptr;
355
356 annotation->readXml( element, context );
357
358 if ( !annotation->mapPositionCrs().isValid() )
359 {
360 annotation->setMapPositionCrs( mProject->crs() );
361 }
362
363 return annotation;
364}
@ SVG
SVG image.
Definition qgis.h:5435
@ Millimeters
Millimeters.
Definition qgis.h:5341
@ FixedSize
Item is rendered at a fixed size, regardless of map scale. Item's location is georeferenced to a spat...
Definition qgis.h:2571
@ RelativeToMapFrame
Items size and placement is relative to the map's frame, and the item will always be rendered in the ...
Definition qgis.h:2572
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:76
double right() const
Returns the right margin.
Definition qgsmargins.h:82
double bottom() const
Returns the bottom margin.
Definition qgsmargins.h:88
double left() const
Returns the left margin.
Definition qgsmargins.h:70
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:113
QgsCoordinateReferenceSystem crs
Definition qgsproject.h:119
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.