00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
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
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 = std::min( -halfSymbolSize, mOffsetFromReferencePoint.x() - mFrameBorderWidth );
00111 double xMaxPos = std::max( halfSymbolSize, mOffsetFromReferencePoint.x() + mFrameSize.width() + mFrameBorderWidth );
00112 double yMinPos = std::min( -halfSymbolSize, mOffsetFromReferencePoint.y() - mFrameBorderWidth );
00113 double yMaxPos = std::max( halfSymbolSize, mOffsetFromReferencePoint.y() + mFrameSize.height() + mFrameBorderWidth );
00114 mBoundingRect = QRectF( xMinPos, yMinPos, xMaxPos - xMinPos, yMaxPos - yMinPos );
00115 }
00116
00117 void QgsAnnotationItem::updateBalloon()
00118 {
00119
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
00129 QList<QLineF> segmentList;
00130 segmentList << segment( 0 ); segmentList << segment( 1 ); segmentList << segment( 2 ); segmentList << segment( 3 );
00131
00132
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
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 );
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 QPointF canvasPoint = toCanvasCoordinates( mMapPosition );
00220 if ( mMarkerSymbol )
00221 {
00222 mMarkerSymbol->startRender( renderContext );
00223 mMarkerSymbol->renderPoint( QPointF( 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
00236 if ( data( 0 ).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 ( abs( itemPos.x() ) < cursorSensitivity && abs( itemPos.y() ) < cursorSensitivity )
00287 {
00288 return MoveMapPosition;
00289 }
00290
00291 bool left, right, up, down;
00292 left = abs( itemPos.x() - mOffsetFromReferencePoint.x() ) < cursorSensitivity;
00293 right = abs( itemPos.x() - ( mOffsetFromReferencePoint.x() + mFrameSize.width() ) ) < cursorSensitivity;
00294 up = abs( itemPos.y() - mOffsetFromReferencePoint.y() ) < cursorSensitivity;
00295 down = abs( 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
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", mMapPosition.x() );
00390 annotationElem.setAttribute( "mapPosY", mMapPosition.y() );
00391 annotationElem.setAttribute( "offsetX", mOffsetFromReferencePoint.x() );
00392 annotationElem.setAttribute( "offsetY", mOffsetFromReferencePoint.y() );
00393 annotationElem.setAttribute( "frameWidth", mFrameSize.width() );
00394 annotationElem.setAttribute( "frameHeight", mFrameSize.height() );
00395 QPointF canvasPos = pos();
00396 annotationElem.setAttribute( "canvasPosX", canvasPos.x() );
00397 annotationElem.setAttribute( "canvasPosY", canvasPos.y() );
00398 annotationElem.setAttribute( "frameBorderWidth", 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 if ( annotationElem.isNull() )
00417 {
00418 return;
00419 }
00420 QPointF pos;
00421 pos.setX( annotationElem.attribute( "canvasPosX", "0" ).toDouble() );
00422 pos.setY( annotationElem.attribute( "canvasPosY", "0" ).toDouble() );
00423 setPos( pos );
00424 QgsPoint mapPos;
00425 mapPos.setX( annotationElem.attribute( "mapPosX", "0" ).toDouble() );
00426 mapPos.setY( annotationElem.attribute( "mapPosY", "0" ).toDouble() );
00427 mMapPosition = mapPos;
00428 mFrameBorderWidth = annotationElem.attribute( "frameBorderWidth", "0.5" ).toDouble();
00429 mFrameColor.setNamedColor( annotationElem.attribute( "frameColor", "#000000" ) );
00430 mFrameBackgroundColor.setNamedColor( annotationElem.attribute( "frameBackgroundColor" ) );
00431 mFrameBackgroundColor.setAlpha( annotationElem.attribute( "frameBackgroundColorAlpha", "255" ).toInt() );
00432 mFrameSize.setWidth( annotationElem.attribute( "frameWidth", "50" ).toDouble() );
00433 mFrameSize.setHeight( annotationElem.attribute( "frameHeight", "50" ).toDouble() );
00434 mOffsetFromReferencePoint.setX( annotationElem.attribute( "offsetX", "0" ).toDouble() );
00435 mOffsetFromReferencePoint.setY( annotationElem.attribute( "offsetY", "0" ).toDouble() );
00436 mMapPositionFixed = annotationElem.attribute( "mapPositionFixed", "1" ).toInt();
00437 setVisible( annotationElem.attribute( "visible", "1" ).toInt() );
00438
00439
00440 QDomElement symbolElem = annotationElem.firstChildElement( "symbol" );
00441 if ( !symbolElem.isNull() )
00442 {
00443 QgsMarkerSymbolV2* symbol = dynamic_cast<QgsMarkerSymbolV2*>( QgsSymbolLayerV2Utils::loadSymbol( symbolElem ) );
00444 if ( symbol )
00445 {
00446 delete mMarkerSymbol;
00447 mMarkerSymbol = symbol;
00448 }
00449 }
00450
00451 updateBoundingRect();
00452 updateBalloon();
00453 }