QGIS API Documentation  3.23.0-Master (22c16f2067)
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 "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 
92 void QgsMapCanvasAnnotationItem::updateBoundingRect()
93 {
94  prepareGeometryChange();
95 
97  const double fillSymbolBleed = mAnnotation && mAnnotation->fillSymbol() ?
99 
100  const double mmToPixelScale = mMapCanvas->logicalDpiX() / 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 
130 void QgsMapCanvasAnnotationItem::onCanvasLayersChanged()
131 {
132  if ( !mAnnotation )
133  return;
134  if ( !mMapCanvas->annotationsVisible() )
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 
148 void 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( QgsFeatureRequest::ExactIntersect ).setLimit( 1 ) );
176 
177  QgsFeature currentFeature;
178  ( void )fit.nextFeature( currentFeature );
179  mAnnotation->setAssociatedFeature( currentFeature );
180 }
181 
182 void QgsMapCanvasAnnotationItem::annotationDeleted()
183 {
184  mAnnotation = nullptr;
185  deleteLater();
186 }
187 
188 void 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;
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 
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  {
241  return ResizeFrameRightDown;
242  }
243  if ( left )
244  {
245  return ResizeFrameLeft;
246  }
247  if ( right )
248  {
249  return ResizeFrameRight;
250  }
251  if ( up )
252  {
253  return ResizeFrameUp;
254  }
255  if ( down )
256  {
257  return ResizeFrameDown;
258  }
259 
260  //finally test if pos is in the frame area
261  if ( itemPos.x() >= offset.x() && itemPos.x() <= ( offset.x() + frameSize.width() )
262  && itemPos.y() >= offset.y() && itemPos.y() <= ( offset.y() + frameSize.height() ) )
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:
276  case MoveFramePosition:
277  return Qt::SizeAllCursor;
278  case ResizeFrameUp:
279  case ResizeFrameDown:
280  return Qt::SizeVerCursor;
281  case ResizeFrameLeft:
282  case ResizeFrameRight:
283  return Qt::SizeHorCursor;
284  case ResizeFrameLeftUp:
286  return Qt::SizeFDiagCursor;
287  case ResizeFrameRightUp:
288  case ResizeFrameLeftDown:
289  return Qt::SizeBDiagCursor;
290  default:
291  return Qt::ArrowCursor;
292  }
293 }
294 
295 double 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 
311 void 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 }
@ Antialiasing
Use antialiasing while drawing.
Abstract base class for annotation items which are drawn over a map.
Definition: qgsannotation.h:54
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:72
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.
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:73
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:95
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 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 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:88
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:770
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:232
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:59
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:468
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.