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