Quantum GIS API Documentation  1.7.4
src/gui/qgsannotationitem.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002                               qgsannotationitem.cpp
00003                               ----------------------
00004   begin                : February 9, 2010
00005   copyright            : (C) 2010 by Marco Hugentobler
00006   email                : marco dot hugentobler at hugis dot net
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00018 #include "qgsannotationitem.h"
00019 #include "qgsmapcanvas.h"
00020 #include "qgsrendercontext.h"
00021 #include "qgssymbollayerv2utils.h"
00022 #include "qgssymbolv2.h"
00023 #include <QPainter>
00024 #include <QPen>
00025 
00026 QgsAnnotationItem::QgsAnnotationItem( QgsMapCanvas* mapCanvas ): QgsMapCanvasItem( mapCanvas ), mMapPositionFixed( true ), mOffsetFromReferencePoint( QPointF( 50, -50 ) )
00027 {
00028   setFlag( QGraphicsItem::ItemIsSelectable, true );
00029   mMarkerSymbol = new QgsMarkerSymbolV2();
00030   mFrameBorderWidth = 1.0;
00031   mFrameColor = QColor( 0, 0, 0 );
00032   mFrameBackgroundColor = QColor( 255, 255, 255 );
00033 }
00034 
00035 QgsAnnotationItem::~QgsAnnotationItem()
00036 {
00037   delete mMarkerSymbol;
00038 }
00039 
00040 void QgsAnnotationItem::setMarkerSymbol( QgsMarkerSymbolV2* symbol )
00041 {
00042   delete mMarkerSymbol;
00043   mMarkerSymbol = symbol;
00044   updateBoundingRect();
00045 }
00046 
00047 void QgsAnnotationItem::setMapPosition( const QgsPoint& pos )
00048 {
00049   mMapPosition = pos;
00050   setPos( toCanvasCoordinates( mMapPosition ) );
00051 }
00052 
00053 void QgsAnnotationItem::setOffsetFromReferencePoint( const QPointF& offset )
00054 {
00055   mOffsetFromReferencePoint = offset;
00056   updateBoundingRect();
00057   updateBalloon();
00058 }
00059 
00060 void QgsAnnotationItem::setMapPositionFixed( bool fixed )
00061 {
00062   if ( mMapPositionFixed && !fixed )
00063   {
00064     //set map position to the top left corner of the balloon
00065     setMapPosition( toMapCoordinates( QPointF( pos() + mOffsetFromReferencePoint ).toPoint() ) );
00066     mOffsetFromReferencePoint = QPointF( 0, 0 );
00067   }
00068   else if ( fixed && !mMapPositionFixed )
00069   {
00070     setMapPosition( toMapCoordinates( QPointF( pos() + QPointF( -100, -100 ) ).toPoint() ) );
00071     mOffsetFromReferencePoint = QPointF( 100, 100 );
00072   }
00073   mMapPositionFixed = fixed;
00074   updateBoundingRect();
00075   updateBalloon();
00076   update();
00077 }
00078 
00079 void QgsAnnotationItem::updatePosition()
00080 {
00081   if ( mMapPositionFixed )
00082   {
00083     setPos( toCanvasCoordinates( mMapPosition ) );
00084   }
00085   else
00086   {
00087     mMapPosition = toMapCoordinates( pos().toPoint() );
00088   }
00089 }
00090 
00091 QRectF QgsAnnotationItem::boundingRect() const
00092 {
00093   return mBoundingRect;
00094 }
00095 
00096 QSizeF QgsAnnotationItem::minimumFrameSize() const
00097 {
00098   return QSizeF( 0, 0 );
00099 }
00100 
00101 void QgsAnnotationItem::updateBoundingRect()
00102 {
00103   prepareGeometryChange();
00104   double halfSymbolSize = 0.0;
00105   if ( mMarkerSymbol )
00106   {
00107     halfSymbolSize = scaledSymbolSize() / 2.0;
00108   }
00109 
00110   double xMinPos = qMin( -halfSymbolSize, mOffsetFromReferencePoint.x() - mFrameBorderWidth );
00111   double xMaxPos = qMax( halfSymbolSize, mOffsetFromReferencePoint.x() + mFrameSize.width() + mFrameBorderWidth );
00112   double yMinPos = qMin( -halfSymbolSize, mOffsetFromReferencePoint.y() - mFrameBorderWidth );
00113   double yMaxPos = qMax( halfSymbolSize, mOffsetFromReferencePoint.y() + mFrameSize.height() + mFrameBorderWidth );
00114   mBoundingRect = QRectF( xMinPos, yMinPos, xMaxPos - xMinPos, yMaxPos - yMinPos );
00115 }
00116 
00117 void QgsAnnotationItem::updateBalloon()
00118 {
00119   //first test if the point is in the frame. In that case we don't need a balloon.
00120   if ( !mMapPositionFixed ||
00121        ( mOffsetFromReferencePoint.x() < 0 && ( mOffsetFromReferencePoint.x() + mFrameSize.width() ) > 0 \
00122          && mOffsetFromReferencePoint.y() < 0 && ( mOffsetFromReferencePoint.y() + mFrameSize.height() ) > 0 ) )
00123   {
00124     mBalloonSegment = -1;
00125     return;
00126   }
00127 
00128   //edge list
00129   QList<QLineF> segmentList;
00130   segmentList << segment( 0 ); segmentList << segment( 1 ); segmentList << segment( 2 ); segmentList << segment( 3 );
00131 
00132   //find  closest edge / closest edge point
00133   double minEdgeDist = DBL_MAX;
00134   int minEdgeIndex = -1;
00135   QLineF minEdge;
00136   QgsPoint minEdgePoint;
00137   QgsPoint origin( 0, 0 );
00138 
00139   for ( int i = 0; i < 4; ++i )
00140   {
00141     QLineF currentSegment = segmentList.at( i );
00142     QgsPoint currentMinDistPoint;
00143     double currentMinDist = origin.sqrDistToSegment( currentSegment.x1(), currentSegment.y1(), currentSegment.x2(), currentSegment.y2(), currentMinDistPoint );
00144     if ( currentMinDist < minEdgeDist )
00145     {
00146       minEdgeIndex = i;
00147       minEdgePoint = currentMinDistPoint;
00148       minEdgeDist = currentMinDist;
00149       minEdge = currentSegment;
00150     }
00151   }
00152 
00153   if ( minEdgeIndex < 0 )
00154   {
00155     return;
00156   }
00157 
00158   //make that configurable for the item
00159   double segmentPointWidth = 10;
00160 
00161   mBalloonSegment = minEdgeIndex;
00162   QPointF minEdgeEnd = minEdge.p2();
00163   mBalloonSegmentPoint1 = QPointF( minEdgePoint.x(), minEdgePoint.y() );
00164   if ( sqrt( minEdgePoint.sqrDist( minEdgeEnd.x(), minEdgeEnd.y() ) ) < segmentPointWidth )
00165   {
00166     mBalloonSegmentPoint1 = pointOnLineWithDistance( minEdge.p2(), minEdge.p1(), segmentPointWidth );
00167   }
00168 
00169   mBalloonSegmentPoint2 = pointOnLineWithDistance( mBalloonSegmentPoint1, minEdge.p2(), 10 );
00170 }
00171 
00172 void QgsAnnotationItem::drawFrame( QPainter* p )
00173 {
00174   QPen framePen( mFrameColor );
00175   framePen.setWidthF( mFrameBorderWidth );
00176 
00177   p->setPen( framePen );
00178   QBrush frameBrush( mFrameBackgroundColor );
00179   p->setBrush( frameBrush );
00180   p->setRenderHint( QPainter::Antialiasing, true );
00181 
00182   QPolygonF poly;
00183   for ( int i = 0; i < 4; ++i )
00184   {
00185     QLineF currentSegment = segment( i );
00186     poly << currentSegment.p1();
00187     if ( i == mBalloonSegment && mMapPositionFixed )
00188     {
00189       poly << mBalloonSegmentPoint1;
00190       poly << QPointF( 0, 0 );
00191       poly << mBalloonSegmentPoint2;
00192     }
00193     poly << currentSegment.p2();
00194   }
00195   p->drawPolygon( poly );
00196 }
00197 
00198 void QgsAnnotationItem::setFrameSize( const QSizeF& size )
00199 {
00200   QSizeF frameSize = minimumFrameSize().expandedTo( size ); //don't allow frame sizes below minimum
00201   mFrameSize = frameSize;
00202   updateBoundingRect();
00203   updateBalloon();
00204 }
00205 
00206 void QgsAnnotationItem::drawMarkerSymbol( QPainter* p )
00207 {
00208   if ( !p )
00209   {
00210     return;
00211   }
00212 
00213   QgsRenderContext renderContext;
00214   if ( !setRenderContextVariables( p, renderContext ) )
00215   {
00216     return;
00217   }
00218 
00219   if ( mMarkerSymbol )
00220   {
00221     mMarkerSymbol->startRender( renderContext );
00222     mMarkerSymbol->renderPoint( QPointF( 0, 0 ), renderContext );
00223     mMarkerSymbol->stopRender( renderContext );
00224   }
00225 }
00226 
00227 void QgsAnnotationItem::drawSelectionBoxes( QPainter* p )
00228 {
00229   if ( !p )
00230   {
00231     return;
00232   }
00233 
00234   //no selection boxes for composer mode
00235   if ( data( 0 ).toString() == "composer" )
00236   {
00237     return;
00238   }
00239 
00240   double handlerSize = 10;
00241   p->setPen( Qt::NoPen );
00242   p->setBrush( QColor( 200, 200, 210, 120 ) );
00243   p->drawRect( QRectF( mBoundingRect.left(), mBoundingRect.top(), handlerSize, handlerSize ) );
00244   p->drawRect( QRectF( mBoundingRect.right() - handlerSize, mBoundingRect.top(), handlerSize, handlerSize ) );
00245   p->drawRect( QRectF( mBoundingRect.right() - handlerSize, mBoundingRect.bottom() - handlerSize, handlerSize, handlerSize ) );
00246   p->drawRect( QRectF( mBoundingRect.left(), mBoundingRect.bottom() - handlerSize, handlerSize, handlerSize ) );
00247 }
00248 
00249 QLineF QgsAnnotationItem::segment( int index )
00250 {
00251   switch ( index )
00252   {
00253     case 0:
00254       return QLineF( mOffsetFromReferencePoint.x(), mOffsetFromReferencePoint.y(), mOffsetFromReferencePoint.x() \
00255                      + mFrameSize.width(), mOffsetFromReferencePoint.y() );
00256     case 1:
00257       return QLineF( mOffsetFromReferencePoint.x() + mFrameSize.width(), mOffsetFromReferencePoint.y(), \
00258                      mOffsetFromReferencePoint.x() + mFrameSize.width(), mOffsetFromReferencePoint.y() + mFrameSize.height() );
00259     case 2:
00260       return QLineF( mOffsetFromReferencePoint.x() + mFrameSize.width(), mOffsetFromReferencePoint.y() + mFrameSize.height(), \
00261                      mOffsetFromReferencePoint.x(), mOffsetFromReferencePoint.y() + mFrameSize.height() );
00262     case 3:
00263       return QLineF( mOffsetFromReferencePoint.x(), mOffsetFromReferencePoint.y() + mFrameSize.height(), \
00264                      mOffsetFromReferencePoint.x(), mOffsetFromReferencePoint.y() );
00265     default:
00266       return QLineF();
00267   }
00268 }
00269 
00270 QPointF QgsAnnotationItem::pointOnLineWithDistance( const QPointF& startPoint, const QPointF& directionPoint, double distance ) const
00271 {
00272   double dx = directionPoint.x() - startPoint.x();
00273   double dy = directionPoint.y() - startPoint.y();
00274   double length = sqrt( dx * dx + dy * dy );
00275   double scaleFactor = distance / length;
00276   return QPointF( startPoint.x() + dx * scaleFactor, startPoint.y() + dy * scaleFactor );
00277 }
00278 
00279 QgsAnnotationItem::MouseMoveAction QgsAnnotationItem::moveActionForPosition( const QPointF& pos ) const
00280 {
00281   QPointF itemPos = mapFromScene( pos );
00282 
00283   int cursorSensitivity = 7;
00284 
00285   if ( qAbs( itemPos.x() ) < cursorSensitivity && qAbs( itemPos.y() ) < cursorSensitivity ) //move map point if position is close to the origin
00286   {
00287     return MoveMapPosition;
00288   }
00289 
00290   bool left, right, up, down;
00291   left = qAbs( itemPos.x() - mOffsetFromReferencePoint.x() ) < cursorSensitivity;
00292   right = qAbs( itemPos.x() - ( mOffsetFromReferencePoint.x() + mFrameSize.width() ) ) < cursorSensitivity;
00293   up = qAbs( itemPos.y() - mOffsetFromReferencePoint.y() ) < cursorSensitivity;
00294   down = qAbs( itemPos.y() - ( mOffsetFromReferencePoint.y() + mFrameSize.height() ) ) < cursorSensitivity;
00295 
00296   if ( left && up )
00297   {
00298     return ResizeFrameLeftUp;
00299   }
00300   else if ( right && up )
00301   {
00302     return ResizeFrameRightUp;
00303   }
00304   else if ( left && down )
00305   {
00306     return ResizeFrameLeftDown;
00307   }
00308   else if ( right && down )
00309   {
00310     return ResizeFrameRightDown;
00311   }
00312   if ( left )
00313   {
00314     return ResizeFrameLeft;
00315   }
00316   if ( right )
00317   {
00318     return ResizeFrameRight;
00319   }
00320   if ( up )
00321   {
00322     return ResizeFrameUp;
00323   }
00324   if ( down )
00325   {
00326     return ResizeFrameDown;
00327   }
00328 
00329   //finally test if pos is in the frame area
00330   if ( itemPos.x() >= mOffsetFromReferencePoint.x() && itemPos.x() <= ( mOffsetFromReferencePoint.x() + mFrameSize.width() ) \
00331        && itemPos.y() >= mOffsetFromReferencePoint.y() && itemPos.y() <= ( mOffsetFromReferencePoint.y() + mFrameSize.height() ) )
00332   {
00333     return MoveFramePosition;
00334   }
00335   return NoAction;
00336 }
00337 
00338 Qt::CursorShape QgsAnnotationItem::cursorShapeForAction( MouseMoveAction moveAction ) const
00339 {
00340   switch ( moveAction )
00341   {
00342     case NoAction:
00343       return Qt::ArrowCursor;
00344     case MoveMapPosition:
00345     case MoveFramePosition:
00346       return Qt::SizeAllCursor;
00347     case ResizeFrameUp:
00348     case ResizeFrameDown:
00349       return Qt::SizeVerCursor;
00350     case ResizeFrameLeft:
00351     case ResizeFrameRight:
00352       return Qt::SizeHorCursor;
00353     case ResizeFrameLeftUp:
00354     case ResizeFrameRightDown:
00355       return Qt::SizeFDiagCursor;
00356     case ResizeFrameRightUp:
00357     case ResizeFrameLeftDown:
00358       return Qt::SizeBDiagCursor;
00359     default:
00360       return Qt::ArrowCursor;
00361   }
00362 }
00363 
00364 double QgsAnnotationItem::scaledSymbolSize() const
00365 {
00366   if ( !mMarkerSymbol )
00367   {
00368     return 0.0;
00369   }
00370 
00371   if ( !mMapCanvas )
00372   {
00373     return mMarkerSymbol->size();
00374   }
00375 
00376   double dpmm = mMapCanvas->logicalDpiX() / 25.4;
00377   return dpmm * mMarkerSymbol->size();
00378 }
00379 
00380 void QgsAnnotationItem::_writeXML( QDomDocument& doc, QDomElement& itemElem ) const
00381 {
00382   if ( itemElem.isNull() )
00383   {
00384     return;
00385   }
00386   QDomElement annotationElem = doc.createElement( "AnnotationItem" );
00387   annotationElem.setAttribute( "mapPositionFixed", mMapPositionFixed );
00388   annotationElem.setAttribute( "mapPosX", mMapPosition.x() );
00389   annotationElem.setAttribute( "mapPosY", mMapPosition.y() );
00390   annotationElem.setAttribute( "offsetX", mOffsetFromReferencePoint.x() );
00391   annotationElem.setAttribute( "offsetY", mOffsetFromReferencePoint.y() );
00392   annotationElem.setAttribute( "frameWidth", mFrameSize.width() );
00393   annotationElem.setAttribute( "frameHeight", mFrameSize.height() );
00394   QPointF canvasPos = pos();
00395   annotationElem.setAttribute( "canvasPosX", canvasPos.x() );
00396   annotationElem.setAttribute( "canvasPosY", canvasPos.y() );
00397   annotationElem.setAttribute( "frameBorderWidth", mFrameBorderWidth );
00398   annotationElem.setAttribute( "frameColor", mFrameColor.name() );
00399   annotationElem.setAttribute( "frameBackgroundColor", mFrameBackgroundColor.name() );
00400   annotationElem.setAttribute( "frameBackgroundColorAlpha", mFrameBackgroundColor.alpha() );
00401   annotationElem.setAttribute( "visible", isVisible() );
00402   if ( mMarkerSymbol )
00403   {
00404     QDomElement symbolElem = QgsSymbolLayerV2Utils::saveSymbol( "marker symbol", mMarkerSymbol, doc );
00405     if ( !symbolElem.isNull() )
00406     {
00407       annotationElem.appendChild( symbolElem );
00408     }
00409   }
00410   itemElem.appendChild( annotationElem );
00411 }
00412 
00413 void QgsAnnotationItem::_readXML( const QDomDocument& doc, const QDomElement& annotationElem )
00414 {
00415   if ( annotationElem.isNull() )
00416   {
00417     return;
00418   }
00419   QPointF pos;
00420   pos.setX( annotationElem.attribute( "canvasPosX", "0" ).toDouble() );
00421   pos.setY( annotationElem.attribute( "canvasPosY", "0" ).toDouble() );
00422   setPos( pos );
00423   QgsPoint mapPos;
00424   mapPos.setX( annotationElem.attribute( "mapPosX", "0" ).toDouble() );
00425   mapPos.setY( annotationElem.attribute( "mapPosY", "0" ).toDouble() );
00426   mMapPosition = mapPos;
00427   mFrameBorderWidth = annotationElem.attribute( "frameBorderWidth", "0.5" ).toDouble();
00428   mFrameColor.setNamedColor( annotationElem.attribute( "frameColor", "#000000" ) );
00429   mFrameBackgroundColor.setNamedColor( annotationElem.attribute( "frameBackgroundColor" ) );
00430   mFrameBackgroundColor.setAlpha( annotationElem.attribute( "frameBackgroundColorAlpha", "255" ).toInt() );
00431   mFrameSize.setWidth( annotationElem.attribute( "frameWidth", "50" ).toDouble() );
00432   mFrameSize.setHeight( annotationElem.attribute( "frameHeight", "50" ).toDouble() );
00433   mOffsetFromReferencePoint.setX( annotationElem.attribute( "offsetX", "0" ).toDouble() );
00434   mOffsetFromReferencePoint.setY( annotationElem.attribute( "offsetY", "0" ).toDouble() );
00435   mMapPositionFixed = annotationElem.attribute( "mapPositionFixed", "1" ).toInt();
00436   setVisible( annotationElem.attribute( "visible", "1" ).toInt() );
00437 
00438   //marker symbol
00439   QDomElement symbolElem = annotationElem.firstChildElement( "symbol" );
00440   if ( !symbolElem.isNull() )
00441   {
00442     QgsMarkerSymbolV2* symbol = dynamic_cast<QgsMarkerSymbolV2*>( QgsSymbolLayerV2Utils::loadSymbol( symbolElem ) );
00443     if ( symbol )
00444     {
00445       delete mMarkerSymbol;
00446       mMarkerSymbol = symbol;
00447     }
00448   }
00449 
00450   updateBoundingRect();
00451   updateBalloon();
00452 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines