QGIS API Documentation  2.0.1-Dufour
 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 
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 
250 QLineF QgsAnnotationItem::segment( int index )
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", QString::number( mMapPosition.x() ) );
390  annotationElem.setAttribute( "mapPosY", QString::number( mMapPosition.y() ) );
391  annotationElem.setAttribute( "offsetX", QString::number( mOffsetFromReferencePoint.x() ) );
392  annotationElem.setAttribute( "offsetY", QString::number( 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", QString::number( canvasPos.x() ) );
397  annotationElem.setAttribute( "canvasPosY", QString::number( 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 }