QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
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"
27 #include "qgsannotationmanager.h"
28 #include <QPainter>
29 
30 
32  : QgsMapCanvasItem( mapCanvas )
33  , mAnnotation( annotation )
34 {
35  setFlag( QGraphicsItem::ItemIsSelectable, true );
36  if ( mapCanvas && !mapCanvas->annotationsVisible() )
37  setVisible( false );
38 
39  connect( mAnnotation, &QgsAnnotation::appearanceChanged, this, [this] { update(); } );
40  connect( mAnnotation, &QgsAnnotation::moved, this, [this] { updatePosition(); } );
41  connect( mAnnotation, &QgsAnnotation::moved, this, &QgsMapCanvasAnnotationItem::setFeatureForMapPosition );
42  connect( mMapCanvas, &QgsMapCanvas::destinationCrsChanged, this, [ = ] { updatePosition(); } );
43 
44  connect( mAnnotation, &QgsAnnotation::appearanceChanged, this, &QgsMapCanvasAnnotationItem::updateBoundingRect );
45 
46  connect( mMapCanvas, &QgsMapCanvas::layersChanged, this, &QgsMapCanvasAnnotationItem::onCanvasLayersChanged );
47  connect( mAnnotation, &QgsAnnotation::mapLayerChanged, this, &QgsMapCanvasAnnotationItem::onCanvasLayersChanged );
48 
49  //lifetime is tied to annotation!
50  connect( mAnnotation, &QgsAnnotation::destroyed, this, &QgsMapCanvasAnnotationItem::annotationDeleted );
51 
53  setFeatureForMapPosition();
54 }
55 
57 {
58  if ( !mAnnotation )
59  return;
60 
61  if ( mAnnotation->hasFixedMapPosition() )
62  {
64  QgsPointXY coord = mAnnotation->mapPosition();
65  try
66  {
67  coord = t.transform( coord );
68  }
69  catch ( QgsCsException & )
70  {}
71  setPos( toCanvasCoordinates( coord ) );
72  }
73  else
74  {
75  //relative position
76 
77  double x = mAnnotation->relativePosition().x() * mMapCanvas->width();
78  double y = mAnnotation->relativePosition().y() * mMapCanvas->height();
79  setPos( x, y );
80  }
81  updateBoundingRect();
82 }
83 
85 {
86  return mBoundingRect;
87 }
88 
89 void QgsMapCanvasAnnotationItem::updateBoundingRect()
90 {
91  prepareGeometryChange();
92 
94  double fillSymbolBleed = mAnnotation && mAnnotation->fillSymbol() ?
96 
97  const double mmToPixelScale = mMapCanvas->logicalDpiX() / 25.4;
98 
99  if ( mAnnotation && !mAnnotation->hasFixedMapPosition() )
100  {
101  mBoundingRect = QRectF( - fillSymbolBleed, -fillSymbolBleed,
102  mmToPixelScale * mAnnotation->frameSizeMm().width() + fillSymbolBleed * 2,
103  mmToPixelScale * mAnnotation->frameSizeMm().height() + fillSymbolBleed * 2 );
104  }
105  else
106  {
107  double halfSymbolSize = 0.0;
108  if ( mAnnotation && mAnnotation->markerSymbol() )
109  {
110  halfSymbolSize = scaledSymbolSize() / 2.0;
111  }
112 
113  QPointF offset = mAnnotation ? QPointF( mAnnotation->frameOffsetFromReferencePointMm().x() * mmToPixelScale,
114  mAnnotation->frameOffsetFromReferencePointMm().y() * mmToPixelScale ) : QPointF( 0, 0 );
115 
116  QSizeF frameSize = mAnnotation ? QSizeF( mAnnotation->frameSizeMm().width() * mmToPixelScale,
117  mAnnotation->frameSizeMm().height() * mmToPixelScale ) : QSizeF( 0.0, 0.0 );
118 
119  double xMinPos = std::min( -halfSymbolSize, offset.x() - fillSymbolBleed );
120  double xMaxPos = std::max( halfSymbolSize, offset.x() + frameSize.width() + fillSymbolBleed );
121  double yMinPos = std::min( -halfSymbolSize, offset.y() - fillSymbolBleed );
122  double yMaxPos = std::max( halfSymbolSize, offset.y() + frameSize.height() + fillSymbolBleed );
123  mBoundingRect = QRectF( xMinPos, yMinPos, xMaxPos - xMinPos, yMaxPos - yMinPos );
124  }
125 }
126 
127 void 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().contains( mAnnotation->mapLayer() ) );
142  }
143 }
144 
145 void 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  double halfIdentifyWidth = QgsMapTool::searchRadiusMU( mMapCanvas );
155  QgsPointXY mapPosition = mAnnotation->mapPosition();
156 
157  try
158  {
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,
168  mapPosition.x() + halfIdentifyWidth, mapPosition.y() + halfIdentifyWidth );
169 
170  searchRect = mMapCanvas->mapSettings().mapToLayerCoordinates( vectorLayer, searchRect );
171 
172  QgsFeatureIterator fit = vectorLayer->getFeatures( QgsFeatureRequest().setFilterRect( searchRect ).setFlags( QgsFeatureRequest::ExactIntersect ).setLimit( 1 ) );
173 
174  QgsFeature currentFeature;
175  ( void )fit.nextFeature( currentFeature );
176  mAnnotation->setAssociatedFeature( currentFeature );
177 }
178 
179 void QgsMapCanvasAnnotationItem::annotationDeleted()
180 {
181  mAnnotation = nullptr;
182  deleteLater();
183 }
184 
185 void QgsMapCanvasAnnotationItem::drawSelectionBoxes( QPainter *p ) const
186 {
187  if ( !p )
188  {
189  return;
190  }
191 
192  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  QPointF itemPos = mapFromScene( pos );
204 
205  int cursorSensitivity = 7;
206 
207  if ( mAnnotation && mAnnotation->hasFixedMapPosition() &&
208  std::fabs( itemPos.x() ) < cursorSensitivity && std::fabs( itemPos.y() ) < cursorSensitivity ) //move map point if position is close to the origin
209  {
210  return MoveMapPosition;
211  }
212 
213  const double mmToPixelScale = mMapCanvas->logicalDpiX() / 25.4;
214 
215  QPointF offset = mAnnotation && mAnnotation->hasFixedMapPosition() ? mAnnotation->frameOffsetFromReferencePointMm() * mmToPixelScale : QPointF( 0, 0 );
216  QSizeF frameSize = mAnnotation ? mAnnotation->frameSizeMm() * mmToPixelScale : QSizeF( 0, 0 );
217 
218  bool left, right, up, down;
219  left = std::fabs( itemPos.x() - offset.x() ) < cursorSensitivity;
220  right = std::fabs( itemPos.x() - ( offset.x() + frameSize.width() ) ) < cursorSensitivity;
221  up = std::fabs( itemPos.y() - offset.y() ) < cursorSensitivity;
222  down = std::fabs( itemPos.y() - ( offset.y() + frameSize.height() ) ) < cursorSensitivity;
223 
224  if ( left && up )
225  {
226  return ResizeFrameLeftUp;
227  }
228  else if ( right && up )
229  {
230  return ResizeFrameRightUp;
231  }
232  else if ( left && down )
233  {
234  return ResizeFrameLeftDown;
235  }
236  else if ( right && down )
237  {
238  return ResizeFrameRightDown;
239  }
240  if ( left )
241  {
242  return ResizeFrameLeft;
243  }
244  if ( right )
245  {
246  return ResizeFrameRight;
247  }
248  if ( up )
249  {
250  return ResizeFrameUp;
251  }
252  if ( down )
253  {
254  return ResizeFrameDown;
255  }
256 
257  //finally test if pos is in the frame area
258  if ( itemPos.x() >= offset.x() && itemPos.x() <= ( offset.x() + frameSize.width() )
259  && itemPos.y() >= offset.y() && itemPos.y() <= ( offset.y() + frameSize.height() ) )
260  {
261  return MoveFramePosition;
262  }
263  return NoAction;
264 }
265 
267 {
268  switch ( moveAction )
269  {
270  case NoAction:
271  return Qt::ArrowCursor;
272  case MoveMapPosition:
273  case MoveFramePosition:
274  return Qt::SizeAllCursor;
275  case ResizeFrameUp:
276  case ResizeFrameDown:
277  return Qt::SizeVerCursor;
278  case ResizeFrameLeft:
279  case ResizeFrameRight:
280  return Qt::SizeHorCursor;
281  case ResizeFrameLeftUp:
283  return Qt::SizeFDiagCursor;
284  case ResizeFrameRightUp:
285  case ResizeFrameLeftDown:
286  return Qt::SizeBDiagCursor;
287  default:
288  return Qt::ArrowCursor;
289  }
290 }
291 
292 double QgsMapCanvasAnnotationItem::scaledSymbolSize() const
293 {
294  if ( !mAnnotation || !mAnnotation->markerSymbol() )
295  {
296  return 0.0;
297  }
298 
299  if ( !mMapCanvas )
300  {
301  return mAnnotation->markerSymbol()->size();
302  }
303 
304  double dpmm = mMapCanvas->logicalDpiX() / 25.4;
305  return dpmm * mAnnotation->markerSymbol()->size();
306 }
307 
308 void QgsMapCanvasAnnotationItem::paint( QPainter *painter )
309 {
310  if ( !mAnnotation || !mAnnotation->isVisible() )
311  return;
312 
315 
316  if ( mAnnotation )
317  mAnnotation->render( rc );
318 
319  if ( isSelected() )
320  {
321  drawSelectionBoxes( painter );
322  }
323 }
Abstract base class for annotation items which are drawn over a map.
Definition: qgsannotation.h:52
void appearanceChanged()
Emitted whenever the annotation's appearance changes.
QgsMapLayer * mapLayer() const
Returns the map layer associated with the annotation.
bool hasFixedMapPosition
Definition: qgsannotation.h:70
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.
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.
QgsMarkerSymbol * markerSymbol() const
Returns the symbol that is drawn at the annotation's map position.
virtual void setAssociatedFeature(const QgsFeature &feature)
Sets the feature associated with the annotation.
QgsPointXY mapPosition
Definition: qgsannotation.h:71
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:91
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, TransformDirection direction=ForwardTransform) const SIP_THROW(QgsCsException)
Transform the point from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
This class wraps a request for features to a vector layer (or directly its vector data provider).
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
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:86
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:694
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
QList< QgsMapLayer * > layers() 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:212
double size() const
Returns the estimated size for the whole symbol, which is the maximum size of all marker symbol layer...
Definition: qgssymbol.cpp:1735
A class to represent a 2D point.
Definition: qgspointxy.h:44
double y
Definition: qgspointxy.h:48
Q_GADGET double x
Definition: qgspointxy.h:47
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:501
A rectangle specified with double values.
Definition: qgsrectangle.h:42
Contains information about the context of a rendering operation.
void setFlag(Flag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
@ Antialiasing
Use antialiasing while drawing.
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.