QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
qgsannotationitem.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsannotationitem.cpp
3  ----------------------
4  begin : February 9, 2010
5  copyright : (C) 2010 by Marco Hugentobler
6  email : marco dot hugentobler at hugis dot net
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 
18 #include "qgsannotationitem.h"
19 #include "qgsmapcanvas.h"
20 #include "qgsrendercontext.h"
21 #include "qgssymbollayerv2utils.h"
22 #include "qgssymbolv2.h"
23 #include <QPainter>
24 #include <QPen>
25 
27  : QgsMapCanvasItem( mapCanvas )
28  , mMapPositionFixed( true )
29  , mMapPositionCrs( QgsCoordinateReferenceSystem() )
30  , mOffsetFromReferencePoint( QPointF( 50, -50 ) )
31  , mBalloonSegment( -1 )
32 {
33  setFlag( QGraphicsItem::ItemIsSelectable, true );
35  mFrameBorderWidth = 1.0;
36  mFrameColor = QColor( 0, 0, 0 );
37  mFrameBackgroundColor = QColor( 255, 255, 255 );
38  setData( 0, "AnnotationItem" );
39 }
40 
42 {
43  delete mMarkerSymbol;
44 }
45 
47 {
48  delete mMarkerSymbol;
49  mMarkerSymbol = symbol;
51 }
52 
54 {
55  mMapPosition = pos;
58 }
59 
61 {
62  double x = pos().x() / mMapCanvas->width();
63  double y = pos().y() / mMapCanvas->height();
64  return QPointF( x, y );
65 }
66 
68 {
69  return 1.0 / mMapCanvas->logicalDpiX() * 25.4;
70 }
71 
73 {
74  mMapPositionCrs = crs;
75 }
76 
78 {
81  updateBalloon();
82 }
83 
85 {
86  if ( mMapPositionFixed && !fixed )
87  {
88  //set map position to the top left corner of the balloon
91  }
92  else if ( fixed && !mMapPositionFixed )
93  {
94  setMapPosition( toMapCoordinates( QPointF( pos() + QPointF( -100, -100 ) ).toPoint() ) );
95  mOffsetFromReferencePoint = QPointF( 100, 100 );
96  }
97  mMapPositionFixed = fixed;
99  updateBalloon();
100  update();
101 }
102 
104 {
105  if ( mMapPositionFixed )
106  {
108  setPos( toCanvasCoordinates( t.transform( mMapPosition ) ) );
109  }
110  else
111  {
112  mMapPosition = toMapCoordinates( pos().toPoint() );
113  }
114 }
115 
117 {
118  return mBoundingRect;
119 }
120 
122 {
123  return QSizeF( 0, 0 );
124 }
125 
127 {
129  double halfSymbolSize = 0.0;
130  if ( mMarkerSymbol )
131  {
132  halfSymbolSize = scaledSymbolSize() / 2.0;
133  }
134 
135  double xMinPos = qMin( -halfSymbolSize, mOffsetFromReferencePoint.x() - mFrameBorderWidth );
136  double xMaxPos = qMax( halfSymbolSize, mOffsetFromReferencePoint.x() + mFrameSize.width() + mFrameBorderWidth );
137  double yMinPos = qMin( -halfSymbolSize, mOffsetFromReferencePoint.y() - mFrameBorderWidth );
138  double yMaxPos = qMax( halfSymbolSize, mOffsetFromReferencePoint.y() + mFrameSize.height() + mFrameBorderWidth );
139  mBoundingRect = QRectF( xMinPos, yMinPos, xMaxPos - xMinPos, yMaxPos - yMinPos );
140 }
141 
143 {
144  //first test if the point is in the frame. In that case we don't need a balloon.
145  if ( !mMapPositionFixed ||
148  {
149  mBalloonSegment = -1;
150  return;
151  }
152 
153  //edge list
154  QList<QLineF> segmentList;
155  segmentList << segment( 0 );
156  segmentList << segment( 1 );
157  segmentList << segment( 2 );
158  segmentList << segment( 3 );
159 
160  //find closest edge / closest edge point
161  double minEdgeDist = DBL_MAX;
162  int minEdgeIndex = -1;
163  QLineF minEdge;
164  QgsPoint minEdgePoint;
165  QgsPoint origin( 0, 0 );
166 
167  for ( int i = 0; i < 4; ++i )
168  {
169  QLineF currentSegment = segmentList.at( i );
170  QgsPoint currentMinDistPoint;
171  double currentMinDist = origin.sqrDistToSegment( currentSegment.x1(), currentSegment.y1(), currentSegment.x2(), currentSegment.y2(), currentMinDistPoint );
172  if ( currentMinDist < minEdgeDist )
173  {
174  minEdgeIndex = i;
175  minEdgePoint = currentMinDistPoint;
176  minEdgeDist = currentMinDist;
177  minEdge = currentSegment;
178  }
179  }
180 
181  if ( minEdgeIndex < 0 )
182  {
183  return;
184  }
185 
186  //make that configurable for the item
187  double segmentPointWidth = 10;
188 
189  mBalloonSegment = minEdgeIndex;
190  QPointF minEdgeEnd = minEdge.p2();
191  mBalloonSegmentPoint1 = QPointF( minEdgePoint.x(), minEdgePoint.y() );
192  if ( sqrt( minEdgePoint.sqrDist( minEdgeEnd.x(), minEdgeEnd.y() ) ) < segmentPointWidth )
193  {
194  mBalloonSegmentPoint1 = pointOnLineWithDistance( minEdge.p2(), minEdge.p1(), segmentPointWidth );
195  }
196 
198 }
199 
201 {
202  QPen framePen( mFrameColor );
203  framePen.setWidthF( mFrameBorderWidth );
204 
205  p->setPen( framePen );
206  QBrush frameBrush( mFrameBackgroundColor );
207  p->setBrush( frameBrush );
208  p->setRenderHint( QPainter::Antialiasing, true );
209 
210  QPolygonF poly;
211  for ( int i = 0; i < 4; ++i )
212  {
213  QLineF currentSegment = segment( i );
214  poly << currentSegment.p1();
215  if ( i == mBalloonSegment && mMapPositionFixed )
216  {
217  poly << mBalloonSegmentPoint1;
218  poly << QPointF( 0, 0 );
219  poly << mBalloonSegmentPoint2;
220  }
221  poly << currentSegment.p2();
222  }
223  p->drawPolygon( poly );
224 }
225 
227 {
228  QSizeF frameSize = minimumFrameSize().expandedTo( size ); //don't allow frame sizes below minimum
231  updateBalloon();
232 }
233 
235 {
236  if ( !p )
237  {
238  return;
239  }
240 
241  QgsRenderContext renderContext;
242  if ( !setRenderContextVariables( p, renderContext ) )
243  {
244  return;
245  }
246 
247  if ( mMarkerSymbol )
248  {
249  mMarkerSymbol->startRender( renderContext );
250  mMarkerSymbol->renderPoint( QPointF( 0, 0 ), nullptr, renderContext );
251  mMarkerSymbol->stopRender( renderContext );
252  }
253 }
254 
256 {
257  if ( !p )
258  {
259  return;
260  }
261 
262  //no selection boxes for composer mode
263  if ( data( 1 ).toString() == "composer" )
264  {
265  return;
266  }
267 
268  double handlerSize = 10;
269  p->setPen( Qt::NoPen );
270  p->setBrush( QColor( 200, 200, 210, 120 ) );
271  p->drawRect( QRectF( mBoundingRect.left(), mBoundingRect.top(), handlerSize, handlerSize ) );
272  p->drawRect( QRectF( mBoundingRect.right() - handlerSize, mBoundingRect.top(), handlerSize, handlerSize ) );
273  p->drawRect( QRectF( mBoundingRect.right() - handlerSize, mBoundingRect.bottom() - handlerSize, handlerSize, handlerSize ) );
274  p->drawRect( QRectF( mBoundingRect.left(), mBoundingRect.bottom() - handlerSize, handlerSize, handlerSize ) );
275 }
276 
278 {
279  switch ( index )
280  {
281  case 0:
284  case 1:
287  case 2:
290  case 3:
293  default:
294  return QLineF();
295  }
296 }
297 
298 QPointF QgsAnnotationItem::pointOnLineWithDistance( QPointF startPoint, QPointF directionPoint, double distance ) const
299 {
300  double dx = directionPoint.x() - startPoint.x();
301  double dy = directionPoint.y() - startPoint.y();
302  double length = sqrt( dx * dx + dy * dy );
303  double scaleFactor = distance / length;
304  return QPointF( startPoint.x() + dx * scaleFactor, startPoint.y() + dy * scaleFactor );
305 }
306 
308 {
309  QPointF itemPos = mapFromScene( pos );
310 
311  int cursorSensitivity = 7;
312 
313  if ( qAbs( itemPos.x() ) < cursorSensitivity && qAbs( itemPos.y() ) < cursorSensitivity ) //move map point if position is close to the origin
314  {
315  return MoveMapPosition;
316  }
317 
318  bool left, right, up, down;
319  left = qAbs( itemPos.x() - mOffsetFromReferencePoint.x() ) < cursorSensitivity;
320  right = qAbs( itemPos.x() - ( mOffsetFromReferencePoint.x() + mFrameSize.width() ) ) < cursorSensitivity;
321  up = qAbs( itemPos.y() - mOffsetFromReferencePoint.y() ) < cursorSensitivity;
322  down = qAbs( itemPos.y() - ( mOffsetFromReferencePoint.y() + mFrameSize.height() ) ) < cursorSensitivity;
323 
324  if ( left && up )
325  {
326  return ResizeFrameLeftUp;
327  }
328  else if ( right && up )
329  {
330  return ResizeFrameRightUp;
331  }
332  else if ( left && down )
333  {
334  return ResizeFrameLeftDown;
335  }
336  else if ( right && down )
337  {
338  return ResizeFrameRightDown;
339  }
340  if ( left )
341  {
342  return ResizeFrameLeft;
343  }
344  if ( right )
345  {
346  return ResizeFrameRight;
347  }
348  if ( up )
349  {
350  return ResizeFrameUp;
351  }
352  if ( down )
353  {
354  return ResizeFrameDown;
355  }
356 
357  //finally test if pos is in the frame area
358  if ( itemPos.x() >= mOffsetFromReferencePoint.x() && itemPos.x() <= ( mOffsetFromReferencePoint.x() + mFrameSize.width() )
359  && itemPos.y() >= mOffsetFromReferencePoint.y() && itemPos.y() <= ( mOffsetFromReferencePoint.y() + mFrameSize.height() ) )
360  {
361  return MoveFramePosition;
362  }
363  return NoAction;
364 }
365 
366 Qt::CursorShape QgsAnnotationItem::cursorShapeForAction( MouseMoveAction moveAction ) const
367 {
368  switch ( moveAction )
369  {
370  case NoAction:
371  return Qt::ArrowCursor;
372  case MoveMapPosition:
373  case MoveFramePosition:
374  return Qt::SizeAllCursor;
375  case ResizeFrameUp:
376  case ResizeFrameDown:
377  return Qt::SizeVerCursor;
378  case ResizeFrameLeft:
379  case ResizeFrameRight:
380  return Qt::SizeHorCursor;
381  case ResizeFrameLeftUp:
383  return Qt::SizeFDiagCursor;
384  case ResizeFrameRightUp:
385  case ResizeFrameLeftDown:
386  return Qt::SizeBDiagCursor;
387  default:
388  return Qt::ArrowCursor;
389  }
390 }
391 
393 {
394  if ( !mMarkerSymbol )
395  {
396  return 0.0;
397  }
398 
399  if ( !mMapCanvas )
400  {
401  return mMarkerSymbol->size();
402  }
403 
404  double dpmm = mMapCanvas->logicalDpiX() / 25.4;
405  return dpmm * mMarkerSymbol->size();
406 }
407 
409 {
410  if ( itemElem.isNull() )
411  {
412  return;
413  }
414  QDomElement annotationElem = doc.createElement( "AnnotationItem" );
415  annotationElem.setAttribute( "mapPositionFixed", mMapPositionFixed );
416  annotationElem.setAttribute( "mapPosX", qgsDoubleToString( mMapPosition.x() ) );
417  annotationElem.setAttribute( "mapPosY", qgsDoubleToString( mMapPosition.y() ) );
418  if ( mMapPositionCrs.isValid() )
419  mMapPositionCrs.writeXML( annotationElem, doc );
420  annotationElem.setAttribute( "offsetX", qgsDoubleToString( mOffsetFromReferencePoint.x() ) );
421  annotationElem.setAttribute( "offsetY", qgsDoubleToString( mOffsetFromReferencePoint.y() ) );
422  annotationElem.setAttribute( "frameWidth", qgsDoubleToString( mFrameSize.width() ) );
423  annotationElem.setAttribute( "frameHeight", qgsDoubleToString( mFrameSize.height() ) );
424  QPointF canvasPos = pos();
425  annotationElem.setAttribute( "canvasPosX", qgsDoubleToString( canvasPos.x() ) );
426  annotationElem.setAttribute( "canvasPosY", qgsDoubleToString( canvasPos.y() ) );
427  annotationElem.setAttribute( "frameBorderWidth", qgsDoubleToString( mFrameBorderWidth ) );
428  annotationElem.setAttribute( "frameColor", mFrameColor.name() );
429  annotationElem.setAttribute( "frameColorAlpha", mFrameColor.alpha() );
430  annotationElem.setAttribute( "frameBackgroundColor", mFrameBackgroundColor.name() );
431  annotationElem.setAttribute( "frameBackgroundColorAlpha", mFrameBackgroundColor.alpha() );
432  annotationElem.setAttribute( "visible", isVisible() );
433  if ( mMarkerSymbol )
434  {
435  QDomElement symbolElem = QgsSymbolLayerV2Utils::saveSymbol( "marker symbol", mMarkerSymbol, doc );
436  if ( !symbolElem.isNull() )
437  {
438  annotationElem.appendChild( symbolElem );
439  }
440  }
441  itemElem.appendChild( annotationElem );
442 }
443 
444 void QgsAnnotationItem::_readXML( const QDomDocument& doc, const QDomElement& annotationElem )
445 {
446  Q_UNUSED( doc );
447  if ( annotationElem.isNull() )
448  {
449  return;
450  }
451  QPointF pos;
452  pos.setX( annotationElem.attribute( "canvasPosX", "0" ).toDouble() );
453  pos.setY( annotationElem.attribute( "canvasPosY", "0" ).toDouble() );
454  setPos( pos );
455  QgsPoint mapPos;
456  mapPos.setX( annotationElem.attribute( "mapPosX", "0" ).toDouble() );
457  mapPos.setY( annotationElem.attribute( "mapPosY", "0" ).toDouble() );
458  mMapPosition = mapPos;
459 
460  if ( !mMapPositionCrs.readXML( annotationElem ) )
461  {
463  }
464 
465  mFrameBorderWidth = annotationElem.attribute( "frameBorderWidth", "0.5" ).toDouble();
466  mFrameColor.setNamedColor( annotationElem.attribute( "frameColor", "#000000" ) );
467  mFrameColor.setAlpha( annotationElem.attribute( "frameColorAlpha", "255" ).toInt() );
468  mFrameBackgroundColor.setNamedColor( annotationElem.attribute( "frameBackgroundColor" ) );
469  mFrameBackgroundColor.setAlpha( annotationElem.attribute( "frameBackgroundColorAlpha", "255" ).toInt() );
470  mFrameSize.setWidth( annotationElem.attribute( "frameWidth", "50" ).toDouble() );
471  mFrameSize.setHeight( annotationElem.attribute( "frameHeight", "50" ).toDouble() );
472  mOffsetFromReferencePoint.setX( annotationElem.attribute( "offsetX", "0" ).toDouble() );
473  mOffsetFromReferencePoint.setY( annotationElem.attribute( "offsetY", "0" ).toDouble() );
474  mMapPositionFixed = annotationElem.attribute( "mapPositionFixed", "1" ).toInt();
475  setVisible( annotationElem.attribute( "visible", "1" ).toInt() );
476 
477  //marker symbol
478  QDomElement symbolElem = annotationElem.firstChildElement( "symbol" );
479  if ( !symbolElem.isNull() )
480  {
481  QgsMarkerSymbolV2* symbol = QgsSymbolLayerV2Utils::loadSymbol<QgsMarkerSymbolV2>( symbolElem );
482  if ( symbol )
483  {
484  delete mMarkerSymbol;
485  mMarkerSymbol = symbol;
486  }
487  }
488 
490  updateBalloon();
491 }
492 
493 void QgsAnnotationItem::setItemData( int role, const QVariant& value )
494 {
495  setData( role, value );
496 }
497 
499 {
500  // maintain API compatibility, if annotation item subclasses only implement the paint( QPainter* ) override
501  paint( painter );
502 }
503 
505 {
506  Q_UNUSED( painter );
507 }
static unsigned index
qreal x() const
qreal y() const
QgsMarkerSymbolV2 * mMarkerSymbol
Point symbol that is to be drawn at the map reference location.
void setRenderHint(RenderHint hint, bool on)
QgsPoint toMapCoordinates(QPoint point) const
transformation from screen coordinates to map coordinates
void _readXML(const QDomDocument &doc, const QDomElement &annotationElem)
double mFrameBorderWidth
Width of the frame.
QDomNode appendChild(const QDomNode &newChild)
void setFlag(GraphicsItemFlag flag, bool enabled)
QString name() const
QString attribute(const QString &name, const QString &defValue) const
void setData(int key, const QVariant &value)
QPointF mOffsetFromReferencePoint
Describes the shift of the item content box to the reference point.
const T & at(int i) const
void drawMarkerSymbol(QPainter *p) const
Draws the map position marker symbol to a destination painter.
An abstract class for items that can be placed on the map canvas.
Qt::CursorShape cursorShapeForAction(MouseMoveAction moveAction) const
Returns suitable cursor shape for mouse move action.
virtual void setItemData(int role, const QVariant &value) override
deprecated - do not use
static QDomElement saveSymbol(const QString &symbolName, QgsSymbolV2 *symbol, QDomDocument &doc)
void drawPolygon(const QPointF *points, int pointCount, Qt::FillRule fillRule)
QSizeF expandedTo(const QSizeF &otherSize) const
void setAlpha(int alpha)
qreal top() const
QSizeF frameSize() const
double size() const
Returns the size for the whole symbol, which is the maximum size of all marker symbol layers in the s...
void drawSelectionBoxes(QPainter *p) const
Draws selection handles around the item.
virtual void setMapPositionCrs(const QgsCoordinateReferenceSystem &crs)
Sets the CRS of the map position.
QgsAnnotationItem::MouseMoveAction moveActionForPosition(QPointF pos) const
Returns the mouse move behaviour for a given position.
void setNamedColor(const QString &name)
double toDouble(bool *ok) const
qreal left() const
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:109
void update(const QRectF &rect)
void setFrameSize(QSizeF size)
double y() const
Get the y value of the point.
Definition: qgspoint.h:193
void updatePosition() override
called on changed extent or resize event to update position of the item
void setMarkerSymbol(QgsMarkerSymbolV2 *symbol)
Set symbol that is drawn on map position.
QRectF mBoundingRect
Bounding rect (including item frame and balloon)
QPointF toCanvasCoordinates(const QgsPoint &point) const
transformation from map coordinates to screen coordinates
qreal bottom() const
void drawRect(const QRectF &rectangle)
qreal y1() const
qreal y2() const
QPointF pos() const
qreal x1() const
qreal x2() const
qreal x() const
qreal y() const
QPointF p1() const
QPointF p2() const
QPointF mBalloonSegmentPoint1
First segment point for drawing the connection (ccw direction)
virtual void setMapPosition(const QgsPoint &pos)
void startRender(QgsRenderContext &context, const QgsFields *fields=nullptr)
double sqrDistToSegment(double x1, double y1, double x2, double y2, QgsPoint &minDistPoint, double epsilon=DEFAULT_SEGMENT_EPSILON) const
Returns the minimum distance between this point and a segment.
Definition: qgspoint.cpp:457
bool readXML(const QDomNode &theNode)
Restores state from the given Dom node.
void setPen(const QColor &color)
void setAttribute(const QString &name, const QString &value)
void setWidth(qreal width)
void setPos(const QPointF &pos)
int toInt(bool *ok, int base) const
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:341
QgsAnnotationItem(QgsMapCanvas *mapCanvas)
QPointF mapFromScene(const QPointF &point) const
void setWidthF(qreal width)
void setBrush(const QBrush &brush)
void _writeXML(QDomDocument &doc, QDomElement &itemElem) const
virtual QSizeF minimumFrameSize() const
int mBalloonSegment
Segment number where the connection to the map point is attached.
QRectF boundingRect() const override
default implementation for canvas items
int alpha() const
A class to represent a point.
Definition: qgspoint.h:117
void prepareGeometryChange()
int logicalDpiX() const
qreal right() const
void setX(double x)
Sets the x value of the point.
Definition: qgspoint.h:162
void setOffsetFromReferencePoint(QPointF offset)
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget=nullptr) override
Paint the annotation to a destination painter.
QgsCoordinateReferenceSystem mMapPositionCrs
CRS of the map position.
void setY(double y)
Sets the y value of the point.
Definition: qgspoint.h:170
bool isNull() const
const QgsMapSettings & mapSettings() const
Get access to properties used for map rendering.
double scaledSymbolSize() const
Returns the symbol size scaled in (mapcanvas) pixels.
bool mMapPositionFixed
True: the item stays at the same map position, False: the item stays on same screen position...
QColor mFrameColor
Frame / balloon color.
bool isVisible() const
QVariant data(int key) const
Contains information about the context of a rendering operation.
void stopRender(QgsRenderContext &context)
void updateBalloon()
Check where to attach the balloon connection between frame and map point.
QgsMapCanvas * mMapCanvas
pointer to map canvas
void setX(qreal x)
void setY(qreal y)
QDomElement firstChildElement(const QString &tagName) const
Class for storing a coordinate reference system (CRS)
Class for doing transforms between two map coordinate systems.
void renderPoint(QPointF point, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
void setVisible(bool visible)
QLineF segment(int index) const
Returns frame width in painter units.
virtual QPointF relativePosition() const override
Returns the relative position of the annotation, if it is not attached to a fixed map position...
double sqrDist(double x, double y) const
Returns the squared distance between this point a specified x, y coordinate.
Definition: qgspoint.cpp:353
QPointF pointOnLineWithDistance(QPointF startPoint, QPointF directionPoint, double distance) const
Returns a point on the line from startPoint to directionPoint that is a certain distance away from th...
QPointF mBalloonSegmentPoint2
Second segment point for drawing the balloon connection (ccw direction)
bool writeXML(QDomNode &theNode, QDomDocument &theDoc) const
Stores state to the given Dom node in the given document.
QDomElement createElement(const QString &tagName)
virtual double scaleFactor() const override
Returns a scaling factor which should be applied to painters before rendering the item...
qreal height() const
QgsPoint mMapPosition
Map position (in case mMapPositionFixed is true)
void setHeight(qreal height)
QSizeF mFrameSize
Size of the frame (without balloon)
const QgsCoordinateReferenceSystem & destinationCrs() const
returns CRS of destination coordinate reference system
qreal width() const
double x() const
Get the x value of the point.
Definition: qgspoint.h:185
void setMapPositionFixed(bool fixed)
bool setRenderContextVariables(QPainter *p, QgsRenderContext &context) const
Sets render context parameters.
void drawFrame(QPainter *p) const
Draws the annotation frame to a destination painter.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.