QGIS API Documentation
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  mMapPositionCrs = crs;
63 }
64 
66 {
69  updateBalloon();
70 }
71 
73 {
74  if ( mMapPositionFixed && !fixed )
75  {
76  //set map position to the top left corner of the balloon
79  }
80  else if ( fixed && !mMapPositionFixed )
81  {
82  setMapPosition( toMapCoordinates( QPointF( pos() + QPointF( -100, -100 ) ).toPoint() ) );
83  mOffsetFromReferencePoint = QPointF( 100, 100 );
84  }
85  mMapPositionFixed = fixed;
87  updateBalloon();
88  update();
89 }
90 
92 {
93  if ( mMapPositionFixed )
94  {
96  setPos( toCanvasCoordinates( t.transform( mMapPosition ) ) );
97  }
98  else
99  {
100  mMapPosition = toMapCoordinates( pos().toPoint() );
101  }
102 }
103 
105 {
106  return mBoundingRect;
107 }
108 
110 {
111  return QSizeF( 0, 0 );
112 }
113 
115 {
117  double halfSymbolSize = 0.0;
118  if ( mMarkerSymbol )
119  {
120  halfSymbolSize = scaledSymbolSize() / 2.0;
121  }
122 
123  double xMinPos = qMin( -halfSymbolSize, mOffsetFromReferencePoint.x() - mFrameBorderWidth );
124  double xMaxPos = qMax( halfSymbolSize, mOffsetFromReferencePoint.x() + mFrameSize.width() + mFrameBorderWidth );
125  double yMinPos = qMin( -halfSymbolSize, mOffsetFromReferencePoint.y() - mFrameBorderWidth );
126  double yMaxPos = qMax( halfSymbolSize, mOffsetFromReferencePoint.y() + mFrameSize.height() + mFrameBorderWidth );
127  mBoundingRect = QRectF( xMinPos, yMinPos, xMaxPos - xMinPos, yMaxPos - yMinPos );
128 }
129 
131 {
132  //first test if the point is in the frame. In that case we don't need a balloon.
133  if ( !mMapPositionFixed ||
136  {
137  mBalloonSegment = -1;
138  return;
139  }
140 
141  //edge list
142  QList<QLineF> segmentList;
143  segmentList << segment( 0 );
144  segmentList << segment( 1 );
145  segmentList << segment( 2 );
146  segmentList << segment( 3 );
147 
148  //find closest edge / closest edge point
149  double minEdgeDist = DBL_MAX;
150  int minEdgeIndex = -1;
151  QLineF minEdge;
152  QgsPoint minEdgePoint;
153  QgsPoint origin( 0, 0 );
154 
155  for ( int i = 0; i < 4; ++i )
156  {
157  QLineF currentSegment = segmentList.at( i );
158  QgsPoint currentMinDistPoint;
159  double currentMinDist = origin.sqrDistToSegment( currentSegment.x1(), currentSegment.y1(), currentSegment.x2(), currentSegment.y2(), currentMinDistPoint );
160  if ( currentMinDist < minEdgeDist )
161  {
162  minEdgeIndex = i;
163  minEdgePoint = currentMinDistPoint;
164  minEdgeDist = currentMinDist;
165  minEdge = currentSegment;
166  }
167  }
168 
169  if ( minEdgeIndex < 0 )
170  {
171  return;
172  }
173 
174  //make that configurable for the item
175  double segmentPointWidth = 10;
176 
177  mBalloonSegment = minEdgeIndex;
178  QPointF minEdgeEnd = minEdge.p2();
179  mBalloonSegmentPoint1 = QPointF( minEdgePoint.x(), minEdgePoint.y() );
180  if ( sqrt( minEdgePoint.sqrDist( minEdgeEnd.x(), minEdgeEnd.y() ) ) < segmentPointWidth )
181  {
182  mBalloonSegmentPoint1 = pointOnLineWithDistance( minEdge.p2(), minEdge.p1(), segmentPointWidth );
183  }
184 
186 }
187 
189 {
190  QPen framePen( mFrameColor );
191  framePen.setWidthF( mFrameBorderWidth );
192 
193  p->setPen( framePen );
194  QBrush frameBrush( mFrameBackgroundColor );
195  p->setBrush( frameBrush );
196  p->setRenderHint( QPainter::Antialiasing, true );
197 
198  QPolygonF poly;
199  for ( int i = 0; i < 4; ++i )
200  {
201  QLineF currentSegment = segment( i );
202  poly << currentSegment.p1();
203  if ( i == mBalloonSegment && mMapPositionFixed )
204  {
205  poly << mBalloonSegmentPoint1;
206  poly << QPointF( 0, 0 );
207  poly << mBalloonSegmentPoint2;
208  }
209  poly << currentSegment.p2();
210  }
211  p->drawPolygon( poly );
212 }
213 
215 {
216  QSizeF frameSize = minimumFrameSize().expandedTo( size ); //don't allow frame sizes below minimum
219  updateBalloon();
220 }
221 
223 {
224  if ( !p )
225  {
226  return;
227  }
228 
229  QgsRenderContext renderContext;
230  if ( !setRenderContextVariables( p, renderContext ) )
231  {
232  return;
233  }
234 
235  if ( mMarkerSymbol )
236  {
237  mMarkerSymbol->startRender( renderContext );
238  mMarkerSymbol->renderPoint( QPointF( 0, 0 ), nullptr, renderContext );
239  mMarkerSymbol->stopRender( renderContext );
240  }
241 }
242 
244 {
245  if ( !p )
246  {
247  return;
248  }
249 
250  //no selection boxes for composer mode
251  if ( data( 1 ).toString() == "composer" )
252  {
253  return;
254  }
255 
256  double handlerSize = 10;
257  p->setPen( Qt::NoPen );
258  p->setBrush( QColor( 200, 200, 210, 120 ) );
259  p->drawRect( QRectF( mBoundingRect.left(), mBoundingRect.top(), handlerSize, handlerSize ) );
260  p->drawRect( QRectF( mBoundingRect.right() - handlerSize, mBoundingRect.top(), handlerSize, handlerSize ) );
261  p->drawRect( QRectF( mBoundingRect.right() - handlerSize, mBoundingRect.bottom() - handlerSize, handlerSize, handlerSize ) );
262  p->drawRect( QRectF( mBoundingRect.left(), mBoundingRect.bottom() - handlerSize, handlerSize, handlerSize ) );
263 }
264 
266 {
267  switch ( index )
268  {
269  case 0:
272  case 1:
275  case 2:
278  case 3:
281  default:
282  return QLineF();
283  }
284 }
285 
286 QPointF QgsAnnotationItem::pointOnLineWithDistance( QPointF startPoint, QPointF directionPoint, double distance ) const
287 {
288  double dx = directionPoint.x() - startPoint.x();
289  double dy = directionPoint.y() - startPoint.y();
290  double length = sqrt( dx * dx + dy * dy );
291  double scaleFactor = distance / length;
292  return QPointF( startPoint.x() + dx * scaleFactor, startPoint.y() + dy * scaleFactor );
293 }
294 
296 {
297  QPointF itemPos = mapFromScene( pos );
298 
299  int cursorSensitivity = 7;
300 
301  if ( qAbs( itemPos.x() ) < cursorSensitivity && qAbs( itemPos.y() ) < cursorSensitivity ) //move map point if position is close to the origin
302  {
303  return MoveMapPosition;
304  }
305 
306  bool left, right, up, down;
307  left = qAbs( itemPos.x() - mOffsetFromReferencePoint.x() ) < cursorSensitivity;
308  right = qAbs( itemPos.x() - ( mOffsetFromReferencePoint.x() + mFrameSize.width() ) ) < cursorSensitivity;
309  up = qAbs( itemPos.y() - mOffsetFromReferencePoint.y() ) < cursorSensitivity;
310  down = qAbs( itemPos.y() - ( mOffsetFromReferencePoint.y() + mFrameSize.height() ) ) < cursorSensitivity;
311 
312  if ( left && up )
313  {
314  return ResizeFrameLeftUp;
315  }
316  else if ( right && up )
317  {
318  return ResizeFrameRightUp;
319  }
320  else if ( left && down )
321  {
322  return ResizeFrameLeftDown;
323  }
324  else if ( right && down )
325  {
326  return ResizeFrameRightDown;
327  }
328  if ( left )
329  {
330  return ResizeFrameLeft;
331  }
332  if ( right )
333  {
334  return ResizeFrameRight;
335  }
336  if ( up )
337  {
338  return ResizeFrameUp;
339  }
340  if ( down )
341  {
342  return ResizeFrameDown;
343  }
344 
345  //finally test if pos is in the frame area
346  if ( itemPos.x() >= mOffsetFromReferencePoint.x() && itemPos.x() <= ( mOffsetFromReferencePoint.x() + mFrameSize.width() )
347  && itemPos.y() >= mOffsetFromReferencePoint.y() && itemPos.y() <= ( mOffsetFromReferencePoint.y() + mFrameSize.height() ) )
348  {
349  return MoveFramePosition;
350  }
351  return NoAction;
352 }
353 
354 Qt::CursorShape QgsAnnotationItem::cursorShapeForAction( MouseMoveAction moveAction ) const
355 {
356  switch ( moveAction )
357  {
358  case NoAction:
359  return Qt::ArrowCursor;
360  case MoveMapPosition:
361  case MoveFramePosition:
362  return Qt::SizeAllCursor;
363  case ResizeFrameUp:
364  case ResizeFrameDown:
365  return Qt::SizeVerCursor;
366  case ResizeFrameLeft:
367  case ResizeFrameRight:
368  return Qt::SizeHorCursor;
369  case ResizeFrameLeftUp:
371  return Qt::SizeFDiagCursor;
372  case ResizeFrameRightUp:
373  case ResizeFrameLeftDown:
374  return Qt::SizeBDiagCursor;
375  default:
376  return Qt::ArrowCursor;
377  }
378 }
379 
381 {
382  if ( !mMarkerSymbol )
383  {
384  return 0.0;
385  }
386 
387  if ( !mMapCanvas )
388  {
389  return mMarkerSymbol->size();
390  }
391 
392  double dpmm = mMapCanvas->logicalDpiX() / 25.4;
393  return dpmm * mMarkerSymbol->size();
394 }
395 
397 {
398  if ( itemElem.isNull() )
399  {
400  return;
401  }
402  QDomElement annotationElem = doc.createElement( "AnnotationItem" );
403  annotationElem.setAttribute( "mapPositionFixed", mMapPositionFixed );
404  annotationElem.setAttribute( "mapPosX", qgsDoubleToString( mMapPosition.x() ) );
405  annotationElem.setAttribute( "mapPosY", qgsDoubleToString( mMapPosition.y() ) );
406  if ( mMapPositionCrs.isValid() )
407  mMapPositionCrs.writeXML( annotationElem, doc );
408  annotationElem.setAttribute( "offsetX", qgsDoubleToString( mOffsetFromReferencePoint.x() ) );
409  annotationElem.setAttribute( "offsetY", qgsDoubleToString( mOffsetFromReferencePoint.y() ) );
410  annotationElem.setAttribute( "frameWidth", QString::number( mFrameSize.width() ) );
411  annotationElem.setAttribute( "frameHeight", QString::number( mFrameSize.height() ) );
412  QPointF canvasPos = pos();
413  annotationElem.setAttribute( "canvasPosX", qgsDoubleToString( canvasPos.x() ) );
414  annotationElem.setAttribute( "canvasPosY", qgsDoubleToString( canvasPos.y() ) );
415  annotationElem.setAttribute( "frameBorderWidth", QString::number( mFrameBorderWidth ) );
416  annotationElem.setAttribute( "frameColor", mFrameColor.name() );
417  annotationElem.setAttribute( "frameColorAlpha", mFrameColor.alpha() );
418  annotationElem.setAttribute( "frameBackgroundColor", mFrameBackgroundColor.name() );
419  annotationElem.setAttribute( "frameBackgroundColorAlpha", mFrameBackgroundColor.alpha() );
420  annotationElem.setAttribute( "visible", isVisible() );
421  if ( mMarkerSymbol )
422  {
423  QDomElement symbolElem = QgsSymbolLayerV2Utils::saveSymbol( "marker symbol", mMarkerSymbol, doc );
424  if ( !symbolElem.isNull() )
425  {
426  annotationElem.appendChild( symbolElem );
427  }
428  }
429  itemElem.appendChild( annotationElem );
430 }
431 
432 void QgsAnnotationItem::_readXML( const QDomDocument& doc, const QDomElement& annotationElem )
433 {
434  Q_UNUSED( doc );
435  if ( annotationElem.isNull() )
436  {
437  return;
438  }
439  QPointF pos;
440  pos.setX( annotationElem.attribute( "canvasPosX", "0" ).toDouble() );
441  pos.setY( annotationElem.attribute( "canvasPosY", "0" ).toDouble() );
442  setPos( pos );
443  QgsPoint mapPos;
444  mapPos.setX( annotationElem.attribute( "mapPosX", "0" ).toDouble() );
445  mapPos.setY( annotationElem.attribute( "mapPosY", "0" ).toDouble() );
446  mMapPosition = mapPos;
447  if ( !mMapPositionCrs.readXML( annotationElem ) )
449  mFrameBorderWidth = annotationElem.attribute( "frameBorderWidth", "0.5" ).toDouble();
450  mFrameColor.setNamedColor( annotationElem.attribute( "frameColor", "#000000" ) );
451  mFrameColor.setAlpha( annotationElem.attribute( "frameColorAlpha", "255" ).toInt() );
452  mFrameBackgroundColor.setNamedColor( annotationElem.attribute( "frameBackgroundColor" ) );
453  mFrameBackgroundColor.setAlpha( annotationElem.attribute( "frameBackgroundColorAlpha", "255" ).toInt() );
454  mFrameSize.setWidth( annotationElem.attribute( "frameWidth", "50" ).toDouble() );
455  mFrameSize.setHeight( annotationElem.attribute( "frameHeight", "50" ).toDouble() );
456  mOffsetFromReferencePoint.setX( annotationElem.attribute( "offsetX", "0" ).toDouble() );
457  mOffsetFromReferencePoint.setY( annotationElem.attribute( "offsetY", "0" ).toDouble() );
458  mMapPositionFixed = annotationElem.attribute( "mapPositionFixed", "1" ).toInt();
459  setVisible( annotationElem.attribute( "visible", "1" ).toInt() );
460 
461  //marker symbol
462  QDomElement symbolElem = annotationElem.firstChildElement( "symbol" );
463  if ( !symbolElem.isNull() )
464  {
465  QgsMarkerSymbolV2* symbol = QgsSymbolLayerV2Utils::loadSymbol<QgsMarkerSymbolV2>( symbolElem );
466  if ( symbol )
467  {
468  delete mMarkerSymbol;
469  mMarkerSymbol = symbol;
470  }
471  }
472 
474  updateBalloon();
475 }
virtual QSizeF minimumFrameSize() const
static unsigned index
QgsMarkerSymbolV2 * mMarkerSymbol
Point symbol that is to be drawn at the map reference location.
void setRenderHint(RenderHint hint, bool on)
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
QPointF toCanvasCoordinates(const QgsPoint &point) const
transformation from map coordinates to screen coordinates
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.
double size() const
Returns the size for the whole symbol, which is the maximum size of all marker symbol layers in the s...
const T & at(int i) const
An abstract class for items that can be placed on the map canvas.
static QDomElement saveSymbol(const QString &symbolName, QgsSymbolV2 *symbol, QDomDocument &doc)
const QgsMapSettings & mapSettings() const
Get access to properties used for map rendering.
void drawPolygon(const QPointF *points, int pointCount, Qt::FillRule fillRule)
QSizeF expandedTo(const QSizeF &otherSize) const
void setAlpha(int alpha)
qreal top() const
virtual void setMapPositionCrs(const QgsCoordinateReferenceSystem &crs)
Sets the CRS of the map position.
void setNamedColor(const QString &name)
double toDouble(bool *ok) const
double sqrDist(double x, double y) const
Returns the squared distance between this point a specified x, y coordinate.
Definition: qgspoint.cpp:353
qreal left() const
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:105
void update(const QRectF &rect)
double x() const
Get the x value of the point.
Definition: qgspoint.h:180
void setFrameSize(QSizeF size)
void drawSelectionBoxes(QPainter *p)
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)
qreal bottom() const
void drawRect(const QRectF &rectangle)
qreal y1() const
qreal y2() const
QPointF pos() const
QgsAnnotationItem::MouseMoveAction moveActionForPosition(QPointF pos) const
Returns the mouse move behaviour for a given position.
qreal x1() const
qreal x2() const
QString number(int n, int base)
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)
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)
const QgsCoordinateReferenceSystem & destinationCrs() const
returns CRS of destination coordinate reference system
int toInt(bool *ok, int base) const
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:336
QgsAnnotationItem(QgsMapCanvas *mapCanvas)
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
QPointF mapFromScene(const QPointF &point) const
void setWidthF(qreal width)
void setBrush(const QBrush &brush)
Qt::CursorShape cursorShapeForAction(MouseMoveAction moveAction) const
Returns suitable cursor shape for mouse move action.
void drawMarkerSymbol(QPainter *p)
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:157
void setOffsetFromReferencePoint(QPointF offset)
QgsCoordinateReferenceSystem mMapPositionCrs
CRS of the map position.
void setY(double y)
Sets the y value of the point.
Definition: qgspoint.h:165
bool isNull() const
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...
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QColor mFrameColor
Frame / balloon color.
bool isVisible() const
QVariant data(int key) const
bool writeXML(QDomNode &theNode, QDomDocument &theDoc) const
Stores state to the given Dom node in the given document.
Contains information about the context of a rendering operation.
void stopRender(QgsRenderContext &context)
void _writeXML(QDomDocument &doc, QDomElement &itemElem) const
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)
void drawFrame(QPainter *p)
QSizeF frameSize() const
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)
double y() const
Get the y value of the point.
Definition: qgspoint.h:188
QPointF mBalloonSegmentPoint2
Second segment point for drawing the balloon connection (ccw direction)
QDomElement createElement(const QString &tagName)
qreal height() const
QgsPoint mMapPosition
Map position (in case mMapPositionFixed is true)
void setHeight(qreal height)
QSizeF mFrameSize
Size of the frame (without balloon)
qreal width() const
QgsPoint toMapCoordinates(QPoint point) const
transformation from screen coordinates to map coordinates
void setMapPositionFixed(bool fixed)
bool setRenderContextVariables(QPainter *p, QgsRenderContext &context) const
Sets render context parameters.
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...
QLineF segment(int index)
Returns frame width in painter units.