35 props.insert( QStringLiteral(
"color" ), QStringLiteral(
"white" ) );
36 props.insert( QStringLiteral(
"style" ), QStringLiteral(
"solid" ) );
37 props.insert( QStringLiteral(
"style_border" ), QStringLiteral(
"solid" ) );
38 props.insert( QStringLiteral(
"color_border" ), QStringLiteral(
"black" ) );
39 props.insert( QStringLiteral(
"width_border" ), QStringLiteral(
"0.3" ) );
40 props.insert( QStringLiteral(
"joinstyle" ), QStringLiteral(
"miter" ) );
55 if ( mHasFixedMapPosition == fixed )
58 mHasFixedMapPosition = fixed;
65 mMapPosition = position;
71 mMapPositionCrs =
crs;
77 mRelativePosition = position;
89 return mOffsetFromReferencePoint / 3.7795275;
94 mOffsetFromReferencePoint = offset;
109 return mFrameSize / 3.7795275;
123 mContentsMargins = margins;
129 mFillSymbol.reset( symbol );
135 QPainter *painter = context.
painter();
144 drawFrame( context );
145 if ( mHasFixedMapPosition )
147 drawMarkerSymbol( context );
149 if ( mHasFixedMapPosition )
171 mMarkerSymbol.reset( symbol );
214 return QSizeF( 0, 0 );
217 void QgsAnnotation::updateBalloon()
220 if ( !mHasFixedMapPosition ||
221 ( mOffsetFromReferencePoint.x() < 0 && ( mOffsetFromReferencePoint.x() + mFrameSize.width() ) > 0
222 && mOffsetFromReferencePoint.y() < 0 && ( mOffsetFromReferencePoint.y() + mFrameSize.height() ) > 0 ) )
224 mBalloonSegment = -1;
229 QList<QLineF> segmentList;
230 segmentList << segment( 0,
nullptr );
231 segmentList << segment( 1,
nullptr );
232 segmentList << segment( 2,
nullptr );
233 segmentList << segment( 3,
nullptr );
236 double minEdgeDist = std::numeric_limits<double>::max();
237 int minEdgeIndex = -1;
242 for (
int i = 0; i < 4; ++i )
244 QLineF currentSegment = segmentList.at( i );
246 double currentMinDist = origin.
sqrDistToSegment( currentSegment.x1(), currentSegment.y1(), currentSegment.x2(), currentSegment.y2(), currentMinDistPoint );
247 bool isPreferredSegment =
false;
251 const double angle = fmod( origin.azimuth( currentMinDistPoint ) + 360.0, 360.0 );
252 if ( angle < 45 || angle > 315 )
253 isPreferredSegment = i == 0;
254 else if (
angle < 135 )
255 isPreferredSegment = i == 3;
256 else if (
angle < 225 )
257 isPreferredSegment = i == 2;
259 isPreferredSegment = i == 1;
261 else if ( currentMinDist < minEdgeDist )
262 isPreferredSegment =
true;
264 if ( isPreferredSegment )
267 minEdgePoint = currentMinDistPoint;
268 minEdgeDist = currentMinDist;
269 minEdge = currentSegment;
273 if ( minEdgeIndex < 0 )
278 mBalloonSegment = minEdgeIndex;
279 QPointF minEdgeEnd = minEdge.p2();
280 mBalloonSegmentPoint1 = QPointF( minEdgePoint.
x(), minEdgePoint.
y() );
281 if ( std::sqrt( minEdgePoint.
sqrDist( minEdgeEnd.x(), minEdgeEnd.y() ) ) < mSegmentPointWidthMm )
286 mBalloonSegmentPoint1 = QPointF( x, y );
293 mBalloonSegmentPoint2 = QPointF( x, y );
300 auto scaleSize = [context](
double size )->
double
304 if ( mHasFixedMapPosition )
309 return QLineF( scaleSize( mOffsetFromReferencePoint.x() ),
310 scaleSize( mOffsetFromReferencePoint.y() ),
311 scaleSize( mOffsetFromReferencePoint.x() ) + scaleSize( mFrameSize.width() ),
312 scaleSize( mOffsetFromReferencePoint.y() ) );
314 return QLineF( scaleSize( mOffsetFromReferencePoint.x() ) + scaleSize( mFrameSize.width() ),
315 scaleSize( mOffsetFromReferencePoint.y() ),
316 scaleSize( mOffsetFromReferencePoint.x() ) + scaleSize( mFrameSize.width() ),
317 scaleSize( mOffsetFromReferencePoint.y() ) + scaleSize( mFrameSize.height() ) );
319 return QLineF( scaleSize( mOffsetFromReferencePoint.x() ) + scaleSize( mFrameSize.width() ),
320 scaleSize( mOffsetFromReferencePoint.y() ) + scaleSize( mFrameSize.height() ),
321 scaleSize( mOffsetFromReferencePoint.x() ),
322 scaleSize( mOffsetFromReferencePoint.y() ) + scaleSize( mFrameSize.height() ) );
324 return QLineF( scaleSize( mOffsetFromReferencePoint.x() ),
325 scaleSize( mOffsetFromReferencePoint.y() ) + scaleSize( mFrameSize.height() ),
326 scaleSize( mOffsetFromReferencePoint.x() ),
327 scaleSize( mOffsetFromReferencePoint.y() ) );
337 return QLineF( 0, 0, scaleSize( mFrameSize.width() ), 0 );
339 return QLineF( scaleSize( mFrameSize.width() ), 0,
340 scaleSize( mFrameSize.width() ), scaleSize( mFrameSize.height() ) );
342 return QLineF( scaleSize( mFrameSize.width() ), scaleSize( mFrameSize.height() ),
343 0, scaleSize( mFrameSize.height() ) );
345 return QLineF( 0, scaleSize( mFrameSize.height() ),
359 poly.reserve( 9 + ( mHasFixedMapPosition ? 3 : 0 ) );
360 QVector<QPolygonF> rings;
361 for (
int i = 0; i < 4; ++i )
363 QLineF currentSegment = segment( i, &context );
364 poly << QPointF( currentSegment.p1().x(),
365 currentSegment.p1().y() );
366 if ( i == mBalloonSegment && mHasFixedMapPosition )
370 poly << QPointF( 0, 0 );
374 poly << QPointF( currentSegment.p2().x(), currentSegment.p2().y() );
376 if ( poly.at( 0 ) != poly.at( poly.count() - 1 ) )
377 poly << poly.at( 0 );
379 mFillSymbol->startRender( context );
380 mFillSymbol->renderPolygon( poly, &rings,
nullptr, context );
381 mFillSymbol->stopRender( context );
393 mMarkerSymbol->startRender( context );
394 mMarkerSymbol->renderPoint( QPointF( 0, 0 ),
nullptr, context );
395 mMarkerSymbol->stopRender( context );
401 if ( itemElem.isNull() )
405 QDomElement annotationElem = doc.createElement( QStringLiteral(
"AnnotationItem" ) );
406 annotationElem.setAttribute( QStringLiteral(
"mapPositionFixed" ), mHasFixedMapPosition );
407 annotationElem.setAttribute( QStringLiteral(
"mapPosX" ),
qgsDoubleToString( mMapPosition.
x() ) );
408 annotationElem.setAttribute( QStringLiteral(
"mapPosY" ),
qgsDoubleToString( mMapPosition.
y() ) );
409 if ( mMapPositionCrs.
isValid() )
410 mMapPositionCrs.
writeXml( annotationElem, doc );
411 annotationElem.setAttribute( QStringLiteral(
"offsetXMM" ),
qgsDoubleToString( mOffsetFromReferencePoint.x() ) );
412 annotationElem.setAttribute( QStringLiteral(
"offsetYMM" ),
qgsDoubleToString( mOffsetFromReferencePoint.y() ) );
413 annotationElem.setAttribute( QStringLiteral(
"frameWidthMM" ),
qgsDoubleToString( mFrameSize.width() ) );
414 annotationElem.setAttribute( QStringLiteral(
"frameHeightMM" ),
qgsDoubleToString( mFrameSize.height() ) );
415 annotationElem.setAttribute( QStringLiteral(
"canvasPosX" ),
qgsDoubleToString( mRelativePosition.x() ) );
416 annotationElem.setAttribute( QStringLiteral(
"canvasPosY" ),
qgsDoubleToString( mRelativePosition.y() ) );
417 annotationElem.setAttribute( QStringLiteral(
"contentsMargin" ), mContentsMargins.
toString() );
418 annotationElem.setAttribute( QStringLiteral(
"visible" ),
isVisible() );
421 annotationElem.setAttribute( QStringLiteral(
"mapLayer" ), mMapLayer->id() );
426 if ( !symbolElem.isNull() )
428 annotationElem.appendChild( symbolElem );
433 QDomElement fillElem = doc.createElement( QStringLiteral(
"fillSymbol" ) );
435 if ( !symbolElem.isNull() )
437 fillElem.appendChild( symbolElem );
438 annotationElem.appendChild( fillElem );
441 itemElem.appendChild( annotationElem );
446 if ( annotationElem.isNull() )
451 pos.setX( annotationElem.attribute( QStringLiteral(
"canvasPosX" ), QStringLiteral(
"0" ) ).toDouble() );
452 pos.setY( annotationElem.attribute( QStringLiteral(
"canvasPosY" ), QStringLiteral(
"0" ) ).toDouble() );
453 if ( pos.x() >= 1 || pos.x() < 0 || pos.y() < 0 || pos.y() >= 1 )
454 mRelativePosition = QPointF();
456 mRelativePosition = pos;
458 mapPos.
setX( annotationElem.attribute( QStringLiteral(
"mapPosX" ), QStringLiteral(
"0" ) ).toDouble() );
459 mapPos.
setY( annotationElem.attribute( QStringLiteral(
"mapPosY" ), QStringLiteral(
"0" ) ).toDouble() );
460 mMapPosition = mapPos;
462 if ( !mMapPositionCrs.
readXml( annotationElem ) )
467 mContentsMargins =
QgsMargins::fromString( annotationElem.attribute( QStringLiteral(
"contentsMargin" ) ) );
469 if ( annotationElem.hasAttribute( QStringLiteral(
"frameWidthMM" ) ) )
470 mFrameSize.setWidth( annotationElem.attribute( QStringLiteral(
"frameWidthMM" ), QStringLiteral(
"5" ) ).toDouble() );
472 mFrameSize.setWidth( dpiScale * annotationElem.attribute( QStringLiteral(
"frameWidth" ), QStringLiteral(
"50" ) ).toDouble() );
473 if ( annotationElem.hasAttribute( QStringLiteral(
"frameHeightMM" ) ) )
474 mFrameSize.setHeight( annotationElem.attribute( QStringLiteral(
"frameHeightMM" ), QStringLiteral(
"3" ) ).toDouble() );
476 mFrameSize.setHeight( dpiScale * annotationElem.attribute( QStringLiteral(
"frameHeight" ), QStringLiteral(
"50" ) ).toDouble() );
478 if ( annotationElem.hasAttribute( QStringLiteral(
"offsetXMM" ) ) )
479 mOffsetFromReferencePoint.setX( annotationElem.attribute( QStringLiteral(
"offsetXMM" ), QStringLiteral(
"0" ) ).toDouble() );
481 mOffsetFromReferencePoint.setX( dpiScale * annotationElem.attribute( QStringLiteral(
"offsetX" ), QStringLiteral(
"0" ) ).toDouble() );
482 if ( annotationElem.hasAttribute( QStringLiteral(
"offsetYMM" ) ) )
483 mOffsetFromReferencePoint.setY( annotationElem.attribute( QStringLiteral(
"offsetYMM" ), QStringLiteral(
"0" ) ).toDouble() );
485 mOffsetFromReferencePoint.setY( dpiScale * annotationElem.attribute( QStringLiteral(
"offsetY" ), QStringLiteral(
"0" ) ).toDouble() );
487 mHasFixedMapPosition = annotationElem.attribute( QStringLiteral(
"mapPositionFixed" ), QStringLiteral(
"1" ) ).toInt();
488 mVisible = annotationElem.attribute( QStringLiteral(
"visible" ), QStringLiteral(
"1" ) ).toInt();
489 if ( annotationElem.hasAttribute( QStringLiteral(
"mapLayer" ) ) )
496 QDomElement symbolElem = annotationElem.firstChildElement( QStringLiteral(
"symbol" ) );
497 if ( !symbolElem.isNull() )
499 QgsMarkerSymbol *symbol = QgsSymbolLayerUtils::loadSymbol<QgsMarkerSymbol>( symbolElem, context );
502 mMarkerSymbol.reset( symbol );
507 mFillSymbol.reset(
nullptr );
508 QDomElement fillElem = annotationElem.firstChildElement( QStringLiteral(
"fillSymbol" ) );
509 if ( !fillElem.isNull() )
511 QDomElement symbolElem = fillElem.firstChildElement( QStringLiteral(
"symbol" ) );
512 if ( !symbolElem.isNull() )
514 QgsFillSymbol *symbol = QgsSymbolLayerUtils::loadSymbol<QgsFillSymbol>( symbolElem, context );
517 mFillSymbol.reset( symbol );
524 frameColor.setNamedColor( annotationElem.attribute( QStringLiteral(
"frameColor" ), QStringLiteral(
"#000000" ) ) );
525 frameColor.setAlpha( annotationElem.attribute( QStringLiteral(
"frameColorAlpha" ), QStringLiteral(
"255" ) ).toInt() );
526 QColor frameBackgroundColor;
527 frameBackgroundColor.setNamedColor( annotationElem.attribute( QStringLiteral(
"frameBackgroundColor" ) ) );
528 frameBackgroundColor.setAlpha( annotationElem.attribute( QStringLiteral(
"frameBackgroundColorAlpha" ), QStringLiteral(
"255" ) ).toInt() );
529 double frameBorderWidth = annotationElem.attribute( QStringLiteral(
"frameBorderWidth" ), QStringLiteral(
"0.5" ) ).toDouble();
531 frameBorderWidth = frameBorderWidth * 25.4 / 96.0;
533 props.insert( QStringLiteral(
"color" ), frameBackgroundColor.name() );
534 props.insert( QStringLiteral(
"style" ), QStringLiteral(
"solid" ) );
535 props.insert( QStringLiteral(
"style_border" ), QStringLiteral(
"solid" ) );
536 props.insert( QStringLiteral(
"color_border" ), frameColor.name() );
537 props.insert( QStringLiteral(
"width_border" ), QString::number( frameBorderWidth ) );
538 props.insert( QStringLiteral(
"joinstyle" ), QStringLiteral(
"miter" ) );
548 target->mVisible = mVisible;
549 target->mHasFixedMapPosition = mHasFixedMapPosition;
550 target->mMapPosition = mMapPosition;
551 target->mMapPositionCrs = mMapPositionCrs;
552 target->mRelativePosition = mRelativePosition;
553 target->mOffsetFromReferencePoint = mOffsetFromReferencePoint;
554 target->mFrameSize = mFrameSize;
555 target->mMarkerSymbol.reset( mMarkerSymbol ? mMarkerSymbol->clone() :
nullptr );
556 target->mContentsMargins = mContentsMargins;
557 target->mFillSymbol.reset( mFillSymbol ? mFillSymbol->clone() :
nullptr );
558 target->mBalloonSegment = mBalloonSegment;
559 target->mBalloonSegmentPoint1 = mBalloonSegmentPoint1;
560 target->mBalloonSegmentPoint2 = mBalloonSegmentPoint2;
561 target->mSegmentPointWidthMm = mSegmentPointWidthMm;
562 target->mMapLayer = mMapLayer;
563 target->mFeature = mFeature;