QGIS API Documentation  2.8.2-Wien
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups 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 
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;
55  setPos( toCanvasCoordinates( mMapPosition ) );
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
70  setMapPosition( toMapCoordinates( QPointF( pos() + mOffsetFromReferencePoint ).toPoint() ) );
71  mOffsetFromReferencePoint = QPointF( 0, 0 );
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  {
88  setPos( toCanvasCoordinates( mMapPosition ) );
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 {
108  prepareGeometryChange();
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 ||
126  ( mOffsetFromReferencePoint.x() < 0 && ( mOffsetFromReferencePoint.x() + mFrameSize.width() ) > 0
127  && mOffsetFromReferencePoint.y() < 0 && ( mOffsetFromReferencePoint.y() + mFrameSize.height() ) > 0 ) )
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 
177 void QgsAnnotationItem::drawFrame( QPainter* p )
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:
260  + mFrameSize.width(), mOffsetFromReferencePoint.y() );
261  case 1:
262  return QLineF( mOffsetFromReferencePoint.x() + mFrameSize.width(), mOffsetFromReferencePoint.y(),
264  case 2:
265  return QLineF( mOffsetFromReferencePoint.x() + mFrameSize.width(), mOffsetFromReferencePoint.y() + mFrameSize.height(),
267  case 3:
268  return QLineF( mOffsetFromReferencePoint.x(), mOffsetFromReferencePoint.y() + mFrameSize.height(),
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 
385 void QgsAnnotationItem::_writeXML( QDomDocument& doc, QDomElement& itemElem ) const
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 }