QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsmapcanvasannotationitem.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmapcanvasannotationitem.cpp
3 ------------------------------
4 begin : January 2017
5 copyright : (C) 2017 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19#include "qgsannotation.h"
20#include "qgsmapcanvas.h"
21#include "qgsmaptool.h"
22#include "qgsvectorlayer.h"
23#include "qgsfeatureiterator.h"
24#include "qgsexception.h"
25#include "qgssymbollayerutils.h"
26#include "qgsproject.h"
28#include "qgsfillsymbol.h"
29#include "qgsmarkersymbol.h"
30
31#include <QPainter>
32
33
35 : QgsMapCanvasItem( mapCanvas )
36 , mAnnotation( annotation )
37{
38 setFlag( QGraphicsItem::ItemIsSelectable, true );
39 if ( mapCanvas && !mapCanvas->annotationsVisible() )
40 setVisible( false );
41
42 connect( mAnnotation, &QgsAnnotation::appearanceChanged, this, [this] { update(); } );
43 connect( mAnnotation, &QgsAnnotation::moved, this, [this] { updatePosition(); } );
44 connect( mAnnotation, &QgsAnnotation::moved, this, &QgsMapCanvasAnnotationItem::setFeatureForMapPosition );
45 connect( mMapCanvas, &QgsMapCanvas::destinationCrsChanged, this, [ = ] { updatePosition(); } );
46
47 connect( mAnnotation, &QgsAnnotation::appearanceChanged, this, &QgsMapCanvasAnnotationItem::updateBoundingRect );
48
49 connect( mMapCanvas, &QgsMapCanvas::layersChanged, this, &QgsMapCanvasAnnotationItem::onCanvasLayersChanged );
50 connect( mAnnotation, &QgsAnnotation::mapLayerChanged, this, &QgsMapCanvasAnnotationItem::onCanvasLayersChanged );
51
52 //lifetime is tied to annotation!
53 connect( mAnnotation, &QgsAnnotation::destroyed, this, &QgsMapCanvasAnnotationItem::annotationDeleted );
54
56 setFeatureForMapPosition();
57}
58
60{
61 if ( !mAnnotation )
62 return;
63
64 if ( mAnnotation->hasFixedMapPosition() )
65 {
67 QgsPointXY coord = mAnnotation->mapPosition();
68 try
69 {
70 coord = t.transform( coord );
71 }
72 catch ( QgsCsException & )
73 {}
74 setPos( toCanvasCoordinates( coord ) );
75 }
76 else
77 {
78 //relative position
79
80 const double x = mAnnotation->relativePosition().x() * mMapCanvas->width();
81 const double y = mAnnotation->relativePosition().y() * mMapCanvas->height();
82 setPos( x, y );
83 }
84 updateBoundingRect();
85}
86
88{
89 return mBoundingRect;
90}
91
92void QgsMapCanvasAnnotationItem::updateBoundingRect()
93{
94 prepareGeometryChange();
95
97 const double fillSymbolBleed = mAnnotation && mAnnotation->fillSymbol() ?
99
100 const double mmToPixelScale = mMapCanvas->physicalDpiX() / 25.4;
101
102 if ( mAnnotation && !mAnnotation->hasFixedMapPosition() )
103 {
104 mBoundingRect = QRectF( - fillSymbolBleed, -fillSymbolBleed,
105 mmToPixelScale * mAnnotation->frameSizeMm().width() + fillSymbolBleed * 2,
106 mmToPixelScale * mAnnotation->frameSizeMm().height() + fillSymbolBleed * 2 );
107 }
108 else
109 {
110 double halfSymbolSize = 0.0;
111 if ( mAnnotation && mAnnotation->markerSymbol() )
112 {
113 halfSymbolSize = scaledSymbolSize() / 2.0;
114 }
115
116 const QPointF offset = mAnnotation ? QPointF( mAnnotation->frameOffsetFromReferencePointMm().x() * mmToPixelScale,
117 mAnnotation->frameOffsetFromReferencePointMm().y() * mmToPixelScale ) : QPointF( 0, 0 );
118
119 const QSizeF frameSize = mAnnotation ? QSizeF( mAnnotation->frameSizeMm().width() * mmToPixelScale,
120 mAnnotation->frameSizeMm().height() * mmToPixelScale ) : QSizeF( 0.0, 0.0 );
121
122 const double xMinPos = std::min( -halfSymbolSize, offset.x() - fillSymbolBleed );
123 const double xMaxPos = std::max( halfSymbolSize, offset.x() + frameSize.width() + fillSymbolBleed );
124 const double yMinPos = std::min( -halfSymbolSize, offset.y() - fillSymbolBleed );
125 const double yMaxPos = std::max( halfSymbolSize, offset.y() + frameSize.height() + fillSymbolBleed );
126 mBoundingRect = QRectF( xMinPos, yMinPos, xMaxPos - xMinPos, yMaxPos - yMinPos );
127 }
128}
129
130void QgsMapCanvasAnnotationItem::onCanvasLayersChanged()
131{
132 if ( !mAnnotation )
133 return;
135 {
136 setVisible( false );
137 }
138 else if ( !mAnnotation->mapLayer() )
139 {
140 setVisible( true );
141 }
142 else
143 {
144 setVisible( mMapCanvas->mapSettings().layers( true ).contains( mAnnotation->mapLayer() ) );
145 }
146}
147
148void QgsMapCanvasAnnotationItem::setFeatureForMapPosition()
149{
150 if ( !mAnnotation || !mAnnotation->hasFixedMapPosition() )
151 return;
152
153 QgsVectorLayer *vectorLayer = qobject_cast< QgsVectorLayer * >( mAnnotation->mapLayer() );
154 if ( !vectorLayer )
155 return;
156
157 const double halfIdentifyWidth = QgsMapTool::searchRadiusMU( mMapCanvas );
158 QgsPointXY mapPosition = mAnnotation->mapPosition();
159
160 try
161 {
163 if ( ct.isValid() )
164 mapPosition = ct.transform( mapPosition );
165 }
166 catch ( QgsCsException & )
167 {
168 }
169
170 QgsRectangle searchRect( mapPosition.x() - halfIdentifyWidth, mapPosition.y() - halfIdentifyWidth,
171 mapPosition.x() + halfIdentifyWidth, mapPosition.y() + halfIdentifyWidth );
172
173 searchRect = mMapCanvas->mapSettings().mapToLayerCoordinates( vectorLayer, searchRect );
174
175 QgsFeatureIterator fit = vectorLayer->getFeatures( QgsFeatureRequest().setFilterRect( searchRect ).setFlags( Qgis::FeatureRequestFlag::ExactIntersect ).setLimit( 1 ) );
176
177 QgsFeature currentFeature;
178 ( void )fit.nextFeature( currentFeature );
179 mAnnotation->setAssociatedFeature( currentFeature );
180}
181
182void QgsMapCanvasAnnotationItem::annotationDeleted()
183{
184 mAnnotation = nullptr;
185 deleteLater();
186}
187
188void QgsMapCanvasAnnotationItem::drawSelectionBoxes( QPainter *p ) const
189{
190 if ( !p )
191 {
192 return;
193 }
194
195 const double handlerSize = 10;
196 p->setPen( Qt::NoPen );
197 p->setBrush( QColor( 200, 200, 210, 120 ) );
198 p->drawRect( QRectF( mBoundingRect.left(), mBoundingRect.top(), handlerSize, handlerSize ) );
199 p->drawRect( QRectF( mBoundingRect.right() - handlerSize, mBoundingRect.top(), handlerSize, handlerSize ) );
200 p->drawRect( QRectF( mBoundingRect.right() - handlerSize, mBoundingRect.bottom() - handlerSize, handlerSize, handlerSize ) );
201 p->drawRect( QRectF( mBoundingRect.left(), mBoundingRect.bottom() - handlerSize, handlerSize, handlerSize ) );
202}
203
205{
206 const QPointF itemPos = mapFromScene( pos );
207
208 const int cursorSensitivity = 7;
209
210 if ( mAnnotation && mAnnotation->hasFixedMapPosition() &&
211 std::fabs( itemPos.x() ) < cursorSensitivity && std::fabs( itemPos.y() ) < cursorSensitivity ) //move map point if position is close to the origin
212 {
213 return MoveMapPosition;
214 }
215
216 const double mmToPixelScale = mMapCanvas->logicalDpiX() / 25.4;
217
218 const QPointF offset = mAnnotation && mAnnotation->hasFixedMapPosition() ? mAnnotation->frameOffsetFromReferencePointMm() * mmToPixelScale : QPointF( 0, 0 );
219 const QSizeF frameSize = mAnnotation ? mAnnotation->frameSizeMm() * mmToPixelScale : QSizeF( 0, 0 );
220
221 bool left, right, up, down, inframe;
222 left = std::fabs( itemPos.x() - offset.x() ) < cursorSensitivity;
223 right = std::fabs( itemPos.x() - ( offset.x() + frameSize.width() ) ) < cursorSensitivity;
224 up = std::fabs( itemPos.y() - offset.y() ) < cursorSensitivity;
225 down = std::fabs( itemPos.y() - ( offset.y() + frameSize.height() ) ) < cursorSensitivity;
226 inframe = (
227 itemPos.x() + cursorSensitivity >= offset.x() &&
228 itemPos.x() - cursorSensitivity <= ( offset.x() + frameSize.width() ) &&
229 itemPos.y() + cursorSensitivity >= offset.y() &&
230 itemPos.y() - cursorSensitivity <= ( offset.y() + frameSize.height() ) );
231
232 // Resize actions are only available if the item is selected
233 // Otherwise, mouse handles are not visible
234 if ( isSelected() )
235 {
236 if ( left && up )
237 {
238 return ResizeFrameLeftUp;
239 }
240 else if ( right && up )
241 {
242 return ResizeFrameRightUp;
243 }
244 else if ( left && down )
245 {
246 return ResizeFrameLeftDown;
247 }
248 else if ( right && down )
249 {
251 }
252 if ( left && inframe )
253 {
254 return ResizeFrameLeft;
255 }
256 if ( right && inframe )
257 {
258 return ResizeFrameRight;
259 }
260 if ( up && inframe )
261 {
262 return ResizeFrameUp;
263 }
264 if ( down && inframe )
265 {
266 return ResizeFrameDown;
267 }
268 }
269
270 //finally test if pos is in the frame area
271 if ( inframe )
272 {
273 return MoveFramePosition;
274 }
275 return NoAction;
276}
277
279{
280 switch ( moveAction )
281 {
282 case NoAction:
283 return Qt::ArrowCursor;
284 case MoveMapPosition:
286 return Qt::SizeAllCursor;
287 case ResizeFrameUp:
288 case ResizeFrameDown:
289 return Qt::SizeVerCursor;
290 case ResizeFrameLeft:
291 case ResizeFrameRight:
292 return Qt::SizeHorCursor;
295 return Qt::SizeFDiagCursor;
298 return Qt::SizeBDiagCursor;
299 default:
300 return Qt::ArrowCursor;
301 }
302}
303
304double QgsMapCanvasAnnotationItem::scaledSymbolSize() const
305{
306 if ( !mAnnotation || !mAnnotation->markerSymbol() )
307 {
308 return 0.0;
309 }
310
311 if ( !mMapCanvas )
312 {
313 return mAnnotation->markerSymbol()->size();
314 }
315
316 const double dpmm = mMapCanvas->logicalDpiX() / 25.4;
317 return dpmm * mAnnotation->markerSymbol()->size();
318}
319
320void QgsMapCanvasAnnotationItem::paint( QPainter *painter )
321{
322 if ( !mAnnotation || !mAnnotation->isVisible() )
323 return;
324
327
328 if ( mAnnotation )
329 mAnnotation->render( rc );
330
331 if ( isSelected() )
332 {
333 drawSelectionBoxes( painter );
334 }
335}
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
@ Antialiasing
Use antialiasing while drawing.
Abstract base class for annotation items which are drawn over a map.
Definition: qgsannotation.h:53
void appearanceChanged()
Emitted whenever the annotation's appearance changes.
bool hasFixedMapPosition
Definition: qgsannotation.h:71
QgsCoordinateReferenceSystem mapPositionCrs() const
Returns the CRS of the map position, or an invalid CRS if the annotation does not have a fixed map po...
QPointF frameOffsetFromReferencePointMm() const
Returns the annotation's frame's offset (in millimeters) from the mapPosition() reference point.
QgsMarkerSymbol * markerSymbol() const
Returns the symbol that is drawn at the annotation's map position.
void moved()
Emitted when the annotation's position has changed and items need to be moved to reflect this.
QgsMapLayer * mapLayer() const
Returns the map layer associated with the annotation.
virtual void setAssociatedFeature(const QgsFeature &feature)
Sets the feature associated with the annotation.
QgsPointXY mapPosition
Definition: qgsannotation.h:72
void render(QgsRenderContext &context) const
Renders the annotation to a target render context.
bool isVisible() const
Returns true if the annotation is visible and should be rendered.
Definition: qgsannotation.h:94
QgsFillSymbol * fillSymbol() const
Returns the symbol that is used for rendering the annotation frame.
QSizeF frameSizeMm() const
Returns the size (in millimeters) of the annotation's frame (the main area in which the annotation's ...
QPointF relativePosition() const
Returns the relative position of the annotation, if it is not attached to a fixed map position.
void mapLayerChanged()
Emitted when the map layer associated with the annotation changes.
Class for doing transforms between two map coordinate systems.
QgsPointXY transform(const QgsPointXY &point, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const
Transform the point from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:67
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
This class wraps a request for features to a vector layer (or directly its vector data provider).
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsMapCanvasAnnotationItem(QgsAnnotation *annotation, QgsMapCanvas *mapCanvas)
Constructor for QgsMapCanvasAnnotationItem.
MouseMoveAction moveActionForPosition(QPointF pos) const
Returns the mouse move behavior for a given position in scene coordinates.
Qt::CursorShape cursorShapeForAction(MouseMoveAction moveAction) const
Returns matching cursor shape for a mouse move action.
void paint(QPainter *painter) override
function to be implemented by derived classes
void updatePosition() override
called on changed extent or resize event to update position of the item
MouseMoveAction
Mouse actions for interacting with item.
@ ResizeFrameRightUp
Resize frame right up.
@ MoveFramePosition
Moving position of frame relative to annotation.
@ ResizeFrameRightDown
Resize frame right down.
@ MoveMapPosition
Moving annotation map position.
@ ResizeFrameLeftUp
Resize frame left up.
@ ResizeFrameLeftDown
Resize frame left down.
An abstract class for items that can be placed on the map canvas.
QPointF toCanvasCoordinates(const QgsPointXY &point) const
transformation from map coordinates to screen coordinates
QgsMapCanvas * mMapCanvas
pointer to map canvas
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:93
void destinationCrsChanged()
Emitted when map CRS has changed.
void layersChanged()
Emitted when a new set of layers has been received.
bool annotationsVisible() const
Returns true if annotations are visible within the map canvas.
Definition: qgsmapcanvas.h:775
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
QList< QgsMapLayer * > layers(bool expandGroupLayers=false) const
Returns the list of layers which will be rendered in the map.
QgsPointXY mapToLayerCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from output CRS to layer's CRS
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
static double searchRadiusMU(const QgsRenderContext &context)
Gets search radius in map units for given context.
Definition: qgsmaptool.cpp:238
double size() const
Returns the estimated size for the whole symbol, which is the maximum size of all marker symbol layer...
A class to represent a 2D point.
Definition: qgspointxy.h:60
double y
Definition: qgspointxy.h:64
Q_GADGET double x
Definition: qgspointxy.h:63
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:481
A rectangle specified with double values.
Definition: qgsrectangle.h:42
Contains information about the context of a rendering operation.
void setFlag(Qgis::RenderContextFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
static QgsRenderContext fromQPainter(QPainter *painter)
Creates a default render context given a pixel based QPainter destination.
static double estimateMaxSymbolBleed(QgsSymbol *symbol, const QgsRenderContext &context)
Returns the maximum estimated bleed for the symbol.
Represents a vector layer which manages a vector based data sets.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.