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