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