QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
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
106 = QRectF( -fillSymbolBleed, -fillSymbolBleed, mmToPixelScale * mAnnotation->frameSizeMm().width() + fillSymbolBleed * 2, 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, mAnnotation->frameOffsetFromReferencePointMm().y() * mmToPixelScale )
117 : QPointF( 0, 0 );
118
119 const QSizeF frameSize = mAnnotation ? QSizeF( mAnnotation->frameSizeMm().width() * mmToPixelScale, mAnnotation->frameSizeMm().height() * mmToPixelScale ) : QSizeF( 0.0, 0.0 );
120
121 const double xMinPos = std::min( -halfSymbolSize, offset.x() - fillSymbolBleed );
122 const double xMaxPos = std::max( halfSymbolSize, offset.x() + frameSize.width() + fillSymbolBleed );
123 const double yMinPos = std::min( -halfSymbolSize, offset.y() - fillSymbolBleed );
124 const double yMaxPos = std::max( halfSymbolSize, offset.y() + frameSize.height() + fillSymbolBleed );
125 mBoundingRect = QRectF( xMinPos, yMinPos, xMaxPos - xMinPos, yMaxPos - yMinPos );
126 }
127}
128
129void QgsMapCanvasAnnotationItem::onCanvasLayersChanged()
130{
131 if ( !mAnnotation )
132 return;
133 if ( !mMapCanvas->annotationsVisible() )
134 {
135 setVisible( false );
136 }
137 else if ( !mAnnotation->mapLayer() )
138 {
139 setVisible( true );
140 }
141 else
142 {
143 setVisible( mMapCanvas->mapSettings().layers( true ).contains( mAnnotation->mapLayer() ) );
144 }
145}
146
147void QgsMapCanvasAnnotationItem::setFeatureForMapPosition()
148{
149 if ( !mAnnotation || !mAnnotation->hasFixedMapPosition() )
150 return;
151
152 QgsVectorLayer *vectorLayer = qobject_cast<QgsVectorLayer *>( mAnnotation->mapLayer() );
153 if ( !vectorLayer )
154 return;
155
156 const double halfIdentifyWidth = QgsMapTool::searchRadiusMU( mMapCanvas );
157 QgsPointXY mapPosition = mAnnotation->mapPosition();
158
159 try
160 {
161 const QgsCoordinateTransform ct( mAnnotation->mapPositionCrs(), mMapCanvas->mapSettings().destinationCrs(), QgsProject::instance() );
162 if ( ct.isValid() )
163 mapPosition = ct.transform( mapPosition );
164 }
165 catch ( QgsCsException & )
166 {}
167
168 QgsRectangle searchRect( mapPosition.x() - halfIdentifyWidth, mapPosition.y() - halfIdentifyWidth, mapPosition.x() + halfIdentifyWidth, mapPosition.y() + halfIdentifyWidth );
169
170 searchRect = mMapCanvas->mapSettings().mapToLayerCoordinates( vectorLayer, searchRect );
171
172 QgsFeatureIterator fit = vectorLayer->getFeatures( QgsFeatureRequest().setFilterRect( searchRect ).setFlags( Qgis::FeatureRequestFlag::ExactIntersect ).setLimit( 1 ) );
173
174 QgsFeature currentFeature;
175 ( void ) fit.nextFeature( currentFeature );
176 mAnnotation->setAssociatedFeature( currentFeature );
177}
178
179void QgsMapCanvasAnnotationItem::annotationDeleted()
180{
181 mAnnotation = nullptr;
182 deleteLater();
183}
184
185void QgsMapCanvasAnnotationItem::drawSelectionBoxes( QPainter *p ) const
186{
187 if ( !p )
188 {
189 return;
190 }
191
192 const double handlerSize = 10;
193 p->setPen( Qt::NoPen );
194 p->setBrush( QColor( 200, 200, 210, 120 ) );
195 p->drawRect( QRectF( mBoundingRect.left(), mBoundingRect.top(), handlerSize, handlerSize ) );
196 p->drawRect( QRectF( mBoundingRect.right() - handlerSize, mBoundingRect.top(), handlerSize, handlerSize ) );
197 p->drawRect( QRectF( mBoundingRect.right() - handlerSize, mBoundingRect.bottom() - handlerSize, handlerSize, handlerSize ) );
198 p->drawRect( QRectF( mBoundingRect.left(), mBoundingRect.bottom() - handlerSize, handlerSize, handlerSize ) );
199}
200
202{
203 const QPointF itemPos = mapFromScene( pos );
204
205 const int cursorSensitivity = 7;
206
207 if ( mAnnotation && mAnnotation->hasFixedMapPosition() && std::fabs( itemPos.x() ) < cursorSensitivity && std::fabs( itemPos.y() ) < cursorSensitivity ) //move map point if position is close to the origin
208 {
209 return MoveMapPosition;
210 }
211
212 const double mmToPixelScale = mMapCanvas->logicalDpiX() / 25.4;
213
214 const QPointF offset = mAnnotation && mAnnotation->hasFixedMapPosition() ? mAnnotation->frameOffsetFromReferencePointMm() * mmToPixelScale : QPointF( 0, 0 );
215 const QSizeF frameSize = mAnnotation ? mAnnotation->frameSizeMm() * mmToPixelScale : QSizeF( 0, 0 );
216
217 bool left, right, up, down, inframe;
218 left = std::fabs( itemPos.x() - offset.x() ) < cursorSensitivity;
219 right = std::fabs( itemPos.x() - ( offset.x() + frameSize.width() ) ) < cursorSensitivity;
220 up = std::fabs( itemPos.y() - offset.y() ) < cursorSensitivity;
221 down = std::fabs( itemPos.y() - ( offset.y() + frameSize.height() ) ) < cursorSensitivity;
222 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() ) );
223
224 // Resize actions are only available if the item is selected
225 // Otherwise, mouse handles are not visible
226 if ( isSelected() )
227 {
228 if ( left && up )
229 {
230 return ResizeFrameLeftUp;
231 }
232 else if ( right && up )
233 {
234 return ResizeFrameRightUp;
235 }
236 else if ( left && down )
237 {
238 return ResizeFrameLeftDown;
239 }
240 else if ( right && down )
241 {
243 }
244 if ( left && inframe )
245 {
246 return ResizeFrameLeft;
247 }
248 if ( right && inframe )
249 {
250 return ResizeFrameRight;
251 }
252 if ( up && inframe )
253 {
254 return ResizeFrameUp;
255 }
256 if ( down && inframe )
257 {
258 return ResizeFrameDown;
259 }
260 }
261
262 //finally test if pos is in the frame area
263 if ( inframe )
264 {
265 return MoveFramePosition;
266 }
267 return NoAction;
268}
269
271{
272 switch ( moveAction )
273 {
274 case NoAction:
275 return Qt::ArrowCursor;
276 case MoveMapPosition:
278 return Qt::SizeAllCursor;
279 case ResizeFrameUp:
280 case ResizeFrameDown:
281 return Qt::SizeVerCursor;
282 case ResizeFrameLeft:
283 case ResizeFrameRight:
284 return Qt::SizeHorCursor;
287 return Qt::SizeFDiagCursor;
290 return Qt::SizeBDiagCursor;
291 default:
292 return Qt::ArrowCursor;
293 }
294}
295
296double QgsMapCanvasAnnotationItem::scaledSymbolSize() const
297{
298 if ( !mAnnotation || !mAnnotation->markerSymbol() )
299 {
300 return 0.0;
301 }
302
303 if ( !mMapCanvas )
304 {
305 return mAnnotation->markerSymbol()->size();
306 }
307
308 const double dpmm = mMapCanvas->logicalDpiX() / 25.4;
309 return dpmm * mAnnotation->markerSymbol()->size();
310}
311
312void QgsMapCanvasAnnotationItem::paint( QPainter *painter )
313{
314 if ( !mAnnotation || !mAnnotation->isVisible() )
315 return;
316
319
320 if ( mAnnotation )
321 mAnnotation->render( rc );
322
323 if ( isSelected() )
324 {
325 drawSelectionBoxes( painter );
326 }
327}
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
Definition qgis.h:2278
@ Antialiasing
Use antialiasing while drawing.
Definition qgis.h:2853
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:62
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
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.