QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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 }
QgsAnnotation::mapLayerChanged
void mapLayerChanged()
Emitted when the map layer associated with the annotation changes.
QgsVectorLayer::getFeatures
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
Definition: qgsvectorlayer.cpp:1052
QgsMapCanvasAnnotationItem::MoveMapPosition
@ MoveMapPosition
Moving annotation map position.
Definition: qgsmapcanvasannotationitem.h:61
QgsMapCanvasAnnotationItem::ResizeFrameLeft
@ ResizeFrameLeft
Resize frame left.
Definition: qgsmapcanvasannotationitem.h:65
QgsPointXY::y
double y
Definition: qgspointxy.h:63
QgsMapCanvasAnnotationItem::NoAction
@ NoAction
No action.
Definition: qgsmapcanvasannotationitem.h:60
QgsMapSettings::mapToLayerCoordinates
QgsPointXY mapToLayerCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from output CRS to layer's CRS
Definition: qgsmapsettings.cpp:633
QgsAnnotation::mapPosition
QgsPointXY mapPosition
Definition: qgsannotation.h:73
QgsMapCanvas::destinationCrsChanged
void destinationCrsChanged()
Emitted when map CRS has changed.
QgsFeatureRequest::ExactIntersect
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
Definition: qgsfeaturerequest.h:117
qgsmapcanvas.h
qgsannotation.h
QgsAnnotation::frameSizeMm
QSizeF frameSizeMm() const
Returns the size (in millimeters) of the annotation's frame (the main area in which the annotation's ...
Definition: qgsannotation.h:223
QgsMapCanvasItem::mMapCanvas
QgsMapCanvas * mMapCanvas
pointer to map canvas
Definition: qgsmapcanvasitem.h:82
QgsMarkerSymbol::size
double size() const
Returns the estimated size for the whole symbol, which is the maximum size of all marker symbol layer...
Definition: qgsmarkersymbol.cpp:197
QgsMapCanvasAnnotationItem::ResizeFrameLeftUp
@ ResizeFrameLeftUp
Resize frame left up.
Definition: qgsmapcanvasannotationitem.h:67
QgsMapCanvas::mapSettings
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
Definition: qgsmapcanvas.cpp:437
QgsAnnotation::render
void render(QgsRenderContext &context) const
Renders the annotation to a target render context.
Definition: qgsannotation.cpp:141
qgssymbollayerutils.h
qgsfeatureiterator.h
QgsMapCanvas
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:89
qgsmapcanvasannotationitem.h
QgsMapCanvasAnnotationItem::cursorShapeForAction
Qt::CursorShape cursorShapeForAction(MouseMoveAction moveAction) const
Returns matching cursor shape for a mouse move action.
Definition: qgsmapcanvasannotationitem.cpp:269
QgsRenderContext
Contains information about the context of a rendering operation.
Definition: qgsrendercontext.h:59
QgsProject::instance
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:480
QgsMapCanvasAnnotationItem::ResizeFrameUp
@ ResizeFrameUp
Resize frame up.
Definition: qgsmapcanvasannotationitem.h:63
QgsMapCanvasAnnotationItem::paint
void paint(QPainter *painter) override
function to be implemented by derived classes
Definition: qgsmapcanvasannotationitem.cpp:311
QgsMapCanvasAnnotationItem::MoveFramePosition
@ MoveFramePosition
Moving position of frame relative to annotation.
Definition: qgsmapcanvasannotationitem.h:62
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:41
QgsAnnotation::mapLayer
QgsMapLayer * mapLayer() const
Returns the map layer associated with the annotation.
Definition: qgsannotation.h:292
QgsFeatureRequest
This class wraps a request for features to a vector layer (or directly its vector data provider).
Definition: qgsfeaturerequest.h:83
QgsCsException
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
QgsAnnotation
Abstract base class for annotation items which are drawn over a map.
Definition: qgsannotation.h:53
QgsMapCanvasItem
An abstract class for items that can be placed on the map canvas.
Definition: qgsmapcanvasitem.h:33
QgsMapCanvasAnnotationItem::MouseMoveAction
MouseMoveAction
Mouse actions for interacting with item.
Definition: qgsmapcanvasannotationitem.h:58
QgsMapCanvasAnnotationItem::QgsMapCanvasAnnotationItem
QgsMapCanvasAnnotationItem(QgsAnnotation *annotation, QgsMapCanvas *mapCanvas)
Constructor for QgsMapCanvasAnnotationItem.
Definition: qgsmapcanvasannotationitem.cpp:34
QgsMapCanvasItem::toCanvasCoordinates
QPointF toCanvasCoordinates(const QgsPointXY &point) const
transformation from map coordinates to screen coordinates
Definition: qgsmapcanvasitem.cpp:61
QgsAnnotation::fillSymbol
QgsFillSymbol * fillSymbol() const
Returns the symbol that is used for rendering the annotation frame.
Definition: qgsannotation.cpp:136
QgsMapCanvas::layersChanged
void layersChanged()
Emitted when a new set of layers has been received.
QgsRenderContext::setFlag
void setFlag(Qgis::RenderContextFlag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
Definition: qgsrendercontext.cpp:216
qgsmaptool.h
QgsMapCanvasAnnotationItem::ResizeFrameLeftDown
@ ResizeFrameLeftDown
Resize frame left down.
Definition: qgsmapcanvasannotationitem.h:69
QgsAnnotation::isVisible
bool isVisible() const
Returns true if the annotation is visible and should be rendered.
Definition: qgsannotation.h:95
qgsannotationmanager.h
QgsAnnotation::hasFixedMapPosition
bool hasFixedMapPosition
Definition: qgsannotation.h:72
QgsAnnotation::setAssociatedFeature
virtual void setAssociatedFeature(const QgsFeature &feature)
Sets the feature associated with the annotation.
Definition: qgsannotation.cpp:189
qgsvectorlayer.h
QgsPointXY
A class to represent a 2D point.
Definition: qgspointxy.h:58
QgsMapSettings::destinationCrs
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
Definition: qgsmapsettings.cpp:358
QgsMapCanvasAnnotationItem::boundingRect
QRectF boundingRect() const override
Definition: qgsmapcanvasannotationitem.cpp:87
QgsMapCanvas::annotationsVisible
bool annotationsVisible() const
Returns true if annotations are visible within the map canvas.
Definition: qgsmapcanvas.h:772
Qgis::RenderContextFlag::Antialiasing
@ Antialiasing
Use antialiasing while drawing.
QgsAnnotation::markerSymbol
QgsMarkerSymbol * markerSymbol() const
Returns the symbol that is drawn at the annotation's map position.
Definition: qgsannotation.h:284
QgsMapCanvasAnnotationItem::moveActionForPosition
MouseMoveAction moveActionForPosition(QPointF pos) const
Returns the mouse move behavior for a given position in scene coordinates.
Definition: qgsmapcanvasannotationitem.cpp:204
QgsFeatureIterator::nextFeature
bool nextFeature(QgsFeature &f)
Definition: qgsfeatureiterator.h:399
QgsMapCanvasAnnotationItem::updatePosition
void updatePosition() override
called on changed extent or resize event to update position of the item
Definition: qgsmapcanvasannotationitem.cpp:59
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:391
QgsMapCanvasAnnotationItem::ResizeFrameDown
@ ResizeFrameDown
Resize frame down.
Definition: qgsmapcanvasannotationitem.h:64
QgsRenderContext::fromQPainter
static QgsRenderContext fromQPainter(QPainter *painter)
Creates a default render context given a pixel based QPainter destination.
Definition: qgsrendercontext.cpp:143
qgsmarkersymbol.h
QgsMapCanvasAnnotationItem::ResizeFrameRight
@ ResizeFrameRight
Resize frame right.
Definition: qgsmapcanvasannotationitem.h:66
QgsPointXY::x
double x
Definition: qgspointxy.h:62
QgsAnnotation::frameOffsetFromReferencePointMm
QPointF frameOffsetFromReferencePointMm() const
Returns the annotation's frame's offset (in millimeters) from the mapPosition() reference point.
Definition: qgsannotation.h:191
QgsAnnotation::appearanceChanged
void appearanceChanged()
Emitted whenever the annotation's appearance changes.
QgsAnnotation::relativePosition
QPointF relativePosition() const
Returns the relative position of the annotation, if it is not attached to a fixed map position.
Definition: qgsannotation.h:155
QgsMapTool::searchRadiusMU
static double searchRadiusMU(const QgsRenderContext &context)
Gets search radius in map units for given context.
Definition: qgsmaptool.cpp:232
QgsAnnotation::mapPositionCrs
QgsCoordinateReferenceSystem mapPositionCrs() const
Returns the CRS of the map position, or an invalid CRS if the annotation does not have a fixed map po...
Definition: qgsannotation.h:141
qgsexception.h
QgsFeature
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:55
QgsSymbolLayerUtils::estimateMaxSymbolBleed
static double estimateMaxSymbolBleed(QgsSymbol *symbol, const QgsRenderContext &context)
Returns the maximum estimated bleed for the symbol.
Definition: qgssymbollayerutils.cpp:934
QgsMapCanvasAnnotationItem::ResizeFrameRightDown
@ ResizeFrameRightDown
Resize frame right down.
Definition: qgsmapcanvasannotationitem.h:70
QgsCoordinateTransform::transform
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.
Definition: qgscoordinatetransform.cpp:272
QgsCoordinateTransform
Class for doing transforms between two map coordinate systems.
Definition: qgscoordinatetransform.h:57
QgsFeatureIterator
Wrapper for iterator of features from vector data provider or vector layer.
Definition: qgsfeatureiterator.h:289
qgsfillsymbol.h
QgsAnnotation::moved
void moved()
Emitted when the annotation's position has changed and items need to be moved to reflect this.
qgsproject.h
QgsMapCanvasAnnotationItem::ResizeFrameRightUp
@ ResizeFrameRightUp
Resize frame right up.
Definition: qgsmapcanvasannotationitem.h:68
QgsMapSettings::layers
QList< QgsMapLayer * > layers(bool expandGroupLayers=false) const
Returns the list of layers which will be rendered in the map.
Definition: qgsmapsettings.cpp:299