QGIS API Documentation  2.12.0-Lyon
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  , mOffsetFromReferencePoint( QPointF( 50, -50 ) )
30  , mBalloonSegment( -1 )
31 {
32  setFlag( QGraphicsItem::ItemIsSelectable, true );
34  mFrameBorderWidth = 1.0;
35  mFrameColor = QColor( 0, 0, 0 );
36  mFrameBackgroundColor = QColor( 255, 255, 255 );
37  setData( 0, "AnnotationItem" );
38 }
39 
41 {
42  delete mMarkerSymbol;
43 }
44 
46 {
47  delete mMarkerSymbol;
48  mMarkerSymbol = symbol;
50 }
51 
53 {
54  mMapPosition = pos;
56 }
57 
59 {
62  updateBalloon();
63 }
64 
66 {
67  if ( mMapPositionFixed && !fixed )
68  {
69  //set map position to the top left corner of the balloon
72  }
73  else if ( fixed && !mMapPositionFixed )
74  {
75  setMapPosition( toMapCoordinates( QPointF( pos() + QPointF( -100, -100 ) ).toPoint() ) );
76  mOffsetFromReferencePoint = QPointF( 100, 100 );
77  }
78  mMapPositionFixed = fixed;
80  updateBalloon();
81  update();
82 }
83 
85 {
86  if ( mMapPositionFixed )
87  {
89  }
90  else
91  {
92  mMapPosition = toMapCoordinates( pos().toPoint() );
93  }
94 }
95 
97 {
98  return mBoundingRect;
99 }
100 
102 {
103  return QSizeF( 0, 0 );
104 }
105 
107 {
109  double halfSymbolSize = 0.0;
110  if ( mMarkerSymbol )
111  {
112  halfSymbolSize = scaledSymbolSize() / 2.0;
113  }
114 
115  double xMinPos = qMin( -halfSymbolSize, mOffsetFromReferencePoint.x() - mFrameBorderWidth );
116  double xMaxPos = qMax( halfSymbolSize, mOffsetFromReferencePoint.x() + mFrameSize.width() + mFrameBorderWidth );
117  double yMinPos = qMin( -halfSymbolSize, mOffsetFromReferencePoint.y() - mFrameBorderWidth );
118  double yMaxPos = qMax( halfSymbolSize, mOffsetFromReferencePoint.y() + mFrameSize.height() + mFrameBorderWidth );
119  mBoundingRect = QRectF( xMinPos, yMinPos, xMaxPos - xMinPos, yMaxPos - yMinPos );
120 }
121 
123 {
124  //first test if the point is in the frame. In that case we don't need a balloon.
125  if ( !mMapPositionFixed ||
128  {
129  mBalloonSegment = -1;
130  return;
131  }
132 
133  //edge list
134  QList<QLineF> segmentList;
135  segmentList << segment( 0 ); segmentList << segment( 1 ); segmentList << segment( 2 ); segmentList << segment( 3 );
136 
137  //find closest edge / closest edge point
138  double minEdgeDist = DBL_MAX;
139  int minEdgeIndex = -1;
140  QLineF minEdge;
141  QgsPoint minEdgePoint;
142  QgsPoint origin( 0, 0 );
143 
144  for ( int i = 0; i < 4; ++i )
145  {
146  QLineF currentSegment = segmentList.at( i );
147  QgsPoint currentMinDistPoint;
148  double currentMinDist = origin.sqrDistToSegment( currentSegment.x1(), currentSegment.y1(), currentSegment.x2(), currentSegment.y2(), currentMinDistPoint );
149  if ( currentMinDist < minEdgeDist )
150  {
151  minEdgeIndex = i;
152  minEdgePoint = currentMinDistPoint;
153  minEdgeDist = currentMinDist;
154  minEdge = currentSegment;
155  }
156  }
157 
158  if ( minEdgeIndex < 0 )
159  {
160  return;
161  }
162 
163  //make that configurable for the item
164  double segmentPointWidth = 10;
165 
166  mBalloonSegment = minEdgeIndex;
167  QPointF minEdgeEnd = minEdge.p2();
168  mBalloonSegmentPoint1 = QPointF( minEdgePoint.x(), minEdgePoint.y() );
169  if ( sqrt( minEdgePoint.sqrDist( minEdgeEnd.x(), minEdgeEnd.y() ) ) < segmentPointWidth )
170  {
171  mBalloonSegmentPoint1 = pointOnLineWithDistance( minEdge.p2(), minEdge.p1(), segmentPointWidth );
172  }
173 
175 }
176 
178 {
179  QPen framePen( mFrameColor );
180  framePen.setWidthF( mFrameBorderWidth );
181 
182  p->setPen( framePen );
183  QBrush frameBrush( mFrameBackgroundColor );
184  p->setBrush( frameBrush );
185  p->setRenderHint( QPainter::Antialiasing, true );
186 
187  QPolygonF poly;
188  for ( int i = 0; i < 4; ++i )
189  {
190  QLineF currentSegment = segment( i );
191  poly << currentSegment.p1();
192  if ( i == mBalloonSegment && mMapPositionFixed )
193  {
194  poly << mBalloonSegmentPoint1;
195  poly << QPointF( 0, 0 );
196  poly << mBalloonSegmentPoint2;
197  }
198  poly << currentSegment.p2();
199  }
200  p->drawPolygon( poly );
201 }
202 
204 {
205  QSizeF frameSize = minimumFrameSize().expandedTo( size ); //don't allow frame sizes below minimum
208  updateBalloon();
209 }
210 
212 {
213  if ( !p )
214  {
215  return;
216  }
217 
218  QgsRenderContext renderContext;
219  if ( !setRenderContextVariables( p, renderContext ) )
220  {
221  return;
222  }
223 
224  if ( mMarkerSymbol )
225  {
226  mMarkerSymbol->startRender( renderContext );
227  mMarkerSymbol->renderPoint( QPointF( 0, 0 ), 0, renderContext );
228  mMarkerSymbol->stopRender( renderContext );
229  }
230 }
231 
233 {
234  if ( !p )
235  {
236  return;
237  }
238 
239  //no selection boxes for composer mode
240  if ( data( 1 ).toString() == "composer" )
241  {
242  return;
243  }
244 
245  double handlerSize = 10;
246  p->setPen( Qt::NoPen );
247  p->setBrush( QColor( 200, 200, 210, 120 ) );
248  p->drawRect( QRectF( mBoundingRect.left(), mBoundingRect.top(), handlerSize, handlerSize ) );
249  p->drawRect( QRectF( mBoundingRect.right() - handlerSize, mBoundingRect.top(), handlerSize, handlerSize ) );
250  p->drawRect( QRectF( mBoundingRect.right() - handlerSize, mBoundingRect.bottom() - handlerSize, handlerSize, handlerSize ) );
251  p->drawRect( QRectF( mBoundingRect.left(), mBoundingRect.bottom() - handlerSize, handlerSize, handlerSize ) );
252 }
253 
255 {
256  switch ( index )
257  {
258  case 0:
261  case 1:
264  case 2:
267  case 3:
270  default:
271  return QLineF();
272  }
273 }
274 
275 QPointF QgsAnnotationItem::pointOnLineWithDistance( const QPointF& startPoint, const QPointF& directionPoint, double distance ) const
276 {
277  double dx = directionPoint.x() - startPoint.x();
278  double dy = directionPoint.y() - startPoint.y();
279  double length = sqrt( dx * dx + dy * dy );
280  double scaleFactor = distance / length;
281  return QPointF( startPoint.x() + dx * scaleFactor, startPoint.y() + dy * scaleFactor );
282 }
283 
285 {
286  QPointF itemPos = mapFromScene( pos );
287 
288  int cursorSensitivity = 7;
289 
290  if ( qAbs( itemPos.x() ) < cursorSensitivity && qAbs( itemPos.y() ) < cursorSensitivity ) //move map point if position is close to the origin
291  {
292  return MoveMapPosition;
293  }
294 
295  bool left, right, up, down;
296  left = qAbs( itemPos.x() - mOffsetFromReferencePoint.x() ) < cursorSensitivity;
297  right = qAbs( itemPos.x() - ( mOffsetFromReferencePoint.x() + mFrameSize.width() ) ) < cursorSensitivity;
298  up = qAbs( itemPos.y() - mOffsetFromReferencePoint.y() ) < cursorSensitivity;
299  down = qAbs( itemPos.y() - ( mOffsetFromReferencePoint.y() + mFrameSize.height() ) ) < cursorSensitivity;
300 
301  if ( left && up )
302  {
303  return ResizeFrameLeftUp;
304  }
305  else if ( right && up )
306  {
307  return ResizeFrameRightUp;
308  }
309  else if ( left && down )
310  {
311  return ResizeFrameLeftDown;
312  }
313  else if ( right && down )
314  {
315  return ResizeFrameRightDown;
316  }
317  if ( left )
318  {
319  return ResizeFrameLeft;
320  }
321  if ( right )
322  {
323  return ResizeFrameRight;
324  }
325  if ( up )
326  {
327  return ResizeFrameUp;
328  }
329  if ( down )
330  {
331  return ResizeFrameDown;
332  }
333 
334  //finally test if pos is in the frame area
335  if ( itemPos.x() >= mOffsetFromReferencePoint.x() && itemPos.x() <= ( mOffsetFromReferencePoint.x() + mFrameSize.width() )
336  && itemPos.y() >= mOffsetFromReferencePoint.y() && itemPos.y() <= ( mOffsetFromReferencePoint.y() + mFrameSize.height() ) )
337  {
338  return MoveFramePosition;
339  }
340  return NoAction;
341 }
342 
343 Qt::CursorShape QgsAnnotationItem::cursorShapeForAction( MouseMoveAction moveAction ) const
344 {
345  switch ( moveAction )
346  {
347  case NoAction:
348  return Qt::ArrowCursor;
349  case MoveMapPosition:
350  case MoveFramePosition:
351  return Qt::SizeAllCursor;
352  case ResizeFrameUp:
353  case ResizeFrameDown:
354  return Qt::SizeVerCursor;
355  case ResizeFrameLeft:
356  case ResizeFrameRight:
357  return Qt::SizeHorCursor;
358  case ResizeFrameLeftUp:
360  return Qt::SizeFDiagCursor;
361  case ResizeFrameRightUp:
362  case ResizeFrameLeftDown:
363  return Qt::SizeBDiagCursor;
364  default:
365  return Qt::ArrowCursor;
366  }
367 }
368 
370 {
371  if ( !mMarkerSymbol )
372  {
373  return 0.0;
374  }
375 
376  if ( !mMapCanvas )
377  {
378  return mMarkerSymbol->size();
379  }
380 
381  double dpmm = mMapCanvas->logicalDpiX() / 25.4;
382  return dpmm * mMarkerSymbol->size();
383 }
384 
386 {
387  if ( itemElem.isNull() )
388  {
389  return;
390  }
391  QDomElement annotationElem = doc.createElement( "AnnotationItem" );
392  annotationElem.setAttribute( "mapPositionFixed", mMapPositionFixed );
393  annotationElem.setAttribute( "mapPosX", qgsDoubleToString( mMapPosition.x() ) );
394  annotationElem.setAttribute( "mapPosY", qgsDoubleToString( mMapPosition.y() ) );
395  annotationElem.setAttribute( "offsetX", qgsDoubleToString( mOffsetFromReferencePoint.x() ) );
396  annotationElem.setAttribute( "offsetY", qgsDoubleToString( mOffsetFromReferencePoint.y() ) );
397  annotationElem.setAttribute( "frameWidth", QString::number( mFrameSize.width() ) );
398  annotationElem.setAttribute( "frameHeight", QString::number( mFrameSize.height() ) );
399  QPointF canvasPos = pos();
400  annotationElem.setAttribute( "canvasPosX", qgsDoubleToString( canvasPos.x() ) );
401  annotationElem.setAttribute( "canvasPosY", qgsDoubleToString( canvasPos.y() ) );
402  annotationElem.setAttribute( "frameBorderWidth", QString::number( mFrameBorderWidth ) );
403  annotationElem.setAttribute( "frameColor", mFrameColor.name() );
404  annotationElem.setAttribute( "frameColorAlpha", mFrameColor.alpha() );
405  annotationElem.setAttribute( "frameBackgroundColor", mFrameBackgroundColor.name() );
406  annotationElem.setAttribute( "frameBackgroundColorAlpha", mFrameBackgroundColor.alpha() );
407  annotationElem.setAttribute( "visible", isVisible() );
408  if ( mMarkerSymbol )
409  {
410  QDomElement symbolElem = QgsSymbolLayerV2Utils::saveSymbol( "marker symbol", mMarkerSymbol, doc );
411  if ( !symbolElem.isNull() )
412  {
413  annotationElem.appendChild( symbolElem );
414  }
415  }
416  itemElem.appendChild( annotationElem );
417 }
418 
419 void QgsAnnotationItem::_readXML( const QDomDocument& doc, const QDomElement& annotationElem )
420 {
421  Q_UNUSED( doc );
422  if ( annotationElem.isNull() )
423  {
424  return;
425  }
426  QPointF pos;
427  pos.setX( annotationElem.attribute( "canvasPosX", "0" ).toDouble() );
428  pos.setY( annotationElem.attribute( "canvasPosY", "0" ).toDouble() );
429  setPos( pos );
430  QgsPoint mapPos;
431  mapPos.setX( annotationElem.attribute( "mapPosX", "0" ).toDouble() );
432  mapPos.setY( annotationElem.attribute( "mapPosY", "0" ).toDouble() );
433  mMapPosition = mapPos;
434  mFrameBorderWidth = annotationElem.attribute( "frameBorderWidth", "0.5" ).toDouble();
435  mFrameColor.setNamedColor( annotationElem.attribute( "frameColor", "#000000" ) );
436  mFrameColor.setAlpha( annotationElem.attribute( "frameColorAlpha", "255" ).toInt() );
437  mFrameBackgroundColor.setNamedColor( annotationElem.attribute( "frameBackgroundColor" ) );
438  mFrameBackgroundColor.setAlpha( annotationElem.attribute( "frameBackgroundColorAlpha", "255" ).toInt() );
439  mFrameSize.setWidth( annotationElem.attribute( "frameWidth", "50" ).toDouble() );
440  mFrameSize.setHeight( annotationElem.attribute( "frameHeight", "50" ).toDouble() );
441  mOffsetFromReferencePoint.setX( annotationElem.attribute( "offsetX", "0" ).toDouble() );
442  mOffsetFromReferencePoint.setY( annotationElem.attribute( "offsetY", "0" ).toDouble() );
443  mMapPositionFixed = annotationElem.attribute( "mapPositionFixed", "1" ).toInt();
444  setVisible( annotationElem.attribute( "visible", "1" ).toInt() );
445 
446  //marker symbol
447  QDomElement symbolElem = annotationElem.firstChildElement( "symbol" );
448  if ( !symbolElem.isNull() )
449  {
450  QgsMarkerSymbolV2* symbol = QgsSymbolLayerV2Utils::loadSymbol<QgsMarkerSymbolV2>( symbolElem );
451  if ( symbol )
452  {
453  delete mMarkerSymbol;
454  mMarkerSymbol = symbol;
455  }
456  }
457 
459  updateBalloon();
460 }
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)
QgsPoint toMapCoordinates(const 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)
QgsAnnotationItem::MouseMoveAction moveActionForPosition(const QPointF &pos) const
Returns the mouse move behaviour for a given position.
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
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)
void drawPolygon(const QPointF *points, int pointCount, Qt::FillRule fillRule)
QSizeF expandedTo(const QSizeF &otherSize) const
void setAlpha(int alpha)
qreal top() const
void setNamedColor(const QString &name)
double toDouble(bool *ok) const
double sqrDist(double x, double y) const
Returns the squared distance between this point and x,y.
Definition: qgspoint.cpp:345
qreal left() const
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:107
void update(const QRectF &rect)
double x() const
Get the x value of the point.
Definition: qgspoint.h:126
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
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=0)
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
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:431
QPointF mapFromScene(const QPointF &point) const
QPointF pointOnLineWithDistance(const QPointF &startPoint, const QPointF &directionPoint, double distance) const
Returns a point on the line from startPoint to directionPoint that is a certain distance away from th...
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)
void renderPoint(const QPointF &point, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
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:63
void prepareGeometryChange()
int logicalDpiX() const
qreal right() const
void setX(double x)
Sets the x value of the point.
Definition: qgspoint.h:103
QString qgsDoubleToString(const double &a, const int &precision=17)
Definition: qgis.h:257
void setY(double y)
Sets the y value of the point.
Definition: qgspoint.h:111
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...
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 _writeXML(QDomDocument &doc, QDomElement &itemElem) const
void updateBalloon()
Check where to attach the balloon connection between frame and map point.
void setOffsetFromReferencePoint(const QPointF &offset)
QgsMapCanvas * mMapCanvas
pointer to map canvas
void setX(qreal x)
void setY(qreal y)
QDomElement firstChildElement(const QString &tagName) const
void setFrameSize(const QSizeF &size)
void drawFrame(QPainter *p)
QSizeF frameSize() const
void setVisible(bool visible)
double y() const
Get the y value of the point.
Definition: qgspoint.h:134
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
void setMapPositionFixed(bool fixed)
bool setRenderContextVariables(QPainter *p, QgsRenderContext &context) const
Sets render context parameters.
QLineF segment(int index)
Returns frame width in painter units.