QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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::deleteLater );
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 ( !mMapCanvas->annotationsVisible() )
130  {
131  setVisible( false );
132  }
133  else if ( !mAnnotation->mapLayer() )
134  {
135  setVisible( true );
136  }
137  else
138  {
139  setVisible( mMapCanvas->mapSettings().layers().contains( mAnnotation->mapLayer() ) );
140  }
141 }
142 
143 void QgsMapCanvasAnnotationItem::setFeatureForMapPosition()
144 {
145  if ( !mAnnotation || !mAnnotation->hasFixedMapPosition() )
146  return;
147 
148  QgsVectorLayer *vectorLayer = qobject_cast< QgsVectorLayer * >( mAnnotation->mapLayer() );
149  if ( !vectorLayer )
150  return;
151 
152  double halfIdentifyWidth = QgsMapTool::searchRadiusMU( mMapCanvas );
153  QgsPointXY mapPosition = mAnnotation->mapPosition();
154 
155  try
156  {
158  if ( ct.isValid() )
159  mapPosition = ct.transform( mapPosition );
160  }
161  catch ( QgsCsException & )
162  {
163  }
164 
165  QgsRectangle searchRect( mapPosition.x() - halfIdentifyWidth, mapPosition.y() - halfIdentifyWidth,
166  mapPosition.x() + halfIdentifyWidth, mapPosition.y() + halfIdentifyWidth );
167 
168  searchRect = mMapCanvas->mapSettings().mapToLayerCoordinates( vectorLayer, searchRect );
169 
170  QgsFeatureIterator fit = vectorLayer->getFeatures( QgsFeatureRequest().setFilterRect( searchRect ).setFlags( QgsFeatureRequest::ExactIntersect ).setLimit( 1 ) );
171 
172  QgsFeature currentFeature;
173  ( void )fit.nextFeature( currentFeature );
174  mAnnotation->setAssociatedFeature( currentFeature );
175 }
176 
177 void QgsMapCanvasAnnotationItem::drawSelectionBoxes( QPainter *p ) const
178 {
179  if ( !p )
180  {
181  return;
182  }
183 
184  double handlerSize = 10;
185  p->setPen( Qt::NoPen );
186  p->setBrush( QColor( 200, 200, 210, 120 ) );
187  p->drawRect( QRectF( mBoundingRect.left(), mBoundingRect.top(), handlerSize, handlerSize ) );
188  p->drawRect( QRectF( mBoundingRect.right() - handlerSize, mBoundingRect.top(), handlerSize, handlerSize ) );
189  p->drawRect( QRectF( mBoundingRect.right() - handlerSize, mBoundingRect.bottom() - handlerSize, handlerSize, handlerSize ) );
190  p->drawRect( QRectF( mBoundingRect.left(), mBoundingRect.bottom() - handlerSize, handlerSize, handlerSize ) );
191 }
192 
194 {
195  QPointF itemPos = mapFromScene( pos );
196 
197  int cursorSensitivity = 7;
198 
199  if ( mAnnotation && mAnnotation->hasFixedMapPosition() &&
200  std::fabs( itemPos.x() ) < cursorSensitivity && std::fabs( itemPos.y() ) < cursorSensitivity ) //move map point if position is close to the origin
201  {
202  return MoveMapPosition;
203  }
204 
205  const double mmToPixelScale = mMapCanvas->logicalDpiX() / 25.4;
206 
207  QPointF offset = mAnnotation && mAnnotation->hasFixedMapPosition() ? mAnnotation->frameOffsetFromReferencePointMm() * mmToPixelScale : QPointF( 0, 0 );
208  QSizeF frameSize = mAnnotation ? mAnnotation->frameSizeMm() * mmToPixelScale : QSizeF( 0, 0 );
209 
210  bool left, right, up, down;
211  left = std::fabs( itemPos.x() - offset.x() ) < cursorSensitivity;
212  right = std::fabs( itemPos.x() - ( offset.x() + frameSize.width() ) ) < cursorSensitivity;
213  up = std::fabs( itemPos.y() - offset.y() ) < cursorSensitivity;
214  down = std::fabs( itemPos.y() - ( offset.y() + frameSize.height() ) ) < cursorSensitivity;
215 
216  if ( left && up )
217  {
218  return ResizeFrameLeftUp;
219  }
220  else if ( right && up )
221  {
222  return ResizeFrameRightUp;
223  }
224  else if ( left && down )
225  {
226  return ResizeFrameLeftDown;
227  }
228  else if ( right && down )
229  {
230  return ResizeFrameRightDown;
231  }
232  if ( left )
233  {
234  return ResizeFrameLeft;
235  }
236  if ( right )
237  {
238  return ResizeFrameRight;
239  }
240  if ( up )
241  {
242  return ResizeFrameUp;
243  }
244  if ( down )
245  {
246  return ResizeFrameDown;
247  }
248 
249  //finally test if pos is in the frame area
250  if ( itemPos.x() >= offset.x() && itemPos.x() <= ( offset.x() + frameSize.width() )
251  && itemPos.y() >= offset.y() && itemPos.y() <= ( offset.y() + frameSize.height() ) )
252  {
253  return MoveFramePosition;
254  }
255  return NoAction;
256 }
257 
259 {
260  switch ( moveAction )
261  {
262  case NoAction:
263  return Qt::ArrowCursor;
264  case MoveMapPosition:
265  case MoveFramePosition:
266  return Qt::SizeAllCursor;
267  case ResizeFrameUp:
268  case ResizeFrameDown:
269  return Qt::SizeVerCursor;
270  case ResizeFrameLeft:
271  case ResizeFrameRight:
272  return Qt::SizeHorCursor;
273  case ResizeFrameLeftUp:
275  return Qt::SizeFDiagCursor;
276  case ResizeFrameRightUp:
277  case ResizeFrameLeftDown:
278  return Qt::SizeBDiagCursor;
279  default:
280  return Qt::ArrowCursor;
281  }
282 }
283 
284 double QgsMapCanvasAnnotationItem::scaledSymbolSize() const
285 {
286  if ( !mAnnotation || !mAnnotation->markerSymbol() )
287  {
288  return 0.0;
289  }
290 
291  if ( !mMapCanvas )
292  {
293  return mAnnotation->markerSymbol()->size();
294  }
295 
296  double dpmm = mMapCanvas->logicalDpiX() / 25.4;
297  return dpmm * mAnnotation->markerSymbol()->size();
298 }
299 
300 void QgsMapCanvasAnnotationItem::paint( QPainter *painter )
301 {
302  if ( !mAnnotation || !mAnnotation->isVisible() )
303  return;
304 
307 
308  if ( mAnnotation )
309  mAnnotation->render( rc );
310 
311  if ( isSelected() )
312  {
313  drawSelectionBoxes( painter );
314  }
315 }
void paint(QPainter *painter) override
function to be implemented by derived classes
Wrapper for iterator of features from vector data provider or vector layer.
A rectangle specified with double values.
Definition: qgsrectangle.h:41
bool isVisible() const
Returns true if the annotation is visible and should be rendered.
Definition: qgsannotation.h:89
static double searchRadiusMU(const QgsRenderContext &context)
Gets search radius in map units for given context.
Definition: qgsmaptool.cpp:215
void appearanceChanged()
Emitted whenever the annotation&#39;s appearance changes.
Use exact geometry intersection (slower) instead of bounding boxes.
Use antialiasing while drawing.
void mapLayerChanged()
Emitted when the map layer associated with the annotation changes.
bool annotationsVisible() const
Returns true if annotations are visible within the map canvas.
Definition: qgsmapcanvas.h:601
double y
Definition: qgspointxy.h:48
A class to represent a 2D point.
Definition: qgspointxy.h:43
An abstract class for items that can be placed on the map canvas.
QList< QgsMapLayer * > layers() const
Gets list of layers for map rendering The layers are stored in the reverse order of how they are rend...
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
bool hasFixedMapPosition
Definition: qgsannotation.h:68
Moving position of frame relative to annotation.
MouseMoveAction moveActionForPosition(QPointF pos) const
Returns the mouse move behavior for a given position in scene coordinates.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:73
QPointF relativePosition() const
Returns the relative position of the annotation, if it is not attached to a fixed map position...
QgsCoordinateReferenceSystem destinationCrs() const
returns CRS of destination coordinate reference system
Abstract base class for annotation items which are drawn over a map.
Definition: qgsannotation.h:49
void updatePosition() override
called on changed extent or resize event to update position of the item
QPointF frameOffsetFromReferencePointMm() const
Returns the annotation&#39;s frame&#39;s offset (in millimeters) from the mapPosition() reference point...
void render(QgsRenderContext &context) const
Renders the annotation to a target render context.
static QgsRenderContext fromQPainter(QPainter *painter)
Creates a default render context given a pixel based QPainter destination.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
void destinationCrsChanged()
Emitted when map CRS has changed.
static double estimateMaxSymbolBleed(QgsSymbol *symbol, const QgsRenderContext &context)
Returns the maximum estimated bleed for the symbol.
QgsMarkerSymbol * markerSymbol() const
Returns the symbol that is drawn at the annotation&#39;s map position.
double x
Definition: qgspointxy.h:47
QgsMapLayer * mapLayer() const
Returns the map layer associated with the annotation.
void moved()
Emitted when the annotation&#39;s position has changed and items need to be moved to reflect this...
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
MouseMoveAction
Mouse actions for interacting with item.
QgsCoordinateReferenceSystem mapPositionCrs() const
Returns the CRS of the map position, or an invalid CRS if the annotation does not have a fixed map po...
Contains information about the context of a rendering operation.
QgsMapCanvasAnnotationItem(QgsAnnotation *annotation, QgsMapCanvas *mapCanvas)
Constructor for QgsMapCanvasAnnotationItem.
Qt::CursorShape cursorShapeForAction(MouseMoveAction moveAction) const
Returns matching cursor shape for a mouse move action.
double size() const
Returns the estimated size for the whole symbol, which is the maximum size of all marker symbol layer...
Definition: qgssymbol.cpp:1342
QgsPointXY mapPosition
Definition: qgsannotation.h:69
QgsMapCanvas * mMapCanvas
pointer to map canvas
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:438
Class for doing transforms between two map coordinate systems.
QSizeF frameSizeMm() const
Returns the size (in millimeters) of the annotation&#39;s frame (the main area in which the annotation&#39;s ...
void setFlag(Flag flag, bool on=true)
Enable or disable a particular flag (other flags are not affected)
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
bool nextFeature(QgsFeature &f)
QPointF toCanvasCoordinates(const QgsPointXY &point) const
transformation from map coordinates to screen coordinates
QgsPointXY mapToLayerCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from output CRS to layer&#39;s CRS
Represents a vector layer which manages a vector based data sets.
virtual void setAssociatedFeature(const QgsFeature &feature)
Sets the feature associated with the annotation.
QgsFillSymbol * fillSymbol() const
Returns the symbol that is used for rendering the annotation frame.
void layersChanged()
Emitted when a new set of layers has been received.