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