switch to XY coordinate types when using label move tool

This commit is contained in:
Damiano Lombardi 2021-09-06 15:35:10 +02:00
parent fd1b819243
commit 9f7353b91c
20 changed files with 440 additions and 163 deletions

View File

@ -0,0 +1,7 @@
# The following has been generated automatically from src/core/labeling/qgslabeling.h
# monkey patching scoped based enum
QgsLabeling.CoordinateType.XY.__doc__ = "Coordinate defined by X and Y values"
QgsLabeling.CoordinateType.Point.__doc__ = "Coordinate defined by a point"
QgsLabeling.CoordinateType.__doc__ = 'Types of coordinate definitions\n\n.. versionadded:: 3.22\n\n' + '* ``XY``: ' + QgsLabeling.CoordinateType.XY.__doc__ + '\n' + '* ``Point``: ' + QgsLabeling.CoordinateType.Point.__doc__
# --
QgsLabeling.CoordinateType.baseClass = QgsLabeling

View File

@ -20,6 +20,9 @@ Contains constants and enums relating to labeling.
%TypeHeaderCode
#include "qgslabeling.h"
%End
public:
static const QMetaObject staticMetaObject;
public:
enum LinePlacementFlag
@ -40,6 +43,12 @@ Contains constants and enums relating to labeling.
typedef QFlags<QgsLabeling::PolygonPlacementFlag> PolygonPlacementFlags;
enum class CoordinateType
{
XY,
Point
};
};
QFlags<QgsLabeling::LinePlacementFlag> operator|(QgsLabeling::LinePlacementFlag f1, QFlags<QgsLabeling::LinePlacementFlag> f2);

View File

@ -218,6 +218,7 @@ Contains settings for how a map layer will be labeled.
// (data defined only)
PositionX,
PositionY,
PositionPoint,
Hali,
Vali,
Rotation,
@ -419,6 +420,24 @@ Set unit for rotation of labels.
int priority;
QgsLabeling::CoordinateType placementCoordinateType() const;
%Docstring
Coordinates type for data defined placement.
.. seealso:: :py:func:`setPlacementCoordinateType`
.. versionadded:: 3.22
%End
void setPlacementCoordinateType( QgsLabeling::CoordinateType placementCoordinateType );
%Docstring
Set coordinates type for data defined placement.
.. seealso:: :py:func:`placementCoordinateType`
.. versionadded:: 3.22
%End
bool scaleVisibility;

View File

@ -11,7 +11,7 @@
class QgsAbstractVectorLayerLabeling
class QgsAbstractVectorLayerLabeling : QObject
{
%Docstring(signature="appended")
Abstract base class - its implementations define different approaches to the labeling of a vector layer.
@ -112,6 +112,10 @@ Returns the default layer settings to use for the specified vector ``layer``.
.. versionadded:: 3.20
%End
signals:
void labelingChanged();
protected:
virtual void writeTextSymbolizer( QDomNode &parent, QgsPalLayerSettings &settings, const QVariantMap &props ) const;

View File

@ -145,11 +145,9 @@ Sets the background color for the text preview widget.
:param color: background color
%End
void enableDataDefinedAlignment( bool enable );
void updateDataDefinedAlignment();
%Docstring
Controls whether data defined alignment buttons are enabled.
:param enable: set to ``True`` to enable alignment controls
Update the enabled state of the data defined alignment buttons.
%End
virtual QgsExpressionContext createExpressionContext() const;

View File

@ -354,6 +354,14 @@ void QgsMapToolMoveLabel::cadCanvasPressEvent( QgsMapMouseEvent *e )
}
}
if ( !isCalloutMove
&& vlayer->labeling()->settings().placementCoordinateType() != QgsLabeling::CoordinateType::XY )
{
QgsPalLayerSettings *settings = new QgsPalLayerSettings( mCurrentLabel.settings );
settings->setPlacementCoordinateType( QgsLabeling::CoordinateType::XY );
vlayer->labeling()->setSettings( settings );
}
bool success = vlayer->changeAttributeValue( featureId, xCol, xNewPos );
success = vlayer->changeAttributeValue( featureId, yCol, yNewPos ) && success;

View File

@ -18,6 +18,7 @@
#include "qgis_core.h"
#include "qgis_sip.h"
#include <QObject>
#include <QFlags>
/**
@ -30,6 +31,8 @@
*/
class CORE_EXPORT QgsLabeling
{
Q_GADGET
public:
/**
@ -56,6 +59,18 @@ class CORE_EXPORT QgsLabeling
};
Q_DECLARE_FLAGS( PolygonPlacementFlags, PolygonPlacementFlag )
/**
* Types of coordinate definitions
*
* \since QGIS 3.22
*/
enum class CoordinateType : int
{
XY, //!< Coordinate defined by X and Y values
Point //!< Coordinate defined by a point
};
Q_ENUM( CoordinateType )
};
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsLabeling::LinePlacementFlags )

View File

@ -244,6 +244,7 @@ void QgsPalLayerSettings::initPropertyDefinitions()
{ QgsPalLayerSettings::PolygonLabelOutside, QgsPropertyDefinition( "PolygonLabelOutside", QgsPropertyDefinition::DataTypeString, QObject::tr( "Label outside polygons" ), QObject::tr( "string " ) + "[<b>yes</b> (allow placing outside)|<b>no</b> (never place outside)|<b>force</b> (always place outside)]", origin ) },
{ QgsPalLayerSettings::PositionX, QgsPropertyDefinition( "PositionX", QObject::tr( "Position (X)" ), QgsPropertyDefinition::Double, origin ) },
{ QgsPalLayerSettings::PositionY, QgsPropertyDefinition( "PositionY", QObject::tr( "Position (Y)" ), QgsPropertyDefinition::Double, origin ) },
{ QgsPalLayerSettings::PositionPoint, QgsPropertyDefinition( "PositionPoint", QObject::tr( "Position (point)" ), QgsPropertyDefinition::Point, origin ) },
{ QgsPalLayerSettings::Hali, QgsPropertyDefinition( "Hali", QgsPropertyDefinition::DataTypeString, QObject::tr( "Horizontal alignment" ), QObject::tr( "string " ) + "[<b>Left</b>|<b>Center</b>|<b>Right</b>]", origin ) },
{
QgsPalLayerSettings::Vali, QgsPropertyDefinition( "Vali", QgsPropertyDefinition::DataTypeString, QObject::tr( "Vertical alignment" ), QObject::tr( "string " ) + QStringLiteral( "[<b>Bottom</b>|<b>Base</b>|<br>"
@ -340,6 +341,7 @@ QgsPalLayerSettings &QgsPalLayerSettings::operator=( const QgsPalLayerSettings &
repeatDistance = s.repeatDistance;
repeatDistanceUnit = s.repeatDistanceUnit;
repeatDistanceMapUnitScale = s.repeatDistanceMapUnitScale;
mPlacementCoordinateType = s.mPlacementCoordinateType;
// rendering
scaleVisibility = s.scaleVisibility;
@ -618,6 +620,16 @@ void QgsPalLayerSettings::setRotationUnit( QgsUnitTypes::AngleUnit angleUnit )
mRotationUnit = angleUnit;
}
QgsLabeling::CoordinateType QgsPalLayerSettings::placementCoordinateType() const
{
return mPlacementCoordinateType;
}
void QgsPalLayerSettings::setPlacementCoordinateType( QgsLabeling::CoordinateType placementCoordinateType )
{
mPlacementCoordinateType = placementCoordinateType;
}
QString updateDataDefinedString( const QString &value )
{
// TODO: update or remove this when project settings for labeling are migrated to better XML layout
@ -841,6 +853,7 @@ void QgsPalLayerSettings::readFromLayerCustomProperties( QgsVectorLayer *layer )
{
repeatDistanceMapUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( layer->customProperty( QStringLiteral( "labeling/repeatDistanceMapUnitScale" ) ).toString() );
}
mPlacementCoordinateType = layer->customEnumProperty( QStringLiteral( "labeling/placementCoordinateType" ), QgsLabeling::CoordinateType::XY );
// rendering
double scalemn = layer->customProperty( QStringLiteral( "labeling/scaleMin" ), QVariant( 0 ) ).toDouble();
@ -1091,6 +1104,8 @@ void QgsPalLayerSettings::readXml( const QDomElement &elem, const QgsReadWriteCo
layerType = qgsEnumKeyToValue( placementElem.attribute( QStringLiteral( "layerType" ) ), QgsWkbTypes::UnknownGeometry );
mPlacementCoordinateType = qgsEnumKeyToValue( placementElem.attribute( QStringLiteral( "coordinateType" ), qgsEnumValueToKey( QgsLabeling::CoordinateType::XY ) ), QgsLabeling::CoordinateType::XY );
// rendering
QDomElement renderingElem = elem.firstChildElement( QStringLiteral( "rendering" ) );
@ -1248,6 +1263,8 @@ QDomElement QgsPalLayerSettings::writeXml( QDomDocument &doc, const QgsReadWrite
placementElem.setAttribute( QStringLiteral( "layerType" ), metaEnum.valueToKey( layerType ) );
placementElem.setAttribute( QStringLiteral( "coordinateType" ), qgsEnumValueToKey( mPlacementCoordinateType ) );
// rendering
QDomElement renderingElem = doc.createElement( QStringLiteral( "rendering" ) );
renderingElem.setAttribute( QStringLiteral( "drawLabels" ), drawLabels );
@ -2229,11 +2246,9 @@ std::unique_ptr<QgsLabelFeature> QgsPalLayerSettings::registerFeatureWithDetails
}
//data defined position / alignment / rotation?
bool hasDataDefinedPosition = false;
bool layerDefinedRotation = false;
bool dataDefinedRotation = false;
double xPos = 0.0, yPos = 0.0, angle = 0.0;
bool ddXPos = false, ddYPos = false;
double quadOffsetX = 0.0, quadOffsetY = 0.0;
double offsetX = 0.0, offsetY = 0.0;
QgsPointXY anchorPosition;
@ -2378,119 +2393,158 @@ std::unique_ptr<QgsLabelFeature> QgsPalLayerSettings::registerFeatureWithDetails
}
}
if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::PositionX ) )
bool hasDataDefinedPosition = false;
{
exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::PositionX, context.expressionContext() );
if ( !exprVal.isNull() )
bool ddPosition = false;
switch ( mPlacementCoordinateType )
{
if ( !exprVal.isNull() )
xPos = exprVal.toDouble( &ddXPos );
if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::PositionY ) )
case QgsLabeling::CoordinateType::XY:
{
exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::PositionY, context.expressionContext() );
if ( !exprVal.isNull() )
if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::PositionX )
&& mDataDefinedProperties.isActive( QgsPalLayerSettings::PositionY )
&& !mDataDefinedProperties.value( QgsPalLayerSettings::PositionX, context.expressionContext() ).isNull()
&& !mDataDefinedProperties.value( QgsPalLayerSettings::PositionY, context.expressionContext() ).isNull() )
{
//data defined position. But field values could be NULL -> positions will be generated by PAL
if ( !exprVal.isNull() )
yPos = exprVal.toDouble( &ddYPos );
ddPosition = true;
bool ddXPos = false, ddYPos = false;
xPos = mDataDefinedProperties.value( QgsPalLayerSettings::PositionX, context.expressionContext() ).toDouble( &ddXPos );
yPos = mDataDefinedProperties.value( QgsPalLayerSettings::PositionY, context.expressionContext() ).toDouble( &ddYPos );
if ( ddXPos && ddYPos )
hasDataDefinedPosition = true;
}
}
break;
case QgsLabeling::CoordinateType::Point:
{
if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::PositionPoint )
&& !mDataDefinedProperties.value( QgsPalLayerSettings::PositionPoint, context.expressionContext() ).isNull() )
{
ddPosition = true;
QVariant pointAsVariant = mDataDefinedProperties.value( QgsPalLayerSettings::PositionPoint, context.expressionContext() );
if ( pointAsVariant.canConvert<QgsGeometry>() )
{
hasDataDefinedPosition = true;
// layer rotation set, but don't rotate pinned labels unless data defined
if ( layerDefinedRotation && !dataDefinedRotation )
{
angle = 0.0;
}
//horizontal alignment
if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::Hali ) )
QgsGeometry geometryPoint = pointAsVariant.value<QgsGeometry>();
const QgsPoint *point = qgsgeometry_cast<QgsPoint *>( geometryPoint.constGet() );
// QgsPoint point = geometryPoint.constGet()->vertexAt( vId );
xPos = point->x();
yPos = point->y();
}
}
}
break;
}
if ( ddPosition )
{
//data defined position. But field values could be NULL -> positions will be generated by PAL
if ( hasDataDefinedPosition )
{
// layer rotation set, but don't rotate pinned labels unless data defined
if ( layerDefinedRotation && !dataDefinedRotation )
{
angle = 0.0;
}
//horizontal alignment
if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::Hali ) )
{
exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::Hali, context.expressionContext() );
if ( !exprVal.isNull() )
{
QString haliString = exprVal.toString();
if ( haliString.compare( QLatin1String( "Center" ), Qt::CaseInsensitive ) == 0 )
{
exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::Hali, context.expressionContext() );
if ( !exprVal.isNull() )
xdiff -= labelX / 2.0;
}
else if ( haliString.compare( QLatin1String( "Right" ), Qt::CaseInsensitive ) == 0 )
{
xdiff -= labelX;
}
}
}
//vertical alignment
if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::Vali ) )
{
exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::Vali, context.expressionContext() );
if ( !exprVal.isNull() )
{
QString valiString = exprVal.toString();
if ( valiString.compare( QLatin1String( "Bottom" ), Qt::CaseInsensitive ) != 0 )
{
if ( valiString.compare( QLatin1String( "Top" ), Qt::CaseInsensitive ) == 0 )
{
QString haliString = exprVal.toString();
if ( haliString.compare( QLatin1String( "Center" ), Qt::CaseInsensitive ) == 0 )
{
xdiff -= labelX / 2.0;
}
else if ( haliString.compare( QLatin1String( "Right" ), Qt::CaseInsensitive ) == 0 )
{
xdiff -= labelX;
}
}
}
//vertical alignment
if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::Vali ) )
{
exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::Vali, context.expressionContext() );
if ( !exprVal.isNull() )
{
QString valiString = exprVal.toString();
if ( valiString.compare( QLatin1String( "Bottom" ), Qt::CaseInsensitive ) != 0 )
{
if ( valiString.compare( QLatin1String( "Top" ), Qt::CaseInsensitive ) == 0 )
{
ydiff -= labelY;
}
else
{
double descentRatio = labelFontMetrics->descent() / labelFontMetrics->height();
if ( valiString.compare( QLatin1String( "Base" ), Qt::CaseInsensitive ) == 0 )
{
ydiff -= labelY * descentRatio;
}
else //'Cap' or 'Half'
{
double capHeightRatio = ( labelFontMetrics->boundingRect( 'H' ).height() + 1 + labelFontMetrics->descent() ) / labelFontMetrics->height();
ydiff -= labelY * capHeightRatio;
if ( valiString.compare( QLatin1String( "Half" ), Qt::CaseInsensitive ) == 0 )
{
ydiff += labelY * ( capHeightRatio - descentRatio ) / 2.0;
}
}
}
}
}
}
if ( dataDefinedRotation )
{
//adjust xdiff and ydiff because the hali/vali point needs to be the rotation center
double xd = xdiff * std::cos( angle ) - ydiff * std::sin( angle );
double yd = xdiff * std::sin( angle ) + ydiff * std::cos( angle );
xdiff = xd;
ydiff = yd;
}
//project xPos and yPos from layer to map CRS, handle rotation
QgsGeometry ddPoint( new QgsPoint( xPos, yPos ) );
if ( QgsPalLabeling::geometryRequiresPreparation( ddPoint, context, ct ) )
{
ddPoint = QgsPalLabeling::prepareGeometry( ddPoint, context, ct );
if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( ddPoint.constGet() ) )
{
xPos = point->x();
yPos = point->y();
anchorPosition = QgsPointXY( xPos, yPos );
ydiff -= labelY;
}
else
{
QgsMessageLog::logMessage( QObject::tr( "Invalid data defined label position (%1, %2)" ).arg( xPos ).arg( yPos ), QObject::tr( "Labeling" ) );
hasDataDefinedPosition = false;
double descentRatio = labelFontMetrics->descent() / labelFontMetrics->height();
if ( valiString.compare( QLatin1String( "Base" ), Qt::CaseInsensitive ) == 0 )
{
ydiff -= labelY * descentRatio;
}
else //'Cap' or 'Half'
{
double capHeightRatio = ( labelFontMetrics->boundingRect( 'H' ).height() + 1 + labelFontMetrics->descent() ) / labelFontMetrics->height();
ydiff -= labelY * capHeightRatio;
if ( valiString.compare( QLatin1String( "Half" ), Qt::CaseInsensitive ) == 0 )
{
ydiff += labelY * ( capHeightRatio - descentRatio ) / 2.0;
}
}
}
}
else
{
anchorPosition = QgsPointXY( xPos, yPos );
}
xPos += xdiff;
yPos += ydiff;
}
}
if ( dataDefinedRotation )
{
//adjust xdiff and ydiff because the hali/vali point needs to be the rotation center
double xd = xdiff * std::cos( angle ) - ydiff * std::sin( angle );
double yd = xdiff * std::sin( angle ) + ydiff * std::cos( angle );
xdiff = xd;
ydiff = yd;
}
//project xPos and yPos from layer to map CRS, handle rotation
QgsGeometry ddPoint( new QgsPoint( xPos, yPos ) );
if ( QgsPalLabeling::geometryRequiresPreparation( ddPoint, context, ct ) )
{
ddPoint = QgsPalLabeling::prepareGeometry( ddPoint, context, ct );
if ( const QgsPoint *point = qgsgeometry_cast< const QgsPoint * >( ddPoint.constGet() ) )
{
xPos = point->x();
yPos = point->y();
anchorPosition = QgsPointXY( xPos, yPos );
}
else
{
QgsMessageLog::logMessage( QObject::tr( "Invalid data defined label position (%1, %2)" ).arg( xPos ).arg( yPos ), QObject::tr( "Labeling" ) );
hasDataDefinedPosition = false;
}
}
else
{
anchorPosition = QgsPointXY( xPos, yPos );
}
xPos += xdiff;
yPos += ydiff;
}
else
{
anchorPosition = QgsPointXY( xPos, yPos );
// only rotate non-pinned OverPoint placements until other placements are supported in pal::Feature
if ( dataDefinedRotation && placement != QgsPalLayerSettings::OverPoint )
{
angle = 0.0;
}
}
}
}

View File

@ -321,6 +321,7 @@ class CORE_EXPORT QgsPalLayerSettings
// (data defined only)
PositionX = 9, //!< X-coordinate data defined label position
PositionY = 10, //!< Y-coordinate data defined label position
PositionPoint = 79, //!< Point-coordinate data defined label position
Hali = 11, //!< Horizontal alignment for data defined label position (Left, Center, Right)
Vali = 12, //!< Vertical alignment for data defined label position (Bottom, Base, Half, Cap, Top)
Rotation = 14, //!< Label rotation (deprecated, for old project compatibility only)
@ -667,6 +668,20 @@ class CORE_EXPORT QgsPalLayerSettings
*/
int priority = 5;
/**
* Coordinates type for data defined placement.
* \see setPlacementCoordinateType()
* \since QGIS 3.22
*/
QgsLabeling::CoordinateType placementCoordinateType() const;
/**
* Set coordinates type for data defined placement.
* \see placementCoordinateType()
* \since QGIS 3.22
*/
void setPlacementCoordinateType( QgsLabeling::CoordinateType placementCoordinateType );
//-- rendering
/**
@ -1150,6 +1165,8 @@ class CORE_EXPORT QgsPalLayerSettings
//! Unit for rotation of labels.
QgsUnitTypes::AngleUnit mRotationUnit = QgsUnitTypes::AngleDegrees;
QgsLabeling::CoordinateType mPlacementCoordinateType = QgsLabeling::CoordinateType::XY;
static void initPropertyDefinitions();
};

View File

@ -535,6 +535,8 @@ void QgsRuleBasedLabeling::setSettings( QgsPalLayerSettings *settings, const QSt
if ( rule && rule->settings() )
rule->setSettings( settings );
}
emit labelingChanged();
}
void QgsRuleBasedLabeling::toSld( QDomNode &parent, const QVariantMap &props ) const

View File

@ -621,4 +621,6 @@ void QgsVectorLayerSimpleLabeling::setSettings( QgsPalLayerSettings *settings, c
return;
mSettings.reset( settings );
emit labelingChanged();
}

View File

@ -38,8 +38,9 @@ class QgsStyleEntityVisitorInterface;
*
* \since QGIS 3.0
*/
class CORE_EXPORT QgsAbstractVectorLayerLabeling
class CORE_EXPORT QgsAbstractVectorLayerLabeling : public QObject
{
Q_OBJECT
#ifdef SIP_RUN
SIP_CONVERT_TO_SUBCLASS_CODE
@ -133,6 +134,10 @@ class CORE_EXPORT QgsAbstractVectorLayerLabeling
*/
static QgsPalLayerSettings defaultSettingsForLayer( const QgsVectorLayer *layer );
signals:
void labelingChanged();
protected:
/**

View File

@ -253,6 +253,9 @@ QgsLabelingGui::QgsLabelingGui( QgsVectorLayer *layer, QgsMapCanvas *mapCanvas,
mCoordRotationUnitComboBox->addItem( QgsUnitTypes::toString( QgsUnitTypes::AngleMilliradiansSI ), QgsUnitTypes::AngleMilliradiansSI );
mCoordRotationUnitComboBox->addItem( QgsUnitTypes::toString( QgsUnitTypes::AngleMilNATO ), QgsUnitTypes::AngleMilNATO );
mCoordTypeComboBox->addItem( tr( "X/Y" ), static_cast< int >( QgsLabeling::CoordinateType::XY ) );
mCoordTypeComboBox->addItem( tr( "Point" ), static_cast< int >( QgsLabeling::CoordinateType::Point ) );
// connections for groupboxes with separate activation checkboxes (that need to honor data defined setting)
connect( mBufferDrawChkBx, &QAbstractButton::toggled, this, &QgsLabelingGui::updateUi );
connect( mBufferDrawDDBtn, &QgsPropertyOverrideButton::changed, this, &QgsLabelingGui::updateUi );
@ -432,6 +435,10 @@ void QgsLabelingGui::setLayer( QgsMapLayer *mapLayer )
mFontMultiLineAlignComboBox->setCurrentIndex( 0 );
}
mCoordTypeComboBox->setCurrentIndex( 0 );
if ( mCoordTypeComboBox->findData( static_cast< int >( mSettings.placementCoordinateType() ) ) >= 0 )
mCoordTypeComboBox->setCurrentIndex( mCoordTypeComboBox->findData( static_cast< int >( mSettings.placementCoordinateType() ) ) );
chkPreserveRotation->setChecked( mSettings.preserveRotation );
mCoordRotationUnitComboBox->setCurrentIndex( 0 );
@ -482,7 +489,8 @@ void QgsLabelingGui::setLayer( QgsMapLayer *mapLayer )
// do this after other widgets are configured, so they can be enabled/disabled
populateDataDefinedButtons();
enableDataDefinedAlignment( mCoordXDDBtn->isActive() && mCoordYDDBtn->isActive() );
updateDataDefinedAlignment();
updateUi(); // should come after data defined button setup
}
@ -624,6 +632,7 @@ QgsPalLayerSettings QgsLabelingGui::layerSettings()
lyr.geometryGenerator = mGeometryGenerator->text();
lyr.geometryGeneratorType = mGeometryGeneratorType->currentData().value<QgsWkbTypes::GeometryType>();
lyr.geometryGeneratorEnabled = mGeometryGeneratorGroupBox->isChecked();
lyr.setPlacementCoordinateType( static_cast< QgsLabeling::CoordinateType >( mCoordTypeComboBox->currentData().toInt() ) );
lyr.layerType = mLayer ? mLayer->geometryType() : mGeomType;

View File

@ -90,6 +90,8 @@ void QgsLabelingWidget::setLayer( QgsMapLayer *mapLayer )
mOldLabelsEnabled = mLayer->labelsEnabled();
adaptToLayer();
connect( mLayer->labeling(), &QgsAbstractVectorLayerLabeling::labelingChanged, this, &QgsLabelingWidget::layerLabelingChanged );
}
void QgsLabelingWidget::adaptToLayer()
@ -300,3 +302,8 @@ void QgsLabelingWidget::showEngineConfigDialog()
activateWindow();
}
}
void QgsLabelingWidget::layerLabelingChanged()
{
adaptToLayer();
}

View File

@ -77,6 +77,7 @@ class GUI_EXPORT QgsLabelingWidget : public QgsMapLayerConfigWidget, private Ui:
private slots:
void labelModeChanged( int index );
void showEngineConfigDialog();
void layerLabelingChanged();
private:

View File

@ -88,6 +88,8 @@ void QgsTextFormatWidget::initWidget()
connect( mMaskBufferUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsTextFormatWidget::mMaskBufferUnitWidget_changed );
connect( mCoordXDDBtn, &QgsPropertyOverrideButton::changed, this, &QgsTextFormatWidget::mCoordXDDBtn_changed );
connect( mCoordYDDBtn, &QgsPropertyOverrideButton::changed, this, &QgsTextFormatWidget::mCoordYDDBtn_changed );
connect( mCoordPointDDBtn, &QgsPropertyOverrideButton::changed, this, &QgsTextFormatWidget::mCoordPointDDBtn_changed );
connect( mCoordTypeComboBox, qOverload<int>( &QComboBox::currentIndexChanged ), this, &QgsTextFormatWidget::mCoordTypeComboBox_currentIndexChanged );
connect( mShapeTypeCmbBx, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsTextFormatWidget::mShapeTypeCmbBx_currentIndexChanged );
connect( mShapeRotationCmbBx, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsTextFormatWidget::mShapeRotationCmbBx_currentIndexChanged );
connect( mShapeSVGParamsBtn, &QPushButton::clicked, this, &QgsTextFormatWidget::mShapeSVGParamsBtn_clicked );
@ -358,6 +360,7 @@ void QgsTextFormatWidget::initWidget()
<< mCentroidInsideCheckBox
<< mChkNoObstacle
<< mCoordRotationUnitComboBox
<< mCoordTypeComboBox
<< mDirectSymbChkBx
<< mDirectSymbLeftLineEdit
<< mDirectSymbRevChkBx
@ -804,6 +807,7 @@ void QgsTextFormatWidget::populateDataDefinedButtons()
// data defined-only
registerDataDefinedButton( mCoordXDDBtn, QgsPalLayerSettings::PositionX );
registerDataDefinedButton( mCoordYDDBtn, QgsPalLayerSettings::PositionY );
registerDataDefinedButton( mCoordPointDDBtn, QgsPalLayerSettings::PositionPoint );
registerDataDefinedButton( mCoordAlignmentHDDBtn, QgsPalLayerSettings::Hali );
registerDataDefinedButton( mCoordAlignmentVDDBtn, QgsPalLayerSettings::Vali );
registerDataDefinedButton( mCoordRotationDDBtn, QgsPalLayerSettings::LabelRotation );
@ -1572,28 +1576,34 @@ void QgsTextFormatWidget::mMaskBufferUnitWidget_changed()
updateFont( mRefFont );
}
void QgsTextFormatWidget::mCoordXDDBtn_changed( )
void QgsTextFormatWidget::mCoordXDDBtn_changed()
{
if ( !mCoordXDDBtn->isActive() ) //no data defined alignment without data defined position
{
enableDataDefinedAlignment( false );
}
else if ( mCoordYDDBtn->isActive() )
{
enableDataDefinedAlignment( true );
}
updateDataDefinedAlignment();
}
void QgsTextFormatWidget::mCoordYDDBtn_changed( )
void QgsTextFormatWidget::mCoordYDDBtn_changed()
{
if ( !mCoordYDDBtn->isActive() ) //no data defined alignment without data defined position
updateDataDefinedAlignment();
}
void QgsTextFormatWidget::mCoordPointDDBtn_changed()
{
updateDataDefinedAlignment();
}
void QgsTextFormatWidget::mCoordTypeComboBox_currentIndexChanged( int index )
{
switch ( static_cast<QgsLabeling::CoordinateType>( index ) )
{
enableDataDefinedAlignment( false );
}
else if ( mCoordXDDBtn->isActive() )
{
enableDataDefinedAlignment( true );
case QgsLabeling::CoordinateType::XY:
mCoordPositionStackWidget->setCurrentWidget( mCoordPositionXYStackWidgetPage );
break;
case QgsLabeling::CoordinateType::Point:
mCoordPositionStackWidget->setCurrentWidget( mCoordPositionPointStackWidgetPage );
break;
}
updateDataDefinedAlignment();
}
void QgsTextFormatWidget::mShapeTypeCmbBx_currentIndexChanged( int )
@ -2035,9 +2045,18 @@ void QgsTextFormatWidget::showBackgroundRadius( bool show )
mShapeRadiusUnitsDDBtn->setVisible( show );
}
void QgsTextFormatWidget::enableDataDefinedAlignment( bool enable )
void QgsTextFormatWidget::updateDataDefinedAlignment()
{
mCoordAlignmentFrame->setEnabled( enable );
// no data defined alignment without data defined position
switch ( static_cast<QgsLabeling::CoordinateType>( mCoordTypeComboBox->currentData().toInt() ) )
{
case QgsLabeling::CoordinateType::XY:
mCoordAlignmentFrame->setEnabled( mCoordXDDBtn->isActive() && mCoordYDDBtn->isActive() );
break;
case QgsLabeling::CoordinateType::Point:
mCoordAlignmentFrame->setEnabled( mCoordPointDDBtn->isActive() );
break;
}
}
QgsExpressionContext QgsTextFormatWidget::createExpressionContext() const

View File

@ -154,10 +154,9 @@ class GUI_EXPORT QgsTextFormatWidget : public QWidget, public QgsExpressionConte
void setPreviewBackground( const QColor &color );
/**
* Controls whether data defined alignment buttons are enabled.
* \param enable set to TRUE to enable alignment controls
* Update the enabled state of the data defined alignment buttons.
*/
void enableDataDefinedAlignment( bool enable );
void updateDataDefinedAlignment();
QgsExpressionContext createExpressionContext() const override;
@ -283,8 +282,10 @@ class GUI_EXPORT QgsTextFormatWidget : public QWidget, public QgsExpressionConte
void mFontMaxPixelSpinBox_valueChanged( int px );
void mBufferUnitWidget_changed();
void mMaskBufferUnitWidget_changed();
void mCoordXDDBtn_changed( );
void mCoordYDDBtn_changed( );
void mCoordXDDBtn_changed();
void mCoordYDDBtn_changed();
void mCoordPointDDBtn_changed();
void mCoordTypeComboBox_currentIndexChanged( int index );
void mShapeTypeCmbBx_currentIndexChanged( int index );
void mShapeRotationCmbBx_currentIndexChanged( int index );
void mShapeSVGParamsBtn_clicked();

View File

@ -5519,44 +5519,102 @@ font-style: italic;</string>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_22">
<item>
<widget class="QLabel" name="mCoordXLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>X</string>
<widget class="QStackedWidget" name="mCoordPositionStackWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="mCoordPositionXYStackWidgetPage">
<layout class="QHBoxLayout" name="horizontalLayout_15">
<item>
<widget class="QLabel" name="mCoordXLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>X</string>
</property>
</widget>
</item>
<item>
<widget class="QgsPropertyOverrideButton" name="mCoordXDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="mCoordYLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Y</string>
</property>
</widget>
</item>
<item>
<widget class="QgsPropertyOverrideButton" name="mCoordYDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="mCoordPositionPointStackWidgetPage">
<layout class="QHBoxLayout" name="horizontalLayout_18">
<item>
<widget class="QLabel" name="label_44">
<property name="text">
<string>Point</string>
</property>
</widget>
</item>
<item>
<widget class="QgsPropertyOverrideButton" name="mCoordPointDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_24">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>298</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QgsPropertyOverrideButton" name="mCoordXDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="mCoordYLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Y</string>
</property>
</widget>
</item>
<item>
<widget class="QgsPropertyOverrideButton" name="mCoordYDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
<widget class="QComboBox" name="mCoordTypeComboBox"/>
</item>
<item>
<spacer name="horizontalSpacer_22">
@ -6919,8 +6977,6 @@ font-style: italic;</string>
<tabstop>mGeometryGeneratorGroupBox</tabstop>
<tabstop>mGeometryGeneratorExpressionButton</tabstop>
<tabstop>mGeometryGeneratorType</tabstop>
<tabstop>mCoordXDDBtn</tabstop>
<tabstop>mCoordYDDBtn</tabstop>
<tabstop>mCoordAlignmentHDDBtn</tabstop>
<tabstop>mCoordAlignmentVDDBtn</tabstop>
<tabstop>mCoordRotationDDBtn</tabstop>

View File

@ -85,6 +85,7 @@ class TestQgsLabelingEngine : public QObject
void curvedOverrun();
void parallelOverrun();
void testDataDefinedLabelAllParts();
void testDataDefinedPlacementPositionPoint();
void testVerticalOrientation();
void testVerticalOrientationLetterLineSpacing();
void testRotationBasedOrientationPoint();
@ -2859,7 +2860,50 @@ void TestQgsLabelingEngine::testDataDefinedLabelAllParts()
QImage img = job.renderedImage();
QVERIFY( imageCheck( QStringLiteral( "label_datadefined_label_all_parts" ), img, 20 ) );
}
void TestQgsLabelingEngine::testDataDefinedPlacementPositionPoint()
{
QSize size( 640, 480 );
QgsMapSettings mapSettings;
mapSettings.setLabelingEngineSettings( createLabelEngineSettings() );
mapSettings.setOutputSize( size );
mapSettings.setExtent( vl->extent() );
mapSettings.setLayers( QList<QgsMapLayer *>() << vl );
mapSettings.setOutputDpi( 96 );
// first render the map and labeling separately
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" );
setDefaultLabelParams( settings );
settings.setPlacementCoordinateType( QgsLabeling::CoordinateType::Point );
settings.dataDefinedProperties().setProperty( QgsPalLayerSettings::PositionPoint, QgsProperty::fromExpression( QStringLiteral( "translate($geometry, 1, 0.5)" ) ) );
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.run( context );
p.end();
QVERIFY( imageCheck( "label_datadefined_placement_position_point", img, 20 ) );
vl->setLabeling( nullptr );
}
void TestQgsLabelingEngine::testVerticalOrientation()