Quantum GIS API Documentation
1.7.4
|
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 }