QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
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
20#include "qgsannotation.h"
22#include "qgsexception.h"
23#include "qgsfeatureiterator.h"
24#include "qgsfillsymbol.h"
25#include "qgsmapcanvas.h"
26#include "qgsmaptool.h"
27#include "qgsmarkersymbol.h"
28#include "qgsproject.h"
29#include "qgssymbollayerutils.h"
30#include "qgsvectorlayer.h"
31
32#include <QPainter>
33
34#include "moc_qgsmapcanvasannotationitem.cpp"
35
37 : QgsMapCanvasItem( mapCanvas )
38 , mAnnotation( annotation )
39{
40 setFlag( QGraphicsItem::ItemIsSelectable, true );
41 if ( mapCanvas && !mapCanvas->annotationsVisible() )
42 setVisible( false );
43
44 connect( mAnnotation, &QgsAnnotation::appearanceChanged, this, [this] { update(); } );
45 connect( mAnnotation, &QgsAnnotation::moved, this, [this] { updatePosition(); } );
46 connect( mAnnotation, &QgsAnnotation::moved, this, &QgsMapCanvasAnnotationItem::setFeatureForMapPosition );
47 connect( mMapCanvas, &QgsMapCanvas::destinationCrsChanged, this, [this] { updatePosition(); } );
48
49 connect( mAnnotation, &QgsAnnotation::appearanceChanged, this, &QgsMapCanvasAnnotationItem::updateBoundingRect );
50
51 connect( mMapCanvas, &QgsMapCanvas::layersChanged, this, &QgsMapCanvasAnnotationItem::onCanvasLayersChanged );
52 connect( mAnnotation, &QgsAnnotation::mapLayerChanged, this, &QgsMapCanvasAnnotationItem::onCanvasLayersChanged );
53
54 //lifetime is tied to annotation!
55 connect( mAnnotation, &QgsAnnotation::destroyed, this, &QgsMapCanvasAnnotationItem::annotationDeleted );
56
58 setFeatureForMapPosition();
59}
60
62{
63 if ( !mAnnotation )
64 return;
65
66 if ( mAnnotation->hasFixedMapPosition() )
67 {
68 const QgsCoordinateTransform t( mAnnotation->mapPositionCrs(), mMapCanvas->mapSettings().destinationCrs(), QgsProject::instance() );
69 QgsPointXY coord = mAnnotation->mapPosition();
70 try
71 {
72 coord = t.transform( coord );
73 }
74 catch ( QgsCsException & )
75 {}
76 setPos( toCanvasCoordinates( coord ) );
77 }
78 else
79 {
80 //relative position
81
82 const double x = mAnnotation->relativePosition().x() * mMapCanvas->width();
83 const double y = mAnnotation->relativePosition().y() * mMapCanvas->height();
84 setPos( x, y );
85 }
86 updateBoundingRect();
87}
88
90{
91 return mBoundingRect;
92}
93
94void QgsMapCanvasAnnotationItem::updateBoundingRect()
95{
96 prepareGeometryChange();
97
99 const double fillSymbolBleed = mAnnotation && mAnnotation->fillSymbol() ? QgsSymbolLayerUtils::estimateMaxSymbolBleed( mAnnotation->fillSymbol(), rc ) : 0;
100
101 const double mmToPixelScale = mMapCanvas->physicalDpiX() / 25.4;
102
103 if ( mAnnotation && !mAnnotation->hasFixedMapPosition() )
104 {
105 mBoundingRect = QRectF( -fillSymbolBleed, -fillSymbolBleed, mmToPixelScale * mAnnotation->frameSizeMm().width() + fillSymbolBleed * 2, mmToPixelScale * mAnnotation->frameSizeMm().height() + fillSymbolBleed * 2 );
106 }
107 else
108 {
109 double halfSymbolSize = 0.0;
110 if ( mAnnotation && mAnnotation->markerSymbol() )
111 {
112 halfSymbolSize = scaledSymbolSize() / 2.0;
113 }
114
115 const QPointF offset = mAnnotation ? QPointF( mAnnotation->frameOffsetFromReferencePointMm().x() * mmToPixelScale, mAnnotation->frameOffsetFromReferencePointMm().y() * mmToPixelScale ) : QPointF( 0, 0 );
116
117 const QSizeF frameSize = mAnnotation ? QSizeF( mAnnotation->frameSizeMm().width() * mmToPixelScale, mAnnotation->frameSizeMm().height() * mmToPixelScale ) : QSizeF( 0.0, 0.0 );
118
119 const double xMinPos = std::min( -halfSymbolSize, offset.x() - fillSymbolBleed );
120 const double xMaxPos = std::max( halfSymbolSize, offset.x() + frameSize.width() + fillSymbolBleed );
121 const double yMinPos = std::min( -halfSymbolSize, offset.y() - fillSymbolBleed );
122 const double yMaxPos = std::max( halfSymbolSize, offset.y() + frameSize.height() + fillSymbolBleed );
123 mBoundingRect = QRectF( xMinPos, yMinPos, xMaxPos - xMinPos, yMaxPos - yMinPos );
124 }
125}
126
127void QgsMapCanvasAnnotationItem::onCanvasLayersChanged()
128{
129 if ( !mAnnotation )
130 return;
131 if ( !mMapCanvas->annotationsVisible() )
132 {
133 setVisible( false );
134 }
135 else if ( !mAnnotation->mapLayer() )
136 {
137 setVisible( true );
138 }
139 else
140 {
141 setVisible( mMapCanvas->mapSettings().layers( true ).contains( mAnnotation->mapLayer() ) );
142 }
143}
144
145void QgsMapCanvasAnnotationItem::setFeatureForMapPosition()
146{
147 if ( !mAnnotation || !mAnnotation->hasFixedMapPosition() )
148 return;
149
150 QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( mAnnotation->mapLayer() );
151 if ( !vectorLayer )
152 return;
153
154 const double halfIdentifyWidth = QgsMapTool::searchRadiusMU( mMapCanvas );
155 QgsPointXY mapPosition = mAnnotation->mapPosition();
156
157 try
158 {
159 const QgsCoordinateTransform ct( mAnnotation->mapPositionCrs(), mMapCanvas->mapSettings().destinationCrs(), QgsProject::instance() );
160 if ( ct.isValid() )
161 mapPosition = ct.transform( mapPosition );
162 }
163 catch ( QgsCsException & )
164 {
165 }
166
167 QgsRectangle searchRect( mapPosition.x() - halfIdentifyWidth, mapPosition.y() - halfIdentifyWidth, mapPosition.x() + halfIdentifyWidth, mapPosition.y() + halfIdentifyWidth );
168
169 searchRect = mMapCanvas->mapSettings().mapToLayerCoordinates( vectorLayer, searchRect );
170
171 QgsFeatureIterator fit = vectorLayer->getFeatures( QgsFeatureRequest().setFilterRect( searchRect ).setFlags( Qgis::FeatureRequestFlag::ExactIntersect ).setLimit( 1 ) );
172
173 QgsFeature currentFeature;
174 ( void ) fit.nextFeature( currentFeature );
175 mAnnotation->setAssociatedFeature( currentFeature );
176}
177
178void QgsMapCanvasAnnotationItem::annotationDeleted()
179{
180 mAnnotation = nullptr;
181 deleteLater();
182}
183
184void QgsMapCanvasAnnotationItem::drawSelectionBoxes( QPainter *p ) const
185{
186 if ( !p )
187 {
188 return;
189 }
190
191 const double handlerSize = 10;
192 p->setPen( Qt::NoPen );
193 p->setBrush( QColor( 200, 200, 210, 120 ) );
194 p->drawRect( QRectF( mBoundingRect.left(), mBoundingRect.top(), handlerSize, handlerSize ) );
195 p->drawRect( QRectF( mBoundingRect.right() - handlerSize, mBoundingRect.top(), handlerSize, handlerSize ) );
196 p->drawRect( QRectF( mBoundingRect.right() - handlerSize, mBoundingRect.bottom() - handlerSize, handlerSize, handlerSize ) );
197 p->drawRect( QRectF( mBoundingRect.left(), mBoundingRect.bottom() - handlerSize, handlerSize, handlerSize ) );
198}
199
201{
202 const QPointF itemPos = mapFromScene( pos );
203
204 const int cursorSensitivity = 7;
205
206 if ( mAnnotation && mAnnotation->hasFixedMapPosition() && std::fabs( itemPos.x() ) < cursorSensitivity && std::fabs( itemPos.y() ) < cursorSensitivity ) //move map point if position is close to the origin
207 {
208 return MoveMapPosition;
209 }
210
211 const double mmToPixelScale = mMapCanvas->logicalDpiX() / 25.4;
212
213 const QPointF offset = mAnnotation && mAnnotation->hasFixedMapPosition() ? mAnnotation->frameOffsetFromReferencePointMm() * mmToPixelScale : QPointF( 0, 0 );
214 const QSizeF frameSize = mAnnotation ? mAnnotation->frameSizeMm() * mmToPixelScale : QSizeF( 0, 0 );
215
216 bool left, right, up, down, inframe;
217 left = std::fabs( itemPos.x() - offset.x() ) < cursorSensitivity;
218 right = std::fabs( itemPos.x() - ( offset.x() + frameSize.width() ) ) < cursorSensitivity;
219 up = std::fabs( itemPos.y() - offset.y() ) < cursorSensitivity;
220 down = std::fabs( itemPos.y() - ( offset.y() + frameSize.height() ) ) < cursorSensitivity;
221 inframe = ( itemPos.x() + cursorSensitivity >= offset.x() && itemPos.x() - cursorSensitivity <= ( offset.x() + frameSize.width() ) && itemPos.y() + cursorSensitivity >= offset.y() && itemPos.y() - cursorSensitivity <= ( offset.y() + frameSize.height() ) );
222
223 // Resize actions are only available if the item is selected
224 // Otherwise, mouse handles are not visible
225 if ( isSelected() )
226 {
227 if ( left && up )
228 {
229 return ResizeFrameLeftUp;
230 }
231 else if ( right && up )
232 {
233 return ResizeFrameRightUp;
234 }
235 else if ( left && down )
236 {
237 return ResizeFrameLeftDown;
238 }
239 else if ( right && down )
240 {
242 }
243 if ( left && inframe )
244 {
245 return ResizeFrameLeft;
246 }
247 if ( right && inframe )
248 {
249 return ResizeFrameRight;
250 }
251 if ( up && inframe )
252 {
253 return ResizeFrameUp;
254 }
255 if ( down && inframe )
256 {
257 return ResizeFrameDown;
258 }
259 }
260
261 //finally test if pos is in the frame area
262 if ( inframe )
263 {
264 return MoveFramePosition;
265 }
266 return NoAction;
267}
268
270{
271 switch ( moveAction )
272 {
273 case NoAction:
274 return Qt::ArrowCursor;
275 case MoveMapPosition:
277 return Qt::SizeAllCursor;
278 case ResizeFrameUp:
279 case ResizeFrameDown:
280 return Qt::SizeVerCursor;
281 case ResizeFrameLeft:
282 case ResizeFrameRight:
283 return Qt::SizeHorCursor;
286 return Qt::SizeFDiagCursor;
289 return Qt::SizeBDiagCursor;
290 default:
291 return Qt::ArrowCursor;
292 }
293}
294
295double QgsMapCanvasAnnotationItem::scaledSymbolSize() const
296{
297 if ( !mAnnotation || !mAnnotation->markerSymbol() )
298 {
299 return 0.0;
300 }
301
302 if ( !mMapCanvas )
303 {
304 return mAnnotation->markerSymbol()->size();
305 }
306
307 const double dpmm = mMapCanvas->logicalDpiX() / 25.4;
308 return dpmm * mAnnotation->markerSymbol()->size();
309}
310
311void QgsMapCanvasAnnotationItem::paint( QPainter *painter )
312{
313 if ( !mAnnotation || !mAnnotation->isVisible() )
314 return;
315
318
319 if ( mAnnotation )
320 mAnnotation->render( rc );
321
322 if ( isSelected() )
323 {
324 drawSelectionBoxes( painter );
325 }
326}
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
Definition qgis.h:2198
@ Antialiasing
Use antialiasing while drawing.
Definition qgis.h:2756
Abstract base class for annotation items which are drawn over a map.
void appearanceChanged()
Emitted whenever the annotation's appearance changes.
bool hasFixedMapPosition
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.
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 ...
void mapLayerChanged()
Emitted when the map layer associated with the annotation changes.
Handles coordinate transforms between two 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.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
const QgsAnnotation * annotation() const
Returns the item's annotation.
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.
QPointF toCanvasCoordinates(const QgsPointXY &point) const
transformation from map coordinates to screen coordinates
QgsMapCanvas * mMapCanvas
pointer to map canvas
QgsMapCanvasItem(QgsMapCanvas *mapCanvas)
protected constructor: cannot be constructed directly
Map canvas is a class for displaying all GIS data types on a canvas.
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.
static double searchRadiusMU(const QgsRenderContext &context)
Gets search radius in map units for given context.
Represents a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
static QgsProject * instance()
Returns the QgsProject singleton instance.
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.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.