[FEATURE][callouts] Add control over anchor point for callout on label
This gives users control over where a callout should join to the label text. (Previously, you only had control over where the callout would join to the corresponding feature geometry). Choices include: - Closest point (previous behavior) - Label Centroid - Fixed corners: Top left/top right/bottom left/bottom right/etc Data defined control over the label anchor is also possible
@ -48,6 +48,7 @@ relevant symbology elements to render them.
|
||||
OffsetFromLabel,
|
||||
DrawCalloutToAllParts,
|
||||
AnchorPointPosition,
|
||||
LabelAnchorPointPosition,
|
||||
};
|
||||
|
||||
enum DrawOrder
|
||||
@ -64,6 +65,20 @@ relevant symbology elements to render them.
|
||||
Centroid,
|
||||
};
|
||||
|
||||
enum LabelAnchorPoint
|
||||
{
|
||||
LabelPointOnExterior,
|
||||
LabelCentroid,
|
||||
LabelTopLeft,
|
||||
LabelTopMiddle,
|
||||
LabelTopRight,
|
||||
LabelMiddleLeft,
|
||||
LabelMiddleRight,
|
||||
LabelBottomLeft,
|
||||
LabelBottomMiddle,
|
||||
LabelBottomRight,
|
||||
};
|
||||
|
||||
QgsCallout();
|
||||
%Docstring
|
||||
Constructor for QgsCallout.
|
||||
@ -272,6 +287,51 @@ anchor point.
|
||||
.. seealso:: :py:func:`encodeAnchorPoint`
|
||||
%End
|
||||
|
||||
|
||||
LabelAnchorPoint labelAnchorPoint() const;
|
||||
%Docstring
|
||||
Returns the label's anchor point position.
|
||||
|
||||
.. seealso:: :py:func:`setLabelAnchorPoint`
|
||||
|
||||
.. versionadded:: 3.14
|
||||
%End
|
||||
|
||||
void setLabelAnchorPoint( LabelAnchorPoint anchor );
|
||||
%Docstring
|
||||
Sets the label's ``anchor`` point position.
|
||||
|
||||
.. seealso:: :py:func:`labelAnchorPoint`
|
||||
|
||||
.. versionadded:: 3.14
|
||||
%End
|
||||
|
||||
static QString encodeLabelAnchorPoint( LabelAnchorPoint anchor );
|
||||
%Docstring
|
||||
Encodes a label ``anchor`` point to its string representation.
|
||||
|
||||
:return: encoded string
|
||||
|
||||
.. seealso:: :py:func:`decodeLabelAnchorPoint`
|
||||
|
||||
.. versionadded:: 3.14
|
||||
%End
|
||||
|
||||
static QgsCallout::LabelAnchorPoint decodeLabelAnchorPoint( const QString &name, bool *ok = 0 );
|
||||
%Docstring
|
||||
Attempts to decode a string representation of a label anchor point name to the corresponding
|
||||
anchor point.
|
||||
|
||||
:param name: encoded label anchor point name
|
||||
:param ok: if specified, will be set to ``True`` if the anchor point was successfully decoded
|
||||
|
||||
:return: decoded name
|
||||
|
||||
.. seealso:: :py:func:`encodeLabelAnchorPoint`
|
||||
|
||||
.. versionadded:: 3.14
|
||||
%End
|
||||
|
||||
protected:
|
||||
|
||||
virtual void draw( QgsRenderContext &context, QRectF bodyBoundingBox, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext ) = 0;
|
||||
@ -292,6 +352,13 @@ Both ``rect`` and ``anchor`` are specified in painter coordinates (i.e. pixels).
|
||||
|
||||
The ``calloutContext`` argument is used to specify additional contextual information about
|
||||
how a callout is being rendered.
|
||||
%End
|
||||
|
||||
QgsGeometry labelAnchorGeometry( QRectF bodyBoundingBox, const double angle, LabelAnchorPoint anchor ) const;
|
||||
%Docstring
|
||||
Returns the anchor point geometry for a label with the given bounding box and ``anchor`` point mode.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
%End
|
||||
|
||||
};
|
||||
|
@ -39,6 +39,13 @@ void QgsCallout::initPropertyDefinitions()
|
||||
{ QgsCallout::OffsetFromLabel, QgsPropertyDefinition( "OffsetFromLabel", QObject::tr( "Offset from label" ), QgsPropertyDefinition::DoublePositive, origin ) },
|
||||
{ QgsCallout::DrawCalloutToAllParts, QgsPropertyDefinition( "DrawCalloutToAllParts", QObject::tr( "Draw lines to all feature parts" ), QgsPropertyDefinition::Boolean, origin ) },
|
||||
{ QgsCallout::AnchorPointPosition, QgsPropertyDefinition( "AnchorPointPosition", QgsPropertyDefinition::DataTypeString, QObject::tr( "Feature's anchor point position" ), QObject::tr( "string " ) + "[<b>pole_of_inaccessibility</b>|<b>point_on_exterior</b>|<b>point_on_surface</b>|<b>centroid</b>]", origin ) },
|
||||
{
|
||||
QgsCallout::LabelAnchorPointPosition, QgsPropertyDefinition( "LabelAnchorPointPosition", QgsPropertyDefinition::DataTypeString, QObject::tr( "Label's anchor point position" ), QObject::tr( "string " ) + "[<b>point_on_exterior</b>|<b>centroid</b>|<b>TL</b>=Top left|<b>T</b>=Top middle|"
|
||||
"<b>TR</b>=Top right|<br>"
|
||||
"<b>L</b>=Left|<b>R</b>=Right|<br>"
|
||||
"<b>BL</b>=Bottom left|<b>B</b>=Bottom middle|"
|
||||
"<b>BR</b>=Bottom right]", origin )
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -52,6 +59,7 @@ QVariantMap QgsCallout::properties( const QgsReadWriteContext & ) const
|
||||
QVariantMap props;
|
||||
props.insert( QStringLiteral( "enabled" ), mEnabled ? "1" : "0" );
|
||||
props.insert( QStringLiteral( "anchorPoint" ), encodeAnchorPoint( mAnchorPoint ) );
|
||||
props.insert( QStringLiteral( "labelAnchorPoint" ), encodeLabelAnchorPoint( mLabelAnchorPoint ) );
|
||||
props.insert( QStringLiteral( "ddProperties" ), mDataDefinedProperties.toVariant( propertyDefinitions() ) );
|
||||
return props;
|
||||
}
|
||||
@ -59,7 +67,8 @@ QVariantMap QgsCallout::properties( const QgsReadWriteContext & ) const
|
||||
void QgsCallout::readProperties( const QVariantMap &props, const QgsReadWriteContext & )
|
||||
{
|
||||
mEnabled = props.value( QStringLiteral( "enabled" ), QStringLiteral( "0" ) ).toInt();
|
||||
mAnchorPoint = decodeAnchorPoint( props.value( QStringLiteral( "anchorPoint" ), QString( "" ) ).toString() );
|
||||
mAnchorPoint = decodeAnchorPoint( props.value( QStringLiteral( "anchorPoint" ), QString() ).toString() );
|
||||
mLabelAnchorPoint = decodeLabelAnchorPoint( props.value( QStringLiteral( "labelAnchorPoint" ), QString() ).toString() );
|
||||
mDataDefinedProperties.loadVariant( props.value( QStringLiteral( "ddProperties" ) ), propertyDefinitions() );
|
||||
}
|
||||
|
||||
@ -184,6 +193,96 @@ QString QgsCallout::encodeAnchorPoint( AnchorPoint anchor )
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString QgsCallout::encodeLabelAnchorPoint( QgsCallout::LabelAnchorPoint anchor )
|
||||
{
|
||||
switch ( anchor )
|
||||
{
|
||||
case LabelPointOnExterior:
|
||||
return QStringLiteral( "point_on_exterior" );
|
||||
case LabelCentroid:
|
||||
return QStringLiteral( "centroid" );
|
||||
case LabelTopLeft:
|
||||
return QStringLiteral( "tl" );
|
||||
case LabelTopMiddle:
|
||||
return QStringLiteral( "t" );
|
||||
case LabelTopRight:
|
||||
return QStringLiteral( "tr" );
|
||||
case LabelMiddleLeft:
|
||||
return QStringLiteral( "l" );
|
||||
case LabelMiddleRight:
|
||||
return QStringLiteral( "r" );
|
||||
case LabelBottomLeft:
|
||||
return QStringLiteral( "bl" );
|
||||
case LabelBottomMiddle:
|
||||
return QStringLiteral( "b" );
|
||||
case LabelBottomRight:
|
||||
return QStringLiteral( "br" );
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
QgsCallout::LabelAnchorPoint QgsCallout::decodeLabelAnchorPoint( const QString &name, bool *ok )
|
||||
{
|
||||
if ( ok )
|
||||
*ok = true;
|
||||
QString cleaned = name.toLower().trimmed();
|
||||
|
||||
if ( cleaned == QLatin1String( "point_on_exterior" ) )
|
||||
return LabelPointOnExterior;
|
||||
else if ( cleaned == QLatin1String( "centroid" ) )
|
||||
return LabelCentroid;
|
||||
else if ( cleaned == QLatin1String( "tl" ) )
|
||||
return LabelTopLeft;
|
||||
else if ( cleaned == QLatin1String( "t" ) )
|
||||
return LabelTopMiddle;
|
||||
else if ( cleaned == QLatin1String( "tr" ) )
|
||||
return LabelTopRight;
|
||||
else if ( cleaned == QLatin1String( "l" ) )
|
||||
return LabelMiddleLeft;
|
||||
else if ( cleaned == QLatin1String( "r" ) )
|
||||
return LabelMiddleRight;
|
||||
else if ( cleaned == QLatin1String( "bl" ) )
|
||||
return LabelBottomLeft;
|
||||
else if ( cleaned == QLatin1String( "b" ) )
|
||||
return LabelBottomMiddle;
|
||||
else if ( cleaned == QLatin1String( "br" ) )
|
||||
return LabelBottomRight;
|
||||
|
||||
if ( ok )
|
||||
*ok = false;
|
||||
return LabelPointOnExterior;
|
||||
}
|
||||
|
||||
QgsGeometry QgsCallout::labelAnchorGeometry( QRectF rect, const double, LabelAnchorPoint anchor ) const
|
||||
{
|
||||
QgsGeometry label( QgsGeometry::fromRect( rect ) );
|
||||
switch ( anchor )
|
||||
{
|
||||
case LabelPointOnExterior:
|
||||
return label;
|
||||
case LabelCentroid:
|
||||
return label.centroid();
|
||||
case LabelTopLeft:
|
||||
return QgsGeometry::fromPointXY( QgsPointXY( rect.bottomLeft() ) );
|
||||
case LabelTopMiddle:
|
||||
return QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.bottom() ) );
|
||||
case LabelTopRight:
|
||||
return QgsGeometry::fromPointXY( QgsPointXY( rect.bottomRight() ) );
|
||||
case LabelMiddleLeft:
|
||||
return QgsGeometry::fromPointXY( QgsPointXY( rect.left(), ( rect.top() + rect.bottom() ) / 2.0 ) );
|
||||
case LabelMiddleRight:
|
||||
return QgsGeometry::fromPointXY( QgsPointXY( rect.right(), ( rect.top() + rect.bottom() ) / 2.0 ) );
|
||||
case LabelBottomLeft:
|
||||
return QgsGeometry::fromPointXY( QgsPointXY( rect.topLeft() ) );
|
||||
case LabelBottomMiddle:
|
||||
return QgsGeometry::fromPointXY( QgsPointXY( ( rect.left() + rect.right() ) / 2.0, rect.top() ) );
|
||||
case LabelBottomRight:
|
||||
return QgsGeometry::fromPointXY( QgsPointXY( rect.topRight() ) );
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
//
|
||||
// QgsSimpleLineCallout
|
||||
//
|
||||
@ -312,9 +411,17 @@ void QgsSimpleLineCallout::setLineSymbol( QgsLineSymbol *symbol )
|
||||
mLineSymbol.reset( symbol );
|
||||
}
|
||||
|
||||
void QgsSimpleLineCallout::draw( QgsRenderContext &context, QRectF rect, const double, const QgsGeometry &anchor, QgsCalloutContext &calloutContext )
|
||||
void QgsSimpleLineCallout::draw( QgsRenderContext &context, QRectF rect, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext )
|
||||
{
|
||||
QgsGeometry label( QgsGeometry::fromRect( rect ) );
|
||||
LabelAnchorPoint labelAnchor = labelAnchorPoint();
|
||||
if ( dataDefinedProperties().isActive( QgsCallout::LabelAnchorPointPosition ) )
|
||||
{
|
||||
QString encodedAnchor = encodeLabelAnchorPoint( labelAnchor );
|
||||
context.expressionContext().setOriginalValueVariable( encodedAnchor );
|
||||
labelAnchor = decodeLabelAnchorPoint( dataDefinedProperties().valueAsString( QgsCallout::LabelAnchorPointPosition, context.expressionContext(), encodedAnchor ) );
|
||||
}
|
||||
QgsGeometry label = labelAnchorGeometry( rect, angle, labelAnchor );
|
||||
|
||||
auto drawCalloutLine = [this, &context, &label]( const QgsGeometry & partAnchor )
|
||||
{
|
||||
QgsGeometry line;
|
||||
@ -451,9 +558,17 @@ QgsManhattanLineCallout *QgsManhattanLineCallout::clone() const
|
||||
return new QgsManhattanLineCallout( *this );
|
||||
}
|
||||
|
||||
void QgsManhattanLineCallout::draw( QgsRenderContext &context, QRectF rect, const double, const QgsGeometry &anchor, QgsCalloutContext &calloutContext )
|
||||
void QgsManhattanLineCallout::draw( QgsRenderContext &context, QRectF rect, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext )
|
||||
{
|
||||
QgsGeometry label( QgsGeometry::fromRect( rect ) );
|
||||
LabelAnchorPoint labelAnchor = labelAnchorPoint();
|
||||
if ( dataDefinedProperties().isActive( QgsCallout::LabelAnchorPointPosition ) )
|
||||
{
|
||||
QString encodedAnchor = encodeLabelAnchorPoint( labelAnchor );
|
||||
context.expressionContext().setOriginalValueVariable( encodedAnchor );
|
||||
labelAnchor = decodeLabelAnchorPoint( dataDefinedProperties().valueAsString( QgsCallout::LabelAnchorPointPosition, context.expressionContext(), encodedAnchor ) );
|
||||
}
|
||||
QgsGeometry label = labelAnchorGeometry( rect, angle, labelAnchor );
|
||||
|
||||
auto drawCalloutLine = [this, &context, &label]( const QgsGeometry & partAnchor )
|
||||
{
|
||||
QgsGeometry line;
|
||||
|
@ -73,6 +73,7 @@ class CORE_EXPORT QgsCallout
|
||||
OffsetFromLabel, //!< Distance to offset lines from label area
|
||||
DrawCalloutToAllParts, //!< Whether callout lines should be drawn to all feature parts
|
||||
AnchorPointPosition, //!< Feature's anchor point position
|
||||
LabelAnchorPointPosition, //!< Label's anchor point position
|
||||
};
|
||||
|
||||
//! Options for draw order (stacking) of callouts
|
||||
@ -91,6 +92,24 @@ class CORE_EXPORT QgsCallout
|
||||
Centroid, //!< The surface's centroid is used as anchor for polygon geometries
|
||||
};
|
||||
|
||||
/**
|
||||
* Label's anchor point position.
|
||||
* \since QGIS 3.14
|
||||
*/
|
||||
enum LabelAnchorPoint
|
||||
{
|
||||
LabelPointOnExterior, //!< The point on the label's boundary closest to the feature
|
||||
LabelCentroid, //!< The labe's centroid
|
||||
LabelTopLeft, //!< Top left corner of the label's boundary
|
||||
LabelTopMiddle, //!< Top middle of the label's boundary
|
||||
LabelTopRight, //!< Top right corner of the label's boundary
|
||||
LabelMiddleLeft, //!< Middle left of the label's boundary
|
||||
LabelMiddleRight, //!< Middle right of the label's boundary
|
||||
LabelBottomLeft, //!< Bottom left corner of the label's boundary
|
||||
LabelBottomMiddle, //!< Bottom middle of the label's boundary
|
||||
LabelBottomRight, //!< Bottom right corner of the label's boundary
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor for QgsCallout.
|
||||
*/
|
||||
@ -293,6 +312,42 @@ class CORE_EXPORT QgsCallout
|
||||
*/
|
||||
static QgsCallout::AnchorPoint decodeAnchorPoint( const QString &name, bool *ok = nullptr );
|
||||
|
||||
|
||||
/**
|
||||
* Returns the label's anchor point position.
|
||||
*
|
||||
* \see setLabelAnchorPoint()
|
||||
* \since QGIS 3.14
|
||||
*/
|
||||
LabelAnchorPoint labelAnchorPoint() const { return mLabelAnchorPoint; }
|
||||
|
||||
/**
|
||||
* Sets the label's \a anchor point position.
|
||||
*
|
||||
* \see labelAnchorPoint()
|
||||
* \since QGIS 3.14
|
||||
*/
|
||||
void setLabelAnchorPoint( LabelAnchorPoint anchor ) { mLabelAnchorPoint = anchor; }
|
||||
|
||||
/**
|
||||
* Encodes a label \a anchor point to its string representation.
|
||||
* \returns encoded string
|
||||
* \see decodeLabelAnchorPoint()
|
||||
* \since QGIS 3.14
|
||||
*/
|
||||
static QString encodeLabelAnchorPoint( LabelAnchorPoint anchor );
|
||||
|
||||
/**
|
||||
* Attempts to decode a string representation of a label anchor point name to the corresponding
|
||||
* anchor point.
|
||||
* \param name encoded label anchor point name
|
||||
* \param ok if specified, will be set to TRUE if the anchor point was successfully decoded
|
||||
* \returns decoded name
|
||||
* \see encodeLabelAnchorPoint()
|
||||
* \since QGIS 3.14
|
||||
*/
|
||||
static QgsCallout::LabelAnchorPoint decodeLabelAnchorPoint( const QString &name, bool *ok = nullptr );
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
@ -315,11 +370,18 @@ class CORE_EXPORT QgsCallout
|
||||
*/
|
||||
virtual void draw( QgsRenderContext &context, QRectF bodyBoundingBox, const double angle, const QgsGeometry &anchor, QgsCalloutContext &calloutContext ) = 0;
|
||||
|
||||
/**
|
||||
* Returns the anchor point geometry for a label with the given bounding box and \a anchor point mode.
|
||||
* \since QGIS 3.14
|
||||
*/
|
||||
QgsGeometry labelAnchorGeometry( QRectF bodyBoundingBox, const double angle, LabelAnchorPoint anchor ) const;
|
||||
|
||||
private:
|
||||
|
||||
bool mEnabled = false;
|
||||
|
||||
AnchorPoint mAnchorPoint = PoleOfInaccessibility;
|
||||
LabelAnchorPoint mLabelAnchorPoint = LabelPointOnExterior;
|
||||
|
||||
//! Property collection for data defined callout settings
|
||||
QgsPropertyCollection mDataDefinedProperties;
|
||||
|
@ -167,6 +167,18 @@ QgsSimpleLineCalloutWidget::QgsSimpleLineCalloutWidget( QgsVectorLayer *vl, QWid
|
||||
mAnchorPointComboBox->addItem( tr( "Centroid" ), static_cast< int >( QgsCallout::Centroid ) );
|
||||
connect( mAnchorPointComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsSimpleLineCalloutWidget::mAnchorPointComboBox_currentIndexChanged );
|
||||
|
||||
mLabelAnchorPointComboBox->addItem( tr( "Closest Point" ), static_cast< int >( QgsCallout::LabelPointOnExterior ) );
|
||||
mLabelAnchorPointComboBox->addItem( tr( "Centroid" ), static_cast< int >( QgsCallout::LabelCentroid ) );
|
||||
mLabelAnchorPointComboBox->addItem( tr( "Top Left" ), static_cast< int >( QgsCallout::LabelTopLeft ) );
|
||||
mLabelAnchorPointComboBox->addItem( tr( "Top Center" ), static_cast< int >( QgsCallout::LabelTopMiddle ) );
|
||||
mLabelAnchorPointComboBox->addItem( tr( "Top Right" ), static_cast< int >( QgsCallout::LabelTopRight ) );
|
||||
mLabelAnchorPointComboBox->addItem( tr( "Left Middle" ), static_cast< int >( QgsCallout::LabelMiddleLeft ) );
|
||||
mLabelAnchorPointComboBox->addItem( tr( "Right Middle" ), static_cast< int >( QgsCallout::LabelMiddleRight ) );
|
||||
mLabelAnchorPointComboBox->addItem( tr( "Bottom Left" ), static_cast< int >( QgsCallout::LabelBottomLeft ) );
|
||||
mLabelAnchorPointComboBox->addItem( tr( "Bottom Center" ), static_cast< int >( QgsCallout::LabelBottomMiddle ) );
|
||||
mLabelAnchorPointComboBox->addItem( tr( "Bottom Right" ), static_cast< int >( QgsCallout::LabelBottomRight ) );
|
||||
connect( mLabelAnchorPointComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsSimpleLineCalloutWidget::mLabelAnchorPointComboBox_currentIndexChanged );
|
||||
|
||||
connect( mCalloutLineStyleButton, &QgsSymbolButton::changed, this, &QgsSimpleLineCalloutWidget::lineSymbolChanged );
|
||||
}
|
||||
|
||||
@ -202,12 +214,14 @@ void QgsSimpleLineCalloutWidget::setCallout( QgsCallout *callout )
|
||||
whileBlocking( mDrawToAllPartsCheck )->setChecked( mCallout->drawCalloutToAllParts() );
|
||||
|
||||
whileBlocking( mAnchorPointComboBox )->setCurrentIndex( mAnchorPointComboBox->findData( static_cast< int >( callout->anchorPoint() ) ) );
|
||||
whileBlocking( mLabelAnchorPointComboBox )->setCurrentIndex( mLabelAnchorPointComboBox->findData( static_cast< int >( callout->labelAnchorPoint() ) ) );
|
||||
|
||||
registerDataDefinedButton( mMinCalloutLengthDDBtn, QgsCallout::MinimumCalloutLength );
|
||||
registerDataDefinedButton( mOffsetFromAnchorDDBtn, QgsCallout::OffsetFromAnchor );
|
||||
registerDataDefinedButton( mOffsetFromLabelDDBtn, QgsCallout::OffsetFromLabel );
|
||||
registerDataDefinedButton( mDrawToAllPartsDDBtn, QgsCallout::DrawCalloutToAllParts );
|
||||
registerDataDefinedButton( mAnchorPointDDBtn, QgsCallout::AnchorPointPosition );
|
||||
registerDataDefinedButton( mLabelAnchorPointDDBtn, QgsCallout::LabelAnchorPointPosition );
|
||||
}
|
||||
|
||||
void QgsSimpleLineCalloutWidget::setGeometryType( QgsWkbTypes::GeometryType type )
|
||||
@ -277,6 +291,12 @@ void QgsSimpleLineCalloutWidget::mAnchorPointComboBox_currentIndexChanged( int i
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void QgsSimpleLineCalloutWidget::mLabelAnchorPointComboBox_currentIndexChanged( int index )
|
||||
{
|
||||
mCallout->setLabelAnchorPoint( static_cast<QgsCallout::LabelAnchorPoint>( mLabelAnchorPointComboBox->itemData( index ).toInt() ) );
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void QgsSimpleLineCalloutWidget::drawToAllPartsToggled( bool active )
|
||||
{
|
||||
mCallout->setDrawCalloutToAllParts( active );
|
||||
@ -296,3 +316,4 @@ QgsManhattanLineCalloutWidget::QgsManhattanLineCalloutWidget( QgsVectorLayer *vl
|
||||
|
||||
|
||||
///@endcond
|
||||
|
||||
|
@ -152,6 +152,7 @@ class GUI_EXPORT QgsSimpleLineCalloutWidget : public QgsCalloutWidget, private U
|
||||
void offsetFromLabelChanged();
|
||||
void lineSymbolChanged();
|
||||
void mAnchorPointComboBox_currentIndexChanged( int index );
|
||||
void mLabelAnchorPointComboBox_currentIndexChanged( int index );
|
||||
void drawToAllPartsToggled( bool active );
|
||||
|
||||
private:
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>337</width>
|
||||
<height>184</height>
|
||||
<width>341</width>
|
||||
<height>201</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -34,13 +34,70 @@
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="2" column="3">
|
||||
<widget class="QgsPropertyOverrideButton" name="mOffsetFromAnchorDDBtn">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Line style</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="mAnchorPointLbl">
|
||||
<property name="text">
|
||||
<string>Anchor point</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QgsUnitSelectionWidget" name="mOffsetFromAnchorUnitWidget" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>10</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QgsPropertyOverrideButton" name="mMinCalloutLengthDDBtn">
|
||||
<property name="text">
|
||||
<string>…</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_47">
|
||||
<property name="text">
|
||||
<string>Offset from label area</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1" colspan="2">
|
||||
<widget class="QComboBox" name="mAnchorPointComboBox"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_46">
|
||||
<property name="text">
|
||||
<string>Offset from feature</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="2">
|
||||
<widget class="QgsSymbolButton" name="mCalloutLineStyleButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Symbol…</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="2">
|
||||
<widget class="QgsUnitSelectionWidget" name="mOffsetFromLabelUnitWidget" native="true">
|
||||
<property name="minimumSize">
|
||||
@ -54,16 +111,30 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="2">
|
||||
<widget class="QgsSymbolButton" name="mCalloutLineStyleButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<item row="4" column="3">
|
||||
<widget class="QgsPropertyOverrideButton" name="mDrawToAllPartsDDBtn">
|
||||
<property name="text">
|
||||
<string>Symbol…</string>
|
||||
<string>…</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="3">
|
||||
<widget class="QgsPropertyOverrideButton" name="mOffsetFromLabelDDBtn">
|
||||
<property name="text">
|
||||
<string>…</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QgsUnitSelectionWidget" name="mMinCalloutWidthUnitWidget" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>10</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -92,65 +163,20 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_46">
|
||||
<item row="4" column="0" colspan="3">
|
||||
<widget class="QCheckBox" name="mDrawToAllPartsCheck">
|
||||
<property name="text">
|
||||
<string>Offset from feature</string>
|
||||
<string>Draw lines to all feature parts</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QgsUnitSelectionWidget" name="mOffsetFromAnchorUnitWidget" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>10</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QgsPropertyOverrideButton" name="mMinCalloutLengthDDBtn">
|
||||
<item row="2" column="3">
|
||||
<widget class="QgsPropertyOverrideButton" name="mOffsetFromAnchorDDBtn">
|
||||
<property name="text">
|
||||
<string>…</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="3">
|
||||
<widget class="QgsPropertyOverrideButton" name="mOffsetFromLabelDDBtn">
|
||||
<property name="text">
|
||||
<string>…</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QgsDoubleSpinBox" name="mOffsetFromLabelSpin">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>100000.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.200000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>0.000000000000000</double>
|
||||
</property>
|
||||
<property name="showClearButton" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QgsDoubleSpinBox" name="mMinCalloutLengthSpin">
|
||||
<property name="sizePolicy">
|
||||
@ -176,23 +202,10 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QgsUnitSelectionWidget" name="mMinCalloutWidthUnitWidget" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>10</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::StrongFocus</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_47">
|
||||
<item row="5" column="3">
|
||||
<widget class="QgsPropertyOverrideButton" name="mAnchorPointDDBtn">
|
||||
<property name="text">
|
||||
<string>Offset from label area</string>
|
||||
<string>…</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -203,39 +216,43 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Line style</string>
|
||||
<item row="3" column="1">
|
||||
<widget class="QgsDoubleSpinBox" name="mOffsetFromLabelSpin">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>100000.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>0.200000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>0.000000000000000</double>
|
||||
</property>
|
||||
<property name="showClearButton" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="3">
|
||||
<widget class="QCheckBox" name="mDrawToAllPartsCheck">
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="mAnchorPointLbl_2">
|
||||
<property name="text">
|
||||
<string>Draw lines to all feature parts</string>
|
||||
<string>Label anchor point</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="3">
|
||||
<widget class="QgsPropertyOverrideButton" name="mDrawToAllPartsDDBtn">
|
||||
<property name="text">
|
||||
<string>…</string>
|
||||
</property>
|
||||
</widget>
|
||||
<item row="6" column="1" colspan="2">
|
||||
<widget class="QComboBox" name="mLabelAnchorPointComboBox"/>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="mAnchorPointLbl">
|
||||
<property name="text">
|
||||
<string>Anchor point</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1" colspan="2">
|
||||
<widget class="QComboBox" name="mAnchorPointComboBox"></widget>
|
||||
</item>
|
||||
<item row="5" column="3">
|
||||
<widget class="QgsPropertyOverrideButton" name="mAnchorPointDDBtn">
|
||||
<item row="6" column="3">
|
||||
<widget class="QgsPropertyOverrideButton" name="mLabelAnchorPointDDBtn">
|
||||
<property name="text">
|
||||
<string>…</string>
|
||||
</property>
|
||||
|
@ -133,6 +133,16 @@ class TestQgsCallout: public QObject
|
||||
void calloutDataDefinedOffsetFromAnchor();
|
||||
void calloutOffsetFromLabel();
|
||||
void calloutDataDefinedOffsetFromLabel();
|
||||
void calloutLabelAnchorTopRight();
|
||||
void calloutLabelAnchorTopLeft();
|
||||
void calloutLabelAnchorTop();
|
||||
void calloutLabelAnchorBottomLeft();
|
||||
void calloutLabelAnchorBottom();
|
||||
void calloutLabelAnchorBottomRight();
|
||||
void calloutLabelAnchorLeft();
|
||||
void calloutLabelAnchorRight();
|
||||
void calloutLabelAnchorCentroid();
|
||||
void calloutLabelDataDefinedAnchor();
|
||||
void calloutBehindLabel();
|
||||
void calloutBehindIndividualLabels();
|
||||
void calloutNoDrawToAllParts();
|
||||
@ -939,6 +949,527 @@ void TestQgsCallout::calloutDataDefinedOffsetFromLabel()
|
||||
QVERIFY( imageCheck( "callout_data_defined_offset_from_label", img, 20 ) );
|
||||
}
|
||||
|
||||
void TestQgsCallout::calloutLabelAnchorTopRight()
|
||||
{
|
||||
QSize size( 640, 480 );
|
||||
QgsMapSettings mapSettings;
|
||||
mapSettings.setOutputSize( size );
|
||||
mapSettings.setExtent( vl->extent() );
|
||||
mapSettings.setLayers( QList<QgsMapLayer *>() << vl );
|
||||
mapSettings.setOutputDpi( 96 );
|
||||
mapSettings.setRotation( 45 );
|
||||
|
||||
QgsMapRendererSequentialJob job( mapSettings );
|
||||
job.start();
|
||||
job.waitForFinished();
|
||||
|
||||
QImage img = job.renderedImage();
|
||||
|
||||
QPainter p( &img );
|
||||
QgsRenderContext context = QgsRenderContext::fromMapSettings( mapSettings );
|
||||
context.setPainter( &p );
|
||||
|
||||
QgsPalLayerSettings settings;
|
||||
settings.fieldName = QStringLiteral( "Class" );
|
||||
settings.placement = QgsPalLayerSettings::AroundPoint;
|
||||
settings.dist = 10;
|
||||
|
||||
QgsTextFormat format;
|
||||
format.setFont( QgsFontUtils::getStandardTestFont( QStringLiteral( "Bold" ) ).family() );
|
||||
format.setSize( 12 );
|
||||
format.setNamedStyle( QStringLiteral( "Bold" ) );
|
||||
format.setColor( QColor( 200, 0, 200 ) );
|
||||
settings.setFormat( format );
|
||||
|
||||
QgsSimpleLineCallout *callout = new QgsSimpleLineCallout();
|
||||
callout->setEnabled( true );
|
||||
callout->lineSymbol()->setWidth( 1 );
|
||||
callout->setLabelAnchorPoint( QgsCallout::LabelTopRight );
|
||||
settings.setCallout( callout );
|
||||
|
||||
vl->setLabeling( new QgsVectorLayerSimpleLabeling( settings ) ); // TODO: this should not be necessary!
|
||||
vl->setLabelsEnabled( true );
|
||||
|
||||
QgsDefaultLabelingEngine engine;
|
||||
engine.setMapSettings( mapSettings );
|
||||
engine.addProvider( new QgsVectorLayerLabelProvider( vl, QString(), true, &settings ) );
|
||||
//engine.setFlags( QgsLabelingEngine::RenderOutlineLabels | QgsLabelingEngine::DrawLabelRectOnly );
|
||||
engine.run( context );
|
||||
|
||||
p.end();
|
||||
|
||||
QVERIFY( imageCheck( "callout_label_anchor_top_right", img, 20 ) );
|
||||
}
|
||||
|
||||
void TestQgsCallout::calloutLabelAnchorTopLeft()
|
||||
{
|
||||
QSize size( 640, 480 );
|
||||
QgsMapSettings mapSettings;
|
||||
mapSettings.setOutputSize( size );
|
||||
mapSettings.setExtent( vl->extent() );
|
||||
mapSettings.setLayers( QList<QgsMapLayer *>() << vl );
|
||||
mapSettings.setOutputDpi( 96 );
|
||||
mapSettings.setRotation( 45 );
|
||||
|
||||
QgsMapRendererSequentialJob job( mapSettings );
|
||||
job.start();
|
||||
job.waitForFinished();
|
||||
|
||||
QImage img = job.renderedImage();
|
||||
|
||||
QPainter p( &img );
|
||||
QgsRenderContext context = QgsRenderContext::fromMapSettings( mapSettings );
|
||||
context.setPainter( &p );
|
||||
|
||||
QgsPalLayerSettings settings;
|
||||
settings.fieldName = QStringLiteral( "Class" );
|
||||
settings.placement = QgsPalLayerSettings::AroundPoint;
|
||||
settings.dist = 10;
|
||||
|
||||
QgsTextFormat format;
|
||||
format.setFont( QgsFontUtils::getStandardTestFont( QStringLiteral( "Bold" ) ).family() );
|
||||
format.setSize( 12 );
|
||||
format.setNamedStyle( QStringLiteral( "Bold" ) );
|
||||
format.setColor( QColor( 200, 0, 200 ) );
|
||||
settings.setFormat( format );
|
||||
|
||||
QgsSimpleLineCallout *callout = new QgsSimpleLineCallout();
|
||||
callout->setEnabled( true );
|
||||
callout->lineSymbol()->setWidth( 1 );
|
||||
callout->setLabelAnchorPoint( QgsCallout::LabelTopLeft );
|
||||
settings.setCallout( callout );
|
||||
|
||||
vl->setLabeling( new QgsVectorLayerSimpleLabeling( settings ) ); // TODO: this should not be necessary!
|
||||
vl->setLabelsEnabled( true );
|
||||
|
||||
QgsDefaultLabelingEngine engine;
|
||||
engine.setMapSettings( mapSettings );
|
||||
engine.addProvider( new QgsVectorLayerLabelProvider( vl, QString(), true, &settings ) );
|
||||
//engine.setFlags( QgsLabelingEngine::RenderOutlineLabels | QgsLabelingEngine::DrawLabelRectOnly );
|
||||
engine.run( context );
|
||||
|
||||
p.end();
|
||||
|
||||
QVERIFY( imageCheck( "callout_label_anchor_top_left", img, 20 ) );
|
||||
}
|
||||
|
||||
void TestQgsCallout::calloutLabelAnchorTop()
|
||||
{
|
||||
QSize size( 640, 480 );
|
||||
QgsMapSettings mapSettings;
|
||||
mapSettings.setOutputSize( size );
|
||||
mapSettings.setExtent( vl->extent() );
|
||||
mapSettings.setLayers( QList<QgsMapLayer *>() << vl );
|
||||
mapSettings.setOutputDpi( 96 );
|
||||
mapSettings.setRotation( 45 );
|
||||
|
||||
QgsMapRendererSequentialJob job( mapSettings );
|
||||
job.start();
|
||||
job.waitForFinished();
|
||||
|
||||
QImage img = job.renderedImage();
|
||||
|
||||
QPainter p( &img );
|
||||
QgsRenderContext context = QgsRenderContext::fromMapSettings( mapSettings );
|
||||
context.setPainter( &p );
|
||||
|
||||
QgsPalLayerSettings settings;
|
||||
settings.fieldName = QStringLiteral( "Class" );
|
||||
settings.placement = QgsPalLayerSettings::AroundPoint;
|
||||
settings.dist = 10;
|
||||
|
||||
QgsTextFormat format;
|
||||
format.setFont( QgsFontUtils::getStandardTestFont( QStringLiteral( "Bold" ) ).family() );
|
||||
format.setSize( 12 );
|
||||
format.setNamedStyle( QStringLiteral( "Bold" ) );
|
||||
format.setColor( QColor( 200, 0, 200 ) );
|
||||
settings.setFormat( format );
|
||||
|
||||
QgsSimpleLineCallout *callout = new QgsSimpleLineCallout();
|
||||
callout->setEnabled( true );
|
||||
callout->lineSymbol()->setWidth( 1 );
|
||||
callout->setLabelAnchorPoint( QgsCallout::LabelTopMiddle );
|
||||
settings.setCallout( callout );
|
||||
|
||||
vl->setLabeling( new QgsVectorLayerSimpleLabeling( settings ) ); // TODO: this should not be necessary!
|
||||
vl->setLabelsEnabled( true );
|
||||
|
||||
QgsDefaultLabelingEngine engine;
|
||||
engine.setMapSettings( mapSettings );
|
||||
engine.addProvider( new QgsVectorLayerLabelProvider( vl, QString(), true, &settings ) );
|
||||
//engine.setFlags( QgsLabelingEngine::RenderOutlineLabels | QgsLabelingEngine::DrawLabelRectOnly );
|
||||
engine.run( context );
|
||||
|
||||
p.end();
|
||||
|
||||
QVERIFY( imageCheck( "callout_label_anchor_top_middle", img, 20 ) );
|
||||
}
|
||||
|
||||
void TestQgsCallout::calloutLabelAnchorBottomLeft()
|
||||
{
|
||||
QSize size( 640, 480 );
|
||||
QgsMapSettings mapSettings;
|
||||
mapSettings.setOutputSize( size );
|
||||
mapSettings.setExtent( vl->extent() );
|
||||
mapSettings.setLayers( QList<QgsMapLayer *>() << vl );
|
||||
mapSettings.setOutputDpi( 96 );
|
||||
mapSettings.setRotation( 45 );
|
||||
|
||||
QgsMapRendererSequentialJob job( mapSettings );
|
||||
job.start();
|
||||
job.waitForFinished();
|
||||
|
||||
QImage img = job.renderedImage();
|
||||
|
||||
QPainter p( &img );
|
||||
QgsRenderContext context = QgsRenderContext::fromMapSettings( mapSettings );
|
||||
context.setPainter( &p );
|
||||
|
||||
QgsPalLayerSettings settings;
|
||||
settings.fieldName = QStringLiteral( "Class" );
|
||||
settings.placement = QgsPalLayerSettings::AroundPoint;
|
||||
settings.dist = 10;
|
||||
|
||||
QgsTextFormat format;
|
||||
format.setFont( QgsFontUtils::getStandardTestFont( QStringLiteral( "Bold" ) ).family() );
|
||||
format.setSize( 12 );
|
||||
format.setNamedStyle( QStringLiteral( "Bold" ) );
|
||||
format.setColor( QColor( 200, 0, 200 ) );
|
||||
settings.setFormat( format );
|
||||
|
||||
QgsSimpleLineCallout *callout = new QgsSimpleLineCallout();
|
||||
callout->setEnabled( true );
|
||||
callout->lineSymbol()->setWidth( 1 );
|
||||
callout->setLabelAnchorPoint( QgsCallout::LabelBottomLeft );
|
||||
settings.setCallout( callout );
|
||||
|
||||
vl->setLabeling( new QgsVectorLayerSimpleLabeling( settings ) ); // TODO: this should not be necessary!
|
||||
vl->setLabelsEnabled( true );
|
||||
|
||||
QgsDefaultLabelingEngine engine;
|
||||
engine.setMapSettings( mapSettings );
|
||||
engine.addProvider( new QgsVectorLayerLabelProvider( vl, QString(), true, &settings ) );
|
||||
//engine.setFlags( QgsLabelingEngine::RenderOutlineLabels | QgsLabelingEngine::DrawLabelRectOnly );
|
||||
engine.run( context );
|
||||
|
||||
p.end();
|
||||
|
||||
QVERIFY( imageCheck( "callout_label_anchor_bottom_left", img, 20 ) );
|
||||
}
|
||||
|
||||
void TestQgsCallout::calloutLabelAnchorBottom()
|
||||
{
|
||||
QSize size( 640, 480 );
|
||||
QgsMapSettings mapSettings;
|
||||
mapSettings.setOutputSize( size );
|
||||
mapSettings.setExtent( vl->extent() );
|
||||
mapSettings.setLayers( QList<QgsMapLayer *>() << vl );
|
||||
mapSettings.setOutputDpi( 96 );
|
||||
mapSettings.setRotation( 45 );
|
||||
|
||||
QgsMapRendererSequentialJob job( mapSettings );
|
||||
job.start();
|
||||
job.waitForFinished();
|
||||
|
||||
QImage img = job.renderedImage();
|
||||
|
||||
QPainter p( &img );
|
||||
QgsRenderContext context = QgsRenderContext::fromMapSettings( mapSettings );
|
||||
context.setPainter( &p );
|
||||
|
||||
QgsPalLayerSettings settings;
|
||||
settings.fieldName = QStringLiteral( "Class" );
|
||||
settings.placement = QgsPalLayerSettings::AroundPoint;
|
||||
settings.dist = 10;
|
||||
|
||||
QgsTextFormat format;
|
||||
format.setFont( QgsFontUtils::getStandardTestFont( QStringLiteral( "Bold" ) ).family() );
|
||||
format.setSize( 12 );
|
||||
format.setNamedStyle( QStringLiteral( "Bold" ) );
|
||||
format.setColor( QColor( 200, 0, 200 ) );
|
||||
settings.setFormat( format );
|
||||
|
||||
QgsSimpleLineCallout *callout = new QgsSimpleLineCallout();
|
||||
callout->setEnabled( true );
|
||||
callout->lineSymbol()->setWidth( 1 );
|
||||
callout->setLabelAnchorPoint( QgsCallout::LabelBottomMiddle );
|
||||
settings.setCallout( callout );
|
||||
|
||||
vl->setLabeling( new QgsVectorLayerSimpleLabeling( settings ) ); // TODO: this should not be necessary!
|
||||
vl->setLabelsEnabled( true );
|
||||
|
||||
QgsDefaultLabelingEngine engine;
|
||||
engine.setMapSettings( mapSettings );
|
||||
engine.addProvider( new QgsVectorLayerLabelProvider( vl, QString(), true, &settings ) );
|
||||
//engine.setFlags( QgsLabelingEngine::RenderOutlineLabels | QgsLabelingEngine::DrawLabelRectOnly );
|
||||
engine.run( context );
|
||||
|
||||
p.end();
|
||||
|
||||
QVERIFY( imageCheck( "callout_label_anchor_bottom_middle", img, 20 ) );
|
||||
}
|
||||
|
||||
void TestQgsCallout::calloutLabelAnchorBottomRight()
|
||||
{
|
||||
QSize size( 640, 480 );
|
||||
QgsMapSettings mapSettings;
|
||||
mapSettings.setOutputSize( size );
|
||||
mapSettings.setExtent( vl->extent() );
|
||||
mapSettings.setLayers( QList<QgsMapLayer *>() << vl );
|
||||
mapSettings.setOutputDpi( 96 );
|
||||
mapSettings.setRotation( 45 );
|
||||
|
||||
QgsMapRendererSequentialJob job( mapSettings );
|
||||
job.start();
|
||||
job.waitForFinished();
|
||||
|
||||
QImage img = job.renderedImage();
|
||||
|
||||
QPainter p( &img );
|
||||
QgsRenderContext context = QgsRenderContext::fromMapSettings( mapSettings );
|
||||
context.setPainter( &p );
|
||||
|
||||
QgsPalLayerSettings settings;
|
||||
settings.fieldName = QStringLiteral( "Class" );
|
||||
settings.placement = QgsPalLayerSettings::AroundPoint;
|
||||
settings.dist = 10;
|
||||
|
||||
QgsTextFormat format;
|
||||
format.setFont( QgsFontUtils::getStandardTestFont( QStringLiteral( "Bold" ) ).family() );
|
||||
format.setSize( 12 );
|
||||
format.setNamedStyle( QStringLiteral( "Bold" ) );
|
||||
format.setColor( QColor( 200, 0, 200 ) );
|
||||
settings.setFormat( format );
|
||||
|
||||
QgsSimpleLineCallout *callout = new QgsSimpleLineCallout();
|
||||
callout->setEnabled( true );
|
||||
callout->lineSymbol()->setWidth( 1 );
|
||||
callout->setLabelAnchorPoint( QgsCallout::LabelBottomRight );
|
||||
settings.setCallout( callout );
|
||||
|
||||
vl->setLabeling( new QgsVectorLayerSimpleLabeling( settings ) ); // TODO: this should not be necessary!
|
||||
vl->setLabelsEnabled( true );
|
||||
|
||||
QgsDefaultLabelingEngine engine;
|
||||
engine.setMapSettings( mapSettings );
|
||||
engine.addProvider( new QgsVectorLayerLabelProvider( vl, QString(), true, &settings ) );
|
||||
//engine.setFlags( QgsLabelingEngine::RenderOutlineLabels | QgsLabelingEngine::DrawLabelRectOnly );
|
||||
engine.run( context );
|
||||
|
||||
p.end();
|
||||
|
||||
QVERIFY( imageCheck( "callout_label_anchor_bottom_right", img, 20 ) );
|
||||
}
|
||||
|
||||
void TestQgsCallout::calloutLabelAnchorLeft()
|
||||
{
|
||||
QSize size( 640, 480 );
|
||||
QgsMapSettings mapSettings;
|
||||
mapSettings.setOutputSize( size );
|
||||
mapSettings.setExtent( vl->extent() );
|
||||
mapSettings.setLayers( QList<QgsMapLayer *>() << vl );
|
||||
mapSettings.setOutputDpi( 96 );
|
||||
mapSettings.setRotation( 45 );
|
||||
|
||||
QgsMapRendererSequentialJob job( mapSettings );
|
||||
job.start();
|
||||
job.waitForFinished();
|
||||
|
||||
QImage img = job.renderedImage();
|
||||
|
||||
QPainter p( &img );
|
||||
QgsRenderContext context = QgsRenderContext::fromMapSettings( mapSettings );
|
||||
context.setPainter( &p );
|
||||
|
||||
QgsPalLayerSettings settings;
|
||||
settings.fieldName = QStringLiteral( "Class" );
|
||||
settings.placement = QgsPalLayerSettings::AroundPoint;
|
||||
settings.dist = 10;
|
||||
|
||||
QgsTextFormat format;
|
||||
format.setFont( QgsFontUtils::getStandardTestFont( QStringLiteral( "Bold" ) ).family() );
|
||||
format.setSize( 12 );
|
||||
format.setNamedStyle( QStringLiteral( "Bold" ) );
|
||||
format.setColor( QColor( 200, 0, 200 ) );
|
||||
settings.setFormat( format );
|
||||
|
||||
QgsSimpleLineCallout *callout = new QgsSimpleLineCallout();
|
||||
callout->setEnabled( true );
|
||||
callout->lineSymbol()->setWidth( 1 );
|
||||
callout->setLabelAnchorPoint( QgsCallout::LabelMiddleLeft );
|
||||
settings.setCallout( callout );
|
||||
|
||||
vl->setLabeling( new QgsVectorLayerSimpleLabeling( settings ) ); // TODO: this should not be necessary!
|
||||
vl->setLabelsEnabled( true );
|
||||
|
||||
QgsDefaultLabelingEngine engine;
|
||||
engine.setMapSettings( mapSettings );
|
||||
engine.addProvider( new QgsVectorLayerLabelProvider( vl, QString(), true, &settings ) );
|
||||
//engine.setFlags( QgsLabelingEngine::RenderOutlineLabels | QgsLabelingEngine::DrawLabelRectOnly );
|
||||
engine.run( context );
|
||||
|
||||
p.end();
|
||||
|
||||
QVERIFY( imageCheck( "callout_label_anchor_left", img, 20 ) );
|
||||
}
|
||||
|
||||
void TestQgsCallout::calloutLabelAnchorRight()
|
||||
{
|
||||
QSize size( 640, 480 );
|
||||
QgsMapSettings mapSettings;
|
||||
mapSettings.setOutputSize( size );
|
||||
mapSettings.setExtent( vl->extent() );
|
||||
mapSettings.setLayers( QList<QgsMapLayer *>() << vl );
|
||||
mapSettings.setOutputDpi( 96 );
|
||||
mapSettings.setRotation( 45 );
|
||||
|
||||
QgsMapRendererSequentialJob job( mapSettings );
|
||||
job.start();
|
||||
job.waitForFinished();
|
||||
|
||||
QImage img = job.renderedImage();
|
||||
|
||||
QPainter p( &img );
|
||||
QgsRenderContext context = QgsRenderContext::fromMapSettings( mapSettings );
|
||||
context.setPainter( &p );
|
||||
|
||||
QgsPalLayerSettings settings;
|
||||
settings.fieldName = QStringLiteral( "Class" );
|
||||
settings.placement = QgsPalLayerSettings::AroundPoint;
|
||||
settings.dist = 10;
|
||||
|
||||
QgsTextFormat format;
|
||||
format.setFont( QgsFontUtils::getStandardTestFont( QStringLiteral( "Bold" ) ).family() );
|
||||
format.setSize( 12 );
|
||||
format.setNamedStyle( QStringLiteral( "Bold" ) );
|
||||
format.setColor( QColor( 200, 0, 200 ) );
|
||||
settings.setFormat( format );
|
||||
|
||||
QgsSimpleLineCallout *callout = new QgsSimpleLineCallout();
|
||||
callout->setEnabled( true );
|
||||
callout->lineSymbol()->setWidth( 1 );
|
||||
callout->setLabelAnchorPoint( QgsCallout::LabelMiddleRight );
|
||||
settings.setCallout( callout );
|
||||
|
||||
vl->setLabeling( new QgsVectorLayerSimpleLabeling( settings ) ); // TODO: this should not be necessary!
|
||||
vl->setLabelsEnabled( true );
|
||||
|
||||
QgsDefaultLabelingEngine engine;
|
||||
engine.setMapSettings( mapSettings );
|
||||
engine.addProvider( new QgsVectorLayerLabelProvider( vl, QString(), true, &settings ) );
|
||||
//engine.setFlags( QgsLabelingEngine::RenderOutlineLabels | QgsLabelingEngine::DrawLabelRectOnly );
|
||||
engine.run( context );
|
||||
|
||||
p.end();
|
||||
|
||||
QVERIFY( imageCheck( "callout_label_anchor_right", img, 20 ) );
|
||||
}
|
||||
|
||||
void TestQgsCallout::calloutLabelAnchorCentroid()
|
||||
{
|
||||
QSize size( 640, 480 );
|
||||
QgsMapSettings mapSettings;
|
||||
mapSettings.setOutputSize( size );
|
||||
mapSettings.setExtent( vl->extent() );
|
||||
mapSettings.setLayers( QList<QgsMapLayer *>() << vl );
|
||||
mapSettings.setOutputDpi( 96 );
|
||||
mapSettings.setRotation( 45 );
|
||||
|
||||
QgsMapRendererSequentialJob job( mapSettings );
|
||||
job.start();
|
||||
job.waitForFinished();
|
||||
|
||||
QImage img = job.renderedImage();
|
||||
|
||||
QPainter p( &img );
|
||||
QgsRenderContext context = QgsRenderContext::fromMapSettings( mapSettings );
|
||||
context.setPainter( &p );
|
||||
|
||||
QgsPalLayerSettings settings;
|
||||
settings.fieldName = QStringLiteral( "Class" );
|
||||
settings.placement = QgsPalLayerSettings::AroundPoint;
|
||||
settings.dist = 10;
|
||||
|
||||
QgsTextFormat format;
|
||||
format.setFont( QgsFontUtils::getStandardTestFont( QStringLiteral( "Bold" ) ).family() );
|
||||
format.setSize( 12 );
|
||||
format.setNamedStyle( QStringLiteral( "Bold" ) );
|
||||
format.setColor( QColor( 200, 0, 200 ) );
|
||||
settings.setFormat( format );
|
||||
|
||||
QgsSimpleLineCallout *callout = new QgsSimpleLineCallout();
|
||||
callout->setEnabled( true );
|
||||
callout->lineSymbol()->setWidth( 1 );
|
||||
callout->setLabelAnchorPoint( QgsCallout::LabelCentroid );
|
||||
settings.setCallout( callout );
|
||||
|
||||
vl->setLabeling( new QgsVectorLayerSimpleLabeling( settings ) ); // TODO: this should not be necessary!
|
||||
vl->setLabelsEnabled( true );
|
||||
|
||||
QgsDefaultLabelingEngine engine;
|
||||
engine.setMapSettings( mapSettings );
|
||||
engine.addProvider( new QgsVectorLayerLabelProvider( vl, QString(), true, &settings ) );
|
||||
//engine.setFlags( QgsLabelingEngine::RenderOutlineLabels | QgsLabelingEngine::DrawLabelRectOnly );
|
||||
engine.run( context );
|
||||
|
||||
p.end();
|
||||
|
||||
QVERIFY( imageCheck( "callout_label_anchor_centroid", img, 20 ) );
|
||||
}
|
||||
|
||||
void TestQgsCallout::calloutLabelDataDefinedAnchor()
|
||||
{
|
||||
QSize size( 640, 480 );
|
||||
QgsMapSettings mapSettings;
|
||||
mapSettings.setOutputSize( size );
|
||||
mapSettings.setExtent( vl->extent() );
|
||||
mapSettings.setLayers( QList<QgsMapLayer *>() << vl );
|
||||
mapSettings.setOutputDpi( 96 );
|
||||
mapSettings.setRotation( 45 );
|
||||
|
||||
QgsMapRendererSequentialJob job( mapSettings );
|
||||
job.start();
|
||||
job.waitForFinished();
|
||||
|
||||
QImage img = job.renderedImage();
|
||||
|
||||
QPainter p( &img );
|
||||
QgsRenderContext context = QgsRenderContext::fromMapSettings( mapSettings );
|
||||
context.setPainter( &p );
|
||||
|
||||
QgsPalLayerSettings settings;
|
||||
settings.fieldName = QStringLiteral( "Class" );
|
||||
settings.placement = QgsPalLayerSettings::AroundPoint;
|
||||
settings.dist = 10;
|
||||
|
||||
QgsTextFormat format;
|
||||
format.setFont( QgsFontUtils::getStandardTestFont( QStringLiteral( "Bold" ) ).family() );
|
||||
format.setSize( 12 );
|
||||
format.setNamedStyle( QStringLiteral( "Bold" ) );
|
||||
format.setColor( QColor( 200, 0, 200 ) );
|
||||
settings.setFormat( format );
|
||||
|
||||
QgsSimpleLineCallout *callout = new QgsSimpleLineCallout();
|
||||
callout->setEnabled( true );
|
||||
callout->lineSymbol()->setWidth( 1 );
|
||||
callout->setLabelAnchorPoint( QgsCallout::LabelCentroid );
|
||||
callout->dataDefinedProperties().setProperty( QgsCallout::LabelAnchorPointPosition, QgsProperty::fromExpression( QStringLiteral( "'TL'" ) ) );
|
||||
settings.setCallout( callout );
|
||||
|
||||
vl->setLabeling( new QgsVectorLayerSimpleLabeling( settings ) ); // TODO: this should not be necessary!
|
||||
vl->setLabelsEnabled( true );
|
||||
|
||||
QgsDefaultLabelingEngine engine;
|
||||
engine.setMapSettings( mapSettings );
|
||||
engine.addProvider( new QgsVectorLayerLabelProvider( vl, QString(), true, &settings ) );
|
||||
//engine.setFlags( QgsLabelingEngine::RenderOutlineLabels | QgsLabelingEngine::DrawLabelRectOnly );
|
||||
engine.run( context );
|
||||
|
||||
p.end();
|
||||
|
||||
QVERIFY( imageCheck( "callout_label_datadefined_anchor", img, 20 ) );
|
||||
}
|
||||
|
||||
void TestQgsCallout::calloutBehindLabel()
|
||||
{
|
||||
// test that callouts are rendered below labels
|
||||
|
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 30 KiB |