mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-15 00:07:25 -05:00
Rework how callouts work with picture item with fixed sizes
When a picture annotation item is set to the fixed size mode, and has a callout anchor set, always place the annotation itself at a fixed offset from the callout anchor. This mimics the behavior of the old SVG annotation decoration.
This commit is contained in:
parent
deb506b5db
commit
e06b9f243e
@ -323,6 +323,58 @@ The callout ``anchor`` geometry must be specified in the parent layer's coordina
|
||||
|
||||
.. seealso:: :py:func:`calloutAnchor`
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
QSizeF offsetFromCallout() const;
|
||||
%Docstring
|
||||
Returns the (optional) offset of the annotation item from the :py:func:`~QgsAnnotationItem.calloutAnchor`.
|
||||
|
||||
Some annotation item subclasses support placement relative to the callout anchor. For these
|
||||
items, the offset from callout defines how far (in screen/page units) the item should be
|
||||
placed from the anchor point.
|
||||
|
||||
Units are defined by :py:func:`~QgsAnnotationItem.offsetFromCalloutUnit`
|
||||
|
||||
.. seealso:: :py:func:`setOffsetFromCallout`
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
void setOffsetFromCallout( const QSizeF &offset );
|
||||
%Docstring
|
||||
Sets the offset of the annotation item from the :py:func:`~QgsAnnotationItem.calloutAnchor`.
|
||||
|
||||
Some annotation item subclasses support placement relative to the callout anchor. For these
|
||||
items, the offset from callout defines how far (in screen/page units) the item should be
|
||||
placed from the anchor point.
|
||||
|
||||
Units are defined by :py:func:`~QgsAnnotationItem.offsetFromCalloutUnit`
|
||||
|
||||
.. seealso:: :py:func:`offsetFromCallout`
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
Qgis::RenderUnit offsetFromCalloutUnit() const;
|
||||
%Docstring
|
||||
Returns the units for the :py:func:`~QgsAnnotationItem.offsetFromCallout`.
|
||||
|
||||
.. seealso:: :py:func:`offsetFromCallout`
|
||||
|
||||
.. seealso:: :py:func:`setOffsetFromCalloutUnit`
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
void setOffsetFromCalloutUnit( Qgis::RenderUnit unit );
|
||||
%Docstring
|
||||
Sets the ``unit`` for the :py:func:`~QgsAnnotationItem.offsetFromCallout`.
|
||||
|
||||
.. seealso:: :py:func:`setOffsetFromCallout`
|
||||
|
||||
.. seealso:: :py:func:`offsetFromCalloutUnit`
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
|
||||
@ -323,6 +323,58 @@ The callout ``anchor`` geometry must be specified in the parent layer's coordina
|
||||
|
||||
.. seealso:: :py:func:`calloutAnchor`
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
QSizeF offsetFromCallout() const;
|
||||
%Docstring
|
||||
Returns the (optional) offset of the annotation item from the :py:func:`~QgsAnnotationItem.calloutAnchor`.
|
||||
|
||||
Some annotation item subclasses support placement relative to the callout anchor. For these
|
||||
items, the offset from callout defines how far (in screen/page units) the item should be
|
||||
placed from the anchor point.
|
||||
|
||||
Units are defined by :py:func:`~QgsAnnotationItem.offsetFromCalloutUnit`
|
||||
|
||||
.. seealso:: :py:func:`setOffsetFromCallout`
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
void setOffsetFromCallout( const QSizeF &offset );
|
||||
%Docstring
|
||||
Sets the offset of the annotation item from the :py:func:`~QgsAnnotationItem.calloutAnchor`.
|
||||
|
||||
Some annotation item subclasses support placement relative to the callout anchor. For these
|
||||
items, the offset from callout defines how far (in screen/page units) the item should be
|
||||
placed from the anchor point.
|
||||
|
||||
Units are defined by :py:func:`~QgsAnnotationItem.offsetFromCalloutUnit`
|
||||
|
||||
.. seealso:: :py:func:`offsetFromCallout`
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
Qgis::RenderUnit offsetFromCalloutUnit() const;
|
||||
%Docstring
|
||||
Returns the units for the :py:func:`~QgsAnnotationItem.offsetFromCallout`.
|
||||
|
||||
.. seealso:: :py:func:`offsetFromCallout`
|
||||
|
||||
.. seealso:: :py:func:`setOffsetFromCalloutUnit`
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
void setOffsetFromCalloutUnit( Qgis::RenderUnit unit );
|
||||
%Docstring
|
||||
Sets the ``unit`` for the :py:func:`~QgsAnnotationItem.offsetFromCallout`.
|
||||
|
||||
.. seealso:: :py:func:`setOffsetFromCallout`
|
||||
|
||||
.. seealso:: :py:func:`offsetFromCalloutUnit`
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
|
||||
@ -20,6 +20,8 @@
|
||||
#include "qgscalloutsregistry.h"
|
||||
#include "qgsapplication.h"
|
||||
#include "qgsrendercontext.h"
|
||||
#include "qgssymbollayerutils.h"
|
||||
#include "qgsunittypes.h"
|
||||
|
||||
QgsAnnotationItem::QgsAnnotationItem() = default;
|
||||
|
||||
@ -97,6 +99,8 @@ void QgsAnnotationItem::copyCommonProperties( const QgsAnnotationItem *other )
|
||||
else
|
||||
setCallout( nullptr );
|
||||
setCalloutAnchor( other->calloutAnchor() );
|
||||
setOffsetFromCallout( other->offsetFromCallout() );
|
||||
setOffsetFromCalloutUnit( other->offsetFromCalloutUnit() );
|
||||
}
|
||||
|
||||
bool QgsAnnotationItem::writeCommonProperties( QDomElement &element, QDomDocument &doc, const QgsReadWriteContext &context ) const
|
||||
@ -115,6 +119,11 @@ bool QgsAnnotationItem::writeCommonProperties( QDomElement &element, QDomDocumen
|
||||
{
|
||||
element.setAttribute( QStringLiteral( "calloutAnchor" ), mCalloutAnchor.asWkt() );
|
||||
}
|
||||
if ( mOffsetFromCallout.isValid() )
|
||||
{
|
||||
element.setAttribute( QStringLiteral( "offsetFromCallout" ), QgsSymbolLayerUtils::encodeSize( mOffsetFromCallout ) );
|
||||
element.setAttribute( QStringLiteral( "offsetFromCalloutUnit" ), QgsUnitTypes::encodeUnit( mOffsetFromCalloutUnit ) );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -140,6 +149,12 @@ bool QgsAnnotationItem::readCommonProperties( const QDomElement &element, const
|
||||
const QString calloutAnchorWkt = element.attribute( QStringLiteral( "calloutAnchor" ) );
|
||||
setCalloutAnchor( calloutAnchorWkt.isEmpty() ? QgsGeometry() : QgsGeometry::fromWkt( calloutAnchorWkt ) );
|
||||
|
||||
mOffsetFromCallout = element.attribute( QStringLiteral( "offsetFromCallout" ) ).isEmpty() ? QSizeF() : QgsSymbolLayerUtils::decodeSize( element.attribute( QStringLiteral( "offsetFromCallout" ) ) );
|
||||
bool ok = false;
|
||||
mOffsetFromCalloutUnit = QgsUnitTypes::decodeRenderUnit( element.attribute( QStringLiteral( "offsetFromCalloutUnit" ) ), &ok );
|
||||
if ( !ok )
|
||||
mOffsetFromCalloutUnit = Qgis::RenderUnit::Millimeters;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -167,3 +182,23 @@ void QgsAnnotationItem::renderCallout( QgsRenderContext &context, const QRectF &
|
||||
mCallout->render( context, rect, angle, anchor, calloutContext );
|
||||
mCallout->stopRender( context );
|
||||
}
|
||||
|
||||
Qgis::RenderUnit QgsAnnotationItem::offsetFromCalloutUnit() const
|
||||
{
|
||||
return mOffsetFromCalloutUnit;
|
||||
}
|
||||
|
||||
void QgsAnnotationItem::setOffsetFromCalloutUnit( Qgis::RenderUnit unit )
|
||||
{
|
||||
mOffsetFromCalloutUnit = unit;
|
||||
}
|
||||
|
||||
QSizeF QgsAnnotationItem::offsetFromCallout() const
|
||||
{
|
||||
return mOffsetFromCallout;
|
||||
}
|
||||
|
||||
void QgsAnnotationItem::setOffsetFromCallout( const QSizeF &offset )
|
||||
{
|
||||
mOffsetFromCallout = offset;
|
||||
}
|
||||
|
||||
@ -334,6 +334,54 @@ class CORE_EXPORT QgsAnnotationItem
|
||||
*/
|
||||
void setCalloutAnchor( const QgsGeometry &anchor );
|
||||
|
||||
/**
|
||||
* Returns the (optional) offset of the annotation item from the calloutAnchor().
|
||||
*
|
||||
* Some annotation item subclasses support placement relative to the callout anchor. For these
|
||||
* items, the offset from callout defines how far (in screen/page units) the item should be
|
||||
* placed from the anchor point.
|
||||
*
|
||||
* Units are defined by offsetFromCalloutUnit()
|
||||
*
|
||||
* \see setOffsetFromCallout()
|
||||
* \since QGIS 3.40
|
||||
*/
|
||||
QSizeF offsetFromCallout() const;
|
||||
|
||||
/**
|
||||
* Sets the offset of the annotation item from the calloutAnchor().
|
||||
*
|
||||
* Some annotation item subclasses support placement relative to the callout anchor. For these
|
||||
* items, the offset from callout defines how far (in screen/page units) the item should be
|
||||
* placed from the anchor point.
|
||||
*
|
||||
* Units are defined by offsetFromCalloutUnit()
|
||||
*
|
||||
* \see offsetFromCallout()
|
||||
* \since QGIS 3.40
|
||||
*/
|
||||
void setOffsetFromCallout( const QSizeF &offset );
|
||||
|
||||
/**
|
||||
* Returns the units for the offsetFromCallout().
|
||||
*
|
||||
* \see offsetFromCallout()
|
||||
* \see setOffsetFromCalloutUnit()
|
||||
*
|
||||
* \since QGIS 3.40
|
||||
*/
|
||||
Qgis::RenderUnit offsetFromCalloutUnit() const;
|
||||
|
||||
/**
|
||||
* Sets the \a unit for the offsetFromCallout().
|
||||
*
|
||||
* \see setOffsetFromCallout()
|
||||
* \see offsetFromCalloutUnit()
|
||||
*
|
||||
* \since QGIS 3.40
|
||||
*/
|
||||
void setOffsetFromCalloutUnit( Qgis::RenderUnit unit );
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
@ -377,6 +425,8 @@ class CORE_EXPORT QgsAnnotationItem
|
||||
|
||||
std::unique_ptr< QgsCallout > mCallout;
|
||||
QgsGeometry mCalloutAnchor;
|
||||
QSizeF mOffsetFromCallout;
|
||||
Qgis::RenderUnit mOffsetFromCalloutUnit = Qgis::RenderUnit::Millimeters;
|
||||
|
||||
#ifdef SIP_RUN
|
||||
QgsAnnotationItem( const QgsAnnotationItem &other );
|
||||
|
||||
@ -30,6 +30,8 @@
|
||||
#include "qgslinesymbollayer.h"
|
||||
#include "qgsunittypes.h"
|
||||
#include "qgscalloutsregistry.h"
|
||||
#include "qgslinestring.h"
|
||||
#include "qgspolygon.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
|
||||
@ -96,16 +98,40 @@ void QgsAnnotationPictureItem::render( QgsRenderContext &context, QgsFeedback *f
|
||||
|
||||
case Qgis::AnnotationPictureSizeMode::FixedSize:
|
||||
{
|
||||
QPointF center = bounds.center().toQPointF();
|
||||
|
||||
context.mapToPixel().transformInPlace( center.rx(), center.ry() );
|
||||
|
||||
const double widthPixels = context.convertToPainterUnits( mFixedSize.width(), mFixedSizeUnit );
|
||||
const double heightPixels = context.convertToPainterUnits( mFixedSize.height(), mFixedSizeUnit );
|
||||
|
||||
painterBounds = QRectF( center.x() - widthPixels * 0.5,
|
||||
center.y() - heightPixels * 0.5,
|
||||
widthPixels, heightPixels );
|
||||
if ( callout() && !calloutAnchor().isEmpty() )
|
||||
{
|
||||
QgsGeometry anchor = calloutAnchor();
|
||||
|
||||
const double calloutOffsetWidthPixels = context.convertToPainterUnits( offsetFromCallout().width(), offsetFromCalloutUnit() );
|
||||
const double calloutOffsetHeightPixels = context.convertToPainterUnits( offsetFromCallout().height(), offsetFromCalloutUnit() );
|
||||
|
||||
QPointF anchorPoint = anchor.asQPointF();
|
||||
if ( context.coordinateTransform().isValid() )
|
||||
{
|
||||
double x = anchorPoint.x();
|
||||
double y = anchorPoint.y();
|
||||
double z = 0.0;
|
||||
context.coordinateTransform().transformInPlace( x, y, z );
|
||||
anchorPoint = QPointF( x, y );
|
||||
}
|
||||
|
||||
context.mapToPixel().transformInPlace( anchorPoint.rx(), anchorPoint.ry() );
|
||||
|
||||
painterBounds = QRectF( anchorPoint.x() + calloutOffsetWidthPixels,
|
||||
anchorPoint.y() + calloutOffsetHeightPixels, widthPixels, heightPixels );
|
||||
}
|
||||
else
|
||||
{
|
||||
QPointF center = bounds.center().toQPointF();
|
||||
|
||||
context.mapToPixel().transformInPlace( center.rx(), center.ry() );
|
||||
painterBounds = QRectF( center.x() - widthPixels * 0.5,
|
||||
center.y() - heightPixels * 0.5,
|
||||
widthPixels, heightPixels );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -283,7 +309,7 @@ QList<QgsAnnotationItemNode> QgsAnnotationPictureItem::nodesV2( const QgsAnnotat
|
||||
BUILTIN_UNREACHABLE
|
||||
}
|
||||
|
||||
Qgis::AnnotationItemEditOperationResult QgsAnnotationPictureItem::applyEditV2( QgsAbstractAnnotationItemEditOperation *operation, const QgsAnnotationItemEditContext & )
|
||||
Qgis::AnnotationItemEditOperationResult QgsAnnotationPictureItem::applyEditV2( QgsAbstractAnnotationItemEditOperation *operation, const QgsAnnotationItemEditContext &context )
|
||||
{
|
||||
switch ( operation->type() )
|
||||
{
|
||||
@ -352,10 +378,34 @@ Qgis::AnnotationItemEditOperationResult QgsAnnotationPictureItem::applyEditV2( Q
|
||||
case QgsAbstractAnnotationItemEditOperation::Type::TranslateItem:
|
||||
{
|
||||
QgsAnnotationItemEditOperationTranslateItem *moveOperation = qgis::down_cast< QgsAnnotationItemEditOperationTranslateItem * >( operation );
|
||||
mBounds = QgsRectangle( mBounds.xMinimum() + moveOperation->translationX(),
|
||||
mBounds.yMinimum() + moveOperation->translationY(),
|
||||
mBounds.xMaximum() + moveOperation->translationX(),
|
||||
mBounds.yMaximum() + moveOperation->translationY() );
|
||||
switch ( mSizeMode )
|
||||
{
|
||||
|
||||
case Qgis::AnnotationPictureSizeMode::SpatialBounds:
|
||||
mBounds = QgsRectangle( mBounds.xMinimum() + moveOperation->translationX(),
|
||||
mBounds.yMinimum() + moveOperation->translationY(),
|
||||
mBounds.xMaximum() + moveOperation->translationX(),
|
||||
mBounds.yMaximum() + moveOperation->translationY() );
|
||||
break;
|
||||
|
||||
case Qgis::AnnotationPictureSizeMode::FixedSize:
|
||||
{
|
||||
if ( callout() && !calloutAnchor().isEmpty() )
|
||||
{
|
||||
const double xOffset = context.renderContext().convertFromPainterUnits( moveOperation->translationXPixels(), offsetFromCalloutUnit() );
|
||||
const double yOffset = context.renderContext().convertFromPainterUnits( moveOperation->translationYPixels(), offsetFromCalloutUnit() );
|
||||
setOffsetFromCallout( QSizeF( offsetFromCallout().width() + xOffset, offsetFromCallout().height() + yOffset ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
mBounds = QgsRectangle( mBounds.xMinimum() + moveOperation->translationX(),
|
||||
mBounds.yMinimum() + moveOperation->translationY(),
|
||||
mBounds.xMaximum() + moveOperation->translationX(),
|
||||
mBounds.yMaximum() + moveOperation->translationY() );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Qgis::AnnotationItemEditOperationResult::Success;
|
||||
}
|
||||
|
||||
@ -435,10 +485,52 @@ QgsAnnotationItemEditOperationTransientResults *QgsAnnotationPictureItem::transi
|
||||
|
||||
case Qgis::AnnotationPictureSizeMode::FixedSize:
|
||||
{
|
||||
const QgsRectangle currentBounds = context.currentItemBounds();
|
||||
const QgsRectangle newBounds = QgsRectangle::fromCenterAndSize( mBounds.center() + QgsVector( moveOperation->translationX(), moveOperation->translationY() ),
|
||||
currentBounds.width(), currentBounds.height() );
|
||||
return new QgsAnnotationItemEditOperationTransientResults( QgsGeometry::fromRect( newBounds ) );
|
||||
if ( callout() && !calloutAnchor().isEmpty() )
|
||||
{
|
||||
QgsGeometry anchor = calloutAnchor();
|
||||
|
||||
const double calloutOffsetWidthPixels = context.renderContext().convertToPainterUnits( offsetFromCallout().width(), offsetFromCalloutUnit() )
|
||||
+ moveOperation->translationXPixels();
|
||||
const double calloutOffsetHeightPixels = context.renderContext().convertToPainterUnits( offsetFromCallout().height(), offsetFromCalloutUnit() )
|
||||
+ moveOperation->translationYPixels();
|
||||
|
||||
QPointF anchorPoint = anchor.asQPointF();
|
||||
if ( context.renderContext().coordinateTransform().isValid() )
|
||||
{
|
||||
double x = anchorPoint.x();
|
||||
double y = anchorPoint.y();
|
||||
double z = 0.0;
|
||||
context.renderContext().coordinateTransform().transformInPlace( x, y, z );
|
||||
anchorPoint = QPointF( x, y );
|
||||
}
|
||||
|
||||
context.renderContext().mapToPixel().transformInPlace( anchorPoint.rx(), anchorPoint.ry() );
|
||||
|
||||
const double textOriginXPixels = anchorPoint.x() + calloutOffsetWidthPixels;
|
||||
const double textOriginYPixels = anchorPoint.y() + calloutOffsetHeightPixels;
|
||||
|
||||
const double widthPixels = context.renderContext().convertToPainterUnits( mFixedSize.width(), mFixedSizeUnit );
|
||||
const double heightPixels = context.renderContext().convertToPainterUnits( mFixedSize.height(), mFixedSizeUnit );
|
||||
|
||||
QgsLineString ls( QVector<QgsPointXY> { QgsPointXY( textOriginXPixels, textOriginYPixels ),
|
||||
QgsPointXY( textOriginXPixels + widthPixels, textOriginYPixels ),
|
||||
QgsPointXY( textOriginXPixels + widthPixels, textOriginYPixels + heightPixels ),
|
||||
QgsPointXY( textOriginXPixels, textOriginYPixels + heightPixels ),
|
||||
QgsPointXY( textOriginXPixels, textOriginYPixels )
|
||||
} );
|
||||
|
||||
QgsGeometry g( new QgsPolygon( ls.clone() ) );
|
||||
g.transform( context.renderContext().mapToPixel().transform().inverted() );
|
||||
g.transform( context.renderContext().coordinateTransform(), Qgis::TransformDirection::Reverse );
|
||||
return new QgsAnnotationItemEditOperationTransientResults( g );
|
||||
}
|
||||
else
|
||||
{
|
||||
const QgsRectangle currentBounds = context.currentItemBounds();
|
||||
const QgsRectangle newBounds = QgsRectangle::fromCenterAndSize( mBounds.center() + QgsVector( moveOperation->translationX(), moveOperation->translationY() ),
|
||||
currentBounds.width(), currentBounds.height() );
|
||||
return new QgsAnnotationItemEditOperationTransientResults( QgsGeometry::fromRect( newBounds ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -521,19 +613,26 @@ QgsRectangle QgsAnnotationPictureItem::boundingBox() const
|
||||
case Qgis::AnnotationPictureSizeMode::SpatialBounds:
|
||||
{
|
||||
bounds = mBounds;
|
||||
if ( callout() && !calloutAnchor().isEmpty() )
|
||||
{
|
||||
QgsGeometry anchor = calloutAnchor();
|
||||
bounds.combineExtentWith( anchor.boundingBox() );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Qgis::AnnotationPictureSizeMode::FixedSize:
|
||||
bounds = QgsRectangle( mBounds.center(), mBounds.center() );
|
||||
if ( callout() && !calloutAnchor().isEmpty() )
|
||||
{
|
||||
bounds = calloutAnchor().boundingBox();
|
||||
}
|
||||
else
|
||||
{
|
||||
bounds = QgsRectangle( mBounds.center(), mBounds.center() );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ( callout() && !calloutAnchor().isEmpty() )
|
||||
{
|
||||
QgsGeometry anchor = calloutAnchor();
|
||||
bounds.combineExtentWith( anchor.boundingBox() );
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
@ -546,24 +645,55 @@ QgsRectangle QgsAnnotationPictureItem::boundingBox( QgsRenderContext &context )
|
||||
|
||||
case Qgis::AnnotationPictureSizeMode::FixedSize:
|
||||
{
|
||||
QPointF center = mBounds.center().toQPointF();
|
||||
if ( context.coordinateTransform().isValid() )
|
||||
{
|
||||
double x = center.x();
|
||||
double y = center.y();
|
||||
double z = 0.0;
|
||||
context.coordinateTransform().transformInPlace( x, y, z );
|
||||
center = QPointF( x, y );
|
||||
}
|
||||
|
||||
context.mapToPixel().transformInPlace( center.rx(), center.ry() );
|
||||
|
||||
const double widthPixels = context.convertToPainterUnits( mFixedSize.width(), mFixedSizeUnit );
|
||||
const double heightPixels = context.convertToPainterUnits( mFixedSize.height(), mFixedSizeUnit );
|
||||
|
||||
const QRectF boundsInPixels( center.x() - widthPixels * 0.5,
|
||||
center.y() - heightPixels * 0.5,
|
||||
widthPixels, heightPixels );
|
||||
QRectF boundsInPixels;
|
||||
if ( callout() && !calloutAnchor().isEmpty() )
|
||||
{
|
||||
QgsGeometry anchor = calloutAnchor();
|
||||
|
||||
const double calloutOffsetWidthPixels = context.convertToPainterUnits( offsetFromCallout().width(), offsetFromCalloutUnit() );
|
||||
const double calloutOffsetHeightPixels = context.convertToPainterUnits( offsetFromCallout().height(), offsetFromCalloutUnit() );
|
||||
|
||||
QPointF anchorPoint = anchor.asQPointF();
|
||||
if ( context.coordinateTransform().isValid() )
|
||||
{
|
||||
double x = anchorPoint.x();
|
||||
double y = anchorPoint.y();
|
||||
double z = 0.0;
|
||||
context.coordinateTransform().transformInPlace( x, y, z );
|
||||
anchorPoint = QPointF( x, y );
|
||||
}
|
||||
|
||||
context.mapToPixel().transformInPlace( anchorPoint.rx(), anchorPoint.ry() );
|
||||
|
||||
QgsRectangle textRect( anchorPoint.x() + calloutOffsetWidthPixels,
|
||||
anchorPoint.y() + calloutOffsetHeightPixels,
|
||||
anchorPoint.x() + calloutOffsetWidthPixels + widthPixels,
|
||||
anchorPoint.y() + calloutOffsetHeightPixels + heightPixels );
|
||||
QgsRectangle anchorRect( anchorPoint.x(), anchorPoint.y(), anchorPoint.x(), anchorPoint.y() );
|
||||
anchorRect.combineExtentWith( textRect );
|
||||
|
||||
boundsInPixels = anchorRect.toRectF();
|
||||
}
|
||||
else
|
||||
{
|
||||
QPointF center = mBounds.center().toQPointF();
|
||||
if ( context.coordinateTransform().isValid() )
|
||||
{
|
||||
double x = center.x();
|
||||
double y = center.y();
|
||||
double z = 0.0;
|
||||
context.coordinateTransform().transformInPlace( x, y, z );
|
||||
center = QPointF( x, y );
|
||||
}
|
||||
|
||||
context.mapToPixel().transformInPlace( center.rx(), center.ry() );
|
||||
boundsInPixels = QRectF( center.x() - widthPixels * 0.5,
|
||||
center.y() - heightPixels * 0.5,
|
||||
widthPixels, heightPixels );
|
||||
}
|
||||
const QgsPointXY topLeft = context.mapToPixel().toMapCoordinates( boundsInPixels.left(), boundsInPixels.top() );
|
||||
const QgsPointXY topRight = context.mapToPixel().toMapCoordinates( boundsInPixels.right(), boundsInPixels.top() );
|
||||
const QgsPointXY bottomLeft = context.mapToPixel().toMapCoordinates( boundsInPixels.left(), boundsInPixels.bottom() );
|
||||
@ -571,12 +701,6 @@ QgsRectangle QgsAnnotationPictureItem::boundingBox( QgsRenderContext &context )
|
||||
|
||||
const QgsRectangle boundsMapUnits = QgsRectangle( topLeft.x(), bottomLeft.y(), bottomRight.x(), topRight.y() );
|
||||
QgsRectangle textRect = context.coordinateTransform().transformBoundingBox( boundsMapUnits, Qgis::TransformDirection::Reverse );
|
||||
|
||||
if ( callout() && !calloutAnchor().isEmpty() )
|
||||
{
|
||||
QgsGeometry anchor = calloutAnchor();
|
||||
textRect.combineExtentWith( anchor.boundingBox() );
|
||||
}
|
||||
return textRect;
|
||||
}
|
||||
}
|
||||
|
||||
@ -378,6 +378,7 @@ void QgsMapToolModifyAnnotation::canvasDoubleClickEvent( QgsMapMouseEvent *event
|
||||
QgsAnnotationItemEditContext context;
|
||||
context.setCurrentItemBounds( toLayerCoordinates( layer, mSelectedItemBounds ) );
|
||||
context.setRenderContext( QgsRenderContext::fromMapSettings( canvas()->mapSettings() ) );
|
||||
|
||||
switch ( layer->applyEditV2( &operation, context ) )
|
||||
{
|
||||
case Qgis::AnnotationItemEditOperationResult::Success:
|
||||
|
||||
@ -74,6 +74,9 @@ class TestQgsAnnotationPictureItem(QgisTestCase):
|
||||
item.setFixedSize(QSizeF(56,
|
||||
57))
|
||||
item.setFixedSizeUnit(Qgis.RenderUnit.Inches)
|
||||
item.setOffsetFromCallout(QSizeF(13.6, 17.2))
|
||||
item.setOffsetFromCalloutUnit(Qgis.RenderUnit.Inches)
|
||||
|
||||
self.assertEqual(item.bounds().toString(3), '100.000,200.000 : 300.000,400.000')
|
||||
self.assertEqual(item.path(), self.get_test_data_path('sample_svg.svg').as_posix())
|
||||
self.assertEqual(item.format(), Qgis.PictureFormat.SVG)
|
||||
@ -86,6 +89,8 @@ class TestQgsAnnotationPictureItem(QgisTestCase):
|
||||
self.assertEqual(item.fixedSize(), QSizeF(56,
|
||||
57))
|
||||
self.assertEqual(item.fixedSizeUnit(), Qgis.RenderUnit.Inches)
|
||||
self.assertEqual(item.offsetFromCallout(), QSizeF(13.6, 17.2))
|
||||
self.assertEqual(item.offsetFromCalloutUnit(), Qgis.RenderUnit.Inches)
|
||||
|
||||
item.setBackgroundSymbol(QgsFillSymbol.createSimple({'color': '200,100,100', 'outline_color': 'black'}))
|
||||
item.setFrameSymbol(QgsFillSymbol.createSimple(
|
||||
@ -141,10 +146,38 @@ class TestQgsAnnotationPictureItem(QgisTestCase):
|
||||
item.setSizeMode(Qgis.AnnotationPictureSizeMode.FixedSize)
|
||||
self.assertEqual(item.bounds().toString(3), '10.000,20.000 : 30.000,40.000')
|
||||
|
||||
context = QgsAnnotationItemEditContext()
|
||||
render_context = QgsRenderContext()
|
||||
render_context.setScaleFactor(5)
|
||||
context.setRenderContext(render_context)
|
||||
|
||||
self.assertEqual(item.applyEditV2(QgsAnnotationItemEditOperationTranslateItem('', 100, 200),
|
||||
QgsAnnotationItemEditContext()),
|
||||
context),
|
||||
Qgis.AnnotationItemEditOperationResult.Success)
|
||||
self.assertEqual(item.bounds().toString(3), '110.000,220.000 : 130.000,240.000')
|
||||
self.assertEqual(item.offsetFromCallout(), QSizeF())
|
||||
|
||||
def test_translate_fixed_size_with_callout_anchor(self):
|
||||
item = QgsAnnotationPictureItem(Qgis.PictureFormat.Raster,
|
||||
self.get_test_data_path(
|
||||
'rgb256x256.png').as_posix(),
|
||||
QgsRectangle(10, 20, 30, 40))
|
||||
item.setCalloutAnchor(QgsGeometry.fromWkt('Point(1 3)'))
|
||||
item.setCallout(QgsBalloonCallout())
|
||||
item.setSizeMode(Qgis.AnnotationPictureSizeMode.FixedSize)
|
||||
self.assertEqual(item.bounds().toString(3), '10.000,20.000 : 30.000,40.000')
|
||||
|
||||
context = QgsAnnotationItemEditContext()
|
||||
render_context = QgsRenderContext()
|
||||
render_context.setScaleFactor(5)
|
||||
context.setRenderContext(render_context)
|
||||
|
||||
self.assertEqual(item.applyEditV2(QgsAnnotationItemEditOperationTranslateItem('', 100, 200, 50, 30),
|
||||
context),
|
||||
Qgis.AnnotationItemEditOperationResult.Success)
|
||||
# should affect callout offset only
|
||||
self.assertEqual(item.offsetFromCallout(), QSizeF(9, 5))
|
||||
self.assertEqual(item.offsetFromCalloutUnit(), Qgis.RenderUnit.Millimeters)
|
||||
|
||||
def test_apply_move_node_edit_spatial_bounds(self):
|
||||
item = QgsAnnotationPictureItem(Qgis.PictureFormat.Raster,
|
||||
@ -189,9 +222,14 @@ class TestQgsAnnotationPictureItem(QgisTestCase):
|
||||
item.setSizeMode(Qgis.AnnotationPictureSizeMode.FixedSize)
|
||||
self.assertEqual(item.bounds().toString(3), '10.000,20.000 : 30.000,40.000')
|
||||
|
||||
context = QgsAnnotationItemEditContext()
|
||||
render_context = QgsRenderContext()
|
||||
render_context.setScaleFactor(5)
|
||||
context.setRenderContext(render_context)
|
||||
|
||||
self.assertEqual(item.applyEditV2(
|
||||
QgsAnnotationItemEditOperationMoveNode('', QgsVertexId(0, 0, 0), QgsPoint(30, 20), QgsPoint(17, 18)),
|
||||
QgsAnnotationItemEditContext()),
|
||||
context),
|
||||
Qgis.AnnotationItemEditOperationResult.Success)
|
||||
self.assertEqual(item.bounds().toString(3), '7.000,8.000 : 27.000,28.000')
|
||||
|
||||
@ -286,10 +324,38 @@ class TestQgsAnnotationPictureItem(QgisTestCase):
|
||||
op = QgsAnnotationItemEditOperationTranslateItem('', 100, 200)
|
||||
context = QgsAnnotationItemEditContext()
|
||||
context.setCurrentItemBounds(QgsRectangle(1, 2, 3, 4))
|
||||
render_context = QgsRenderContext()
|
||||
render_context.setScaleFactor(5)
|
||||
context.setRenderContext(render_context)
|
||||
|
||||
res = item.transientEditResultsV2(op, context)
|
||||
self.assertEqual(res.representativeGeometry().asWkt(),
|
||||
'Polygon ((119 229, 121 229, 121 231, 119 231, 119 229))')
|
||||
|
||||
def test_transient_translate_operation_fixed_size_with_callout_anchor(self):
|
||||
item = QgsAnnotationPictureItem(Qgis.PictureFormat.Raster,
|
||||
self.get_test_data_path(
|
||||
'rgb256x256.png').as_posix(),
|
||||
QgsRectangle(10, 20, 30, 40))
|
||||
item.setSizeMode(Qgis.AnnotationPictureSizeMode.FixedSize)
|
||||
item.setFixedSize(QSizeF(56,
|
||||
57))
|
||||
item.setFixedSizeUnit(Qgis.RenderUnit.Inches)
|
||||
item.setCalloutAnchor(QgsGeometry.fromWkt('Point(1 3)'))
|
||||
item.setCallout(QgsBalloonCallout())
|
||||
self.assertEqual(item.bounds().toString(3), '10.000,20.000 : 30.000,40.000')
|
||||
|
||||
op = QgsAnnotationItemEditOperationTranslateItem('', 100, 200, 50, 30)
|
||||
context = QgsAnnotationItemEditContext()
|
||||
context.setCurrentItemBounds(QgsRectangle(1, 2, 3, 4))
|
||||
render_context = QgsRenderContext()
|
||||
render_context.setScaleFactor(0.5)
|
||||
context.setRenderContext(render_context)
|
||||
|
||||
res = item.transientEditResultsV2(op, context)
|
||||
self.assertEqual(res.representativeGeometry().asWkt(2),
|
||||
'Polygon ((50.5 -26.5, 761.7 -26.5, 761.7 -750.4, 50.5 -750.4, 50.5 -26.5))')
|
||||
|
||||
def testReadWriteXml(self):
|
||||
doc = QDomDocument("testdoc")
|
||||
elem = doc.createElement('test')
|
||||
@ -308,6 +374,8 @@ class TestQgsAnnotationPictureItem(QgisTestCase):
|
||||
item.setFixedSizeUnit(Qgis.RenderUnit.Inches)
|
||||
item.setCalloutAnchor(QgsGeometry.fromWkt('Point(1 3)'))
|
||||
item.setCallout(QgsBalloonCallout())
|
||||
item.setOffsetFromCallout(QSizeF(13.6, 17.2))
|
||||
item.setOffsetFromCalloutUnit(Qgis.RenderUnit.Inches)
|
||||
|
||||
self.assertTrue(item.writeXml(elem, doc, QgsReadWriteContext()))
|
||||
|
||||
@ -331,6 +399,8 @@ class TestQgsAnnotationPictureItem(QgisTestCase):
|
||||
self.assertEqual(s2.fixedSizeUnit(), Qgis.RenderUnit.Inches)
|
||||
self.assertEqual(s2.calloutAnchor().asWkt(), 'Point (1 3)')
|
||||
self.assertIsInstance(s2.callout(), QgsBalloonCallout)
|
||||
self.assertEqual(s2.offsetFromCallout(), QSizeF(13.6, 17.2))
|
||||
self.assertEqual(s2.offsetFromCalloutUnit(), Qgis.RenderUnit.Inches)
|
||||
|
||||
def testClone(self):
|
||||
item = QgsAnnotationPictureItem(Qgis.PictureFormat.Raster, self.get_test_data_path('rgb256x256.png').as_posix(),
|
||||
@ -347,6 +417,8 @@ class TestQgsAnnotationPictureItem(QgisTestCase):
|
||||
item.setFixedSizeUnit(Qgis.RenderUnit.Inches)
|
||||
item.setCalloutAnchor(QgsGeometry.fromWkt('Point(1 3)'))
|
||||
item.setCallout(QgsBalloonCallout())
|
||||
item.setOffsetFromCallout(QSizeF(13.6, 17.2))
|
||||
item.setOffsetFromCalloutUnit(Qgis.RenderUnit.Inches)
|
||||
|
||||
s2 = item.clone()
|
||||
self.assertEqual(s2.bounds().toString(3), '10.000,20.000 : 30.000,40.000')
|
||||
@ -366,6 +438,8 @@ class TestQgsAnnotationPictureItem(QgisTestCase):
|
||||
self.assertEqual(s2.fixedSizeUnit(), Qgis.RenderUnit.Inches)
|
||||
self.assertEqual(s2.calloutAnchor().asWkt(), 'Point (1 3)')
|
||||
self.assertIsInstance(s2.callout(), QgsBalloonCallout)
|
||||
self.assertEqual(s2.offsetFromCallout(), QSizeF(13.6, 17.2))
|
||||
self.assertEqual(s2.offsetFromCalloutUnit(), Qgis.RenderUnit.Inches)
|
||||
|
||||
def testRenderRasterLockedAspect(self):
|
||||
item = QgsAnnotationPictureItem(Qgis.PictureFormat.Raster, self.get_test_data_path('rgb256x256.png').as_posix(),
|
||||
@ -580,6 +654,8 @@ class TestQgsAnnotationPictureItem(QgisTestCase):
|
||||
callout.lineSymbol().setWidth(1)
|
||||
item.setCallout(callout)
|
||||
item.setCalloutAnchor(QgsGeometry.fromWkt('Point(11 12)'))
|
||||
item.setOffsetFromCallout(QSizeF(60, -80))
|
||||
item.setOffsetFromCalloutUnit(Qgis.RenderUnit.Points)
|
||||
|
||||
settings = QgsMapSettings()
|
||||
settings.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:4326'))
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 877 B After Width: | Height: | Size: 857 B |
Loading…
x
Reference in New Issue
Block a user