QGIS API Documentation  3.6.0-Noosa (5873452)
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  if ( mAnnotation && !mAnnotation->hasFixedMapPosition() )
98  {
99  mBoundingRect = QRectF( - fillSymbolBleed, -fillSymbolBleed, mAnnotation->frameSize().width() + fillSymbolBleed * 2, mAnnotation->frameSize().height() + fillSymbolBleed * 2 );
100  }
101  else
102  {
103  double halfSymbolSize = 0.0;
104  if ( mAnnotation && mAnnotation->markerSymbol() )
105  {
106  halfSymbolSize = scaledSymbolSize() / 2.0;
107  }
108 
109  QPointF offset = mAnnotation ? mAnnotation->frameOffsetFromReferencePoint() : QPointF( 0, 0 );
110 
111  QSizeF frameSize = mAnnotation ? mAnnotation->frameSize() : QSizeF( 0.0, 0.0 );
112 
113  double xMinPos = std::min( -halfSymbolSize, offset.x() - fillSymbolBleed );
114  double xMaxPos = std::max( halfSymbolSize, offset.x() + frameSize.width() + fillSymbolBleed );
115  double yMinPos = std::min( -halfSymbolSize, offset.y() - fillSymbolBleed );
116  double yMaxPos = std::max( halfSymbolSize, offset.y() + frameSize.height() + fillSymbolBleed );
117  mBoundingRect = QRectF( xMinPos, yMinPos, xMaxPos - xMinPos, yMaxPos - yMinPos );
118  }
119 }
120 
121 void QgsMapCanvasAnnotationItem::onCanvasLayersChanged()
122 {
123  if ( !mMapCanvas->annotationsVisible() )
124  {
125  setVisible( false );
126  }
127  else if ( !mAnnotation->mapLayer() )
128  {
129  setVisible( true );
130  }
131  else
132  {
133  setVisible( mMapCanvas->mapSettings().layers().contains( mAnnotation->mapLayer() ) );
134  }
135 }
136 
137 void QgsMapCanvasAnnotationItem::setFeatureForMapPosition()
138 {
139  if ( !mAnnotation || !mAnnotation->hasFixedMapPosition() )
140  return;
141 
142  QgsVectorLayer *vectorLayer = qobject_cast< QgsVectorLayer * >( mAnnotation->mapLayer() );
143  if ( !vectorLayer )
144  return;
145 
146  double halfIdentifyWidth = QgsMapTool::searchRadiusMU( mMapCanvas );
147  QgsPointXY mapPosition = mAnnotation->mapPosition();
148 
149  try
150  {
152  if ( ct.isValid() )
153  mapPosition = ct.transform( mapPosition );
154  }
155  catch ( QgsCsException & )
156  {
157  }
158 
159  QgsRectangle searchRect( mapPosition.x() - halfIdentifyWidth, mapPosition.y() - halfIdentifyWidth,
160  mapPosition.x() + halfIdentifyWidth, mapPosition.y() + halfIdentifyWidth );
161 
162  searchRect = mMapCanvas->mapSettings().mapToLayerCoordinates( vectorLayer, searchRect );
163 
164  QgsFeatureIterator fit = vectorLayer->getFeatures( QgsFeatureRequest().setFilterRect( searchRect ).setFlags( QgsFeatureRequest::ExactIntersect ).setLimit( 1 ) );
165 
166  QgsFeature currentFeature;
167  ( void )fit.nextFeature( currentFeature );
168  mAnnotation->setAssociatedFeature( currentFeature );
169 }
170 
171 void QgsMapCanvasAnnotationItem::drawSelectionBoxes( QPainter *p ) const
172 {
173  if ( !p )
174  {
175  return;
176  }
177 
178  double handlerSize = 10;
179  p->setPen( Qt::NoPen );
180  p->setBrush( QColor( 200, 200, 210, 120 ) );
181  p->drawRect( QRectF( mBoundingRect.left(), mBoundingRect.top(), handlerSize, handlerSize ) );
182  p->drawRect( QRectF( mBoundingRect.right() - handlerSize, mBoundingRect.top(), handlerSize, handlerSize ) );
183  p->drawRect( QRectF( mBoundingRect.right() - handlerSize, mBoundingRect.bottom() - handlerSize, handlerSize, handlerSize ) );
184  p->drawRect( QRectF( mBoundingRect.left(), mBoundingRect.bottom() - handlerSize, handlerSize, handlerSize ) );
185 }
186 
188 {
189  QPointF itemPos = mapFromScene( pos );
190 
191  int cursorSensitivity = 7;
192 
193  if ( mAnnotation && mAnnotation->hasFixedMapPosition() &&
194  std::fabs( itemPos.x() ) < cursorSensitivity && std::fabs( itemPos.y() ) < cursorSensitivity ) //move map point if position is close to the origin
195  {
196  return MoveMapPosition;
197  }
198 
199  QPointF offset = mAnnotation && mAnnotation->hasFixedMapPosition() ? mAnnotation->frameOffsetFromReferencePoint() : QPointF( 0, 0 );
200  QSizeF frameSize = mAnnotation ? mAnnotation->frameSize() : QSizeF( 0, 0 );
201 
202  bool left, right, up, down;
203  left = std::fabs( itemPos.x() - offset.x() ) < cursorSensitivity;
204  right = std::fabs( itemPos.x() - ( offset.x() + frameSize.width() ) ) < cursorSensitivity;
205  up = std::fabs( itemPos.y() - offset.y() ) < cursorSensitivity;
206  down = std::fabs( itemPos.y() - ( offset.y() + frameSize.height() ) ) < cursorSensitivity;
207 
208  if ( left && up )
209  {
210  return ResizeFrameLeftUp;
211  }
212  else if ( right && up )
213  {
214  return ResizeFrameRightUp;
215  }
216  else if ( left && down )
217  {
218  return ResizeFrameLeftDown;
219  }
220  else if ( right && down )
221  {
222  return ResizeFrameRightDown;
223  }
224  if ( left )
225  {
226  return ResizeFrameLeft;
227  }
228  if ( right )
229  {
230  return ResizeFrameRight;
231  }
232  if ( up )
233  {
234  return ResizeFrameUp;
235  }
236  if ( down )
237  {
238  return ResizeFrameDown;
239  }
240 
241  //finally test if pos is in the frame area
242  if ( itemPos.x() >= offset.x() && itemPos.x() <= ( offset.x() + frameSize.width() )
243  && itemPos.y() >= offset.y() && itemPos.y() <= ( offset.y() + frameSize.height() ) )
244  {
245  return MoveFramePosition;
246  }
247  return NoAction;
248 }
249 
251 {
252  switch ( moveAction )
253  {
254  case NoAction:
255  return Qt::ArrowCursor;
256  case MoveMapPosition:
257  case MoveFramePosition:
258  return Qt::SizeAllCursor;
259  case ResizeFrameUp:
260  case ResizeFrameDown:
261  return Qt::SizeVerCursor;
262  case ResizeFrameLeft:
263  case ResizeFrameRight:
264  return Qt::SizeHorCursor;
265  case ResizeFrameLeftUp:
267  return Qt::SizeFDiagCursor;
268  case ResizeFrameRightUp:
269  case ResizeFrameLeftDown:
270  return Qt::SizeBDiagCursor;
271  default:
272  return Qt::ArrowCursor;
273  }
274 }
275 
276 double QgsMapCanvasAnnotationItem::scaledSymbolSize() const
277 {
278  if ( !mAnnotation || !mAnnotation->markerSymbol() )
279  {
280  return 0.0;
281  }
282 
283  if ( !mMapCanvas )
284  {
285  return mAnnotation->markerSymbol()->size();
286  }
287 
288  double dpmm = mMapCanvas->logicalDpiX() / 25.4;
289  return dpmm * mAnnotation->markerSymbol()->size();
290 }
291 
292 void QgsMapCanvasAnnotationItem::paint( QPainter *painter )
293 {
294  if ( !mAnnotation || !mAnnotation->isVisible() )
295  return;
296 
299 
300  if ( mAnnotation )
301  mAnnotation->render( rc );
302 
303  if ( isSelected() )
304  {
305  drawSelectionBoxes( painter );
306  }
307 }
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:214
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:599
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
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.
QPointF frameOffsetFromReferencePoint() const
Returns the annotation&#39;s frame&#39;s offset from the mapPosition() reference point.
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:1300
QgsPointXY mapPosition
Definition: qgsannotation.h:69
QgsMapCanvas * mMapCanvas
pointer to map canvas
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:430
Class for doing transforms between two map coordinate systems.
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
Query 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.
QSizeF frameSize
Definition: qgsannotation.h:70
void layersChanged()
Emitted when a new set of layers has been received.