[feature] Add maximum distance setting for point labels

For the "Around point" and "Cartographic" placement modes, this
adds a new optional setting for the maximum distance of the labels from
the feature. It's used together with the existing distance setting
to define a range of distances at which labels may be placed
from their corresponding point features.

This adds more flexibility to the placement for these layers,
ultimately allowing for more labels to be placed in busy maps.

When the layer is set to the "around point" mode, then label
candidates which are closer to the point will always be prefered
over those which are further away.

When the layer is set to the "cartographic" mode, then the default
behavior is also to prioritize closer labels. A new combo box
allows users to control the priority, with an option for
prefering position ordering. If this option is set, then candidates
at the corresponding positions (eg top left) are preferred regardless
of how far they are from the point, with the labelling falling
back to alternate positions only when no labels can be placed
up to the maximum label distance.

Sponsored by Rubicon Concierge Real Estate Services
This commit is contained in:
Nyall Dawson 2024-05-14 10:08:48 +10:00
parent 68e9ba151a
commit fe74b300be
23 changed files with 1140 additions and 532 deletions

View File

@ -970,6 +970,12 @@ Qgis.LabelOverlapHandling.AllowOverlapAtNoCost.__doc__ = "Labels may freely over
Qgis.LabelOverlapHandling.__doc__ = "Label overlap handling.\n\n.. versionadded:: 3.26\n\n" + '* ``PreventOverlap``: ' + Qgis.LabelOverlapHandling.PreventOverlap.__doc__ + '\n' + '* ``AllowOverlapIfRequired``: ' + Qgis.LabelOverlapHandling.AllowOverlapIfRequired.__doc__ + '\n' + '* ``AllowOverlapAtNoCost``: ' + Qgis.LabelOverlapHandling.AllowOverlapAtNoCost.__doc__
# --
Qgis.LabelOverlapHandling.baseClass = Qgis
# monkey patching scoped based enum
Qgis.LabelPrioritization.PreferCloser.__doc__ = "Prefer closer labels, falling back to alternate positions before larger distances"
Qgis.LabelPrioritization.PreferPositionOrdering.__doc__ = "Prefer labels follow position ordering, falling back to more distance labels before alternate positions"
Qgis.LabelPrioritization.__doc__ = "Label prioritization.\n\n.. versionadded:: 3.38\n\n" + '* ``PreferCloser``: ' + Qgis.LabelPrioritization.PreferCloser.__doc__ + '\n' + '* ``PreferPositionOrdering``: ' + Qgis.LabelPrioritization.PreferPositionOrdering.__doc__
# --
Qgis.LabelPrioritization.baseClass = Qgis
QgsPalLayerSettings.Placement = Qgis.LabelPlacement
# monkey patching scoped based enum
QgsPalLayerSettings.AroundPoint = Qgis.LabelPlacement.AroundPoint

File diff suppressed because one or more lines are too long

View File

@ -57,6 +57,24 @@ For instance, this will permit a curved line label to fallback to a horizontal l
if the label cannot otherwise be placed on the line in a curved manner.
.. seealso:: :py:func:`allowDegradedPlacement`
%End
Qgis::LabelPrioritization prioritization() const;
%Docstring
Returns the label prioritization technique.
.. seealso:: :py:func:`setPrioritization`
.. versionadded:: 3.38
%End
void setPrioritization( Qgis::LabelPrioritization prioritization );
%Docstring
Sets the technique used to prioritize labels.
.. seealso:: :py:func:`prioritization`
.. versionadded:: 3.38
%End
void updateDataDefinedProperties( const QgsPropertyCollection &properties, QgsExpressionContext &context );

View File

@ -66,6 +66,84 @@ Positions earlier in the list will be prioritized over later positions.
Only used when the placement is set to :py:class:`Qgis`.LabelPlacement.OrderedPositionsAroundPoint.
.. seealso:: :py:func:`predefinedPositionOrder`
%End
double maximumDistance() const;
%Docstring
Returns the maximum distance which labels are allowed to be from their corresponding points.
This setting works alongside the standard label offset distance properties to define a permissible
range of distances at which labels can be placed from their points.
The default value is 0, which indicates that no maximum is set and the label's usual distance
from point will always be respected.
.. seealso:: :py:func:`setMaximumDistance`
.. seealso:: :py:func:`maximumDistanceUnit`
.. seealso:: :py:func:`maximumDistanceMapUnitScale`
%End
void setMaximumDistance( double distance );
%Docstring
Sets the maximum ``distance`` which labels are allowed to be from their corresponding points.
This setting works alongside the standard label offset distance properties to define a permissible
range of distances at which labels can be placed from their points.
Setting ``distance`` to 0 indicates that no maximum is set and the label's usual distance
from point will always be respected.
.. seealso:: :py:func:`maximumDistance`
.. seealso:: :py:func:`maximumDistanceUnit`
.. seealso:: :py:func:`maximumDistanceMapUnitScale`
%End
Qgis::RenderUnit maximumDistanceUnit() const;
%Docstring
Returns the units for label maximum distance.
.. seealso:: :py:func:`setMaximumDistanceUnit`
.. seealso:: :py:func:`maximumDistance`
.. seealso:: :py:func:`maximumDistanceMapUnitScale`
%End
void setMaximumDistanceUnit( Qgis::RenderUnit unit );
%Docstring
Sets the ``unit`` for label maximum distance.
.. seealso:: :py:func:`maximumDistanceUnit`
.. seealso:: :py:func:`maximumDistance`
.. seealso:: :py:func:`maximumDistanceMapUnitScale`
%End
QgsMapUnitScale maximumDistanceMapUnitScale() const;
%Docstring
Returns the map unit scale for label maximum distance.
.. seealso:: :py:func:`setMaximumDistanceMapUnitScale`
.. seealso:: :py:func:`maximumDistance`
.. seealso:: :py:func:`maximumDistanceUnit`
%End
void setMaximumDistanceMapUnitScale( const QgsMapUnitScale &scale );
%Docstring
Sets the map unit ``scale`` for label maximum distance.
.. seealso:: :py:func:`maximumDistanceMapUnitScale`
.. seealso:: :py:func:`maximumDistance`
.. seealso:: :py:func:`maximumDistanceUnit`
%End
};

View File

@ -121,6 +121,7 @@ Contains settings for how a map layer will be labeled.
OffsetXY,
OffsetUnits,
LabelDistance,
MaximumDistance,
DistanceUnits,
OffsetRotation,
CurvedCharAngleInOut,

View File

@ -550,6 +550,12 @@ The development version
AllowOverlapAtNoCost,
};
enum class LabelPrioritization /BaseType=IntEnum/
{
PreferCloser,
PreferPositionOrdering,
};
enum class LabelPlacement /BaseType=IntEnum/
{
AroundPoint,

View File

@ -949,6 +949,12 @@ Qgis.LabelOverlapHandling.AllowOverlapAtNoCost.__doc__ = "Labels may freely over
Qgis.LabelOverlapHandling.__doc__ = "Label overlap handling.\n\n.. versionadded:: 3.26\n\n" + '* ``PreventOverlap``: ' + Qgis.LabelOverlapHandling.PreventOverlap.__doc__ + '\n' + '* ``AllowOverlapIfRequired``: ' + Qgis.LabelOverlapHandling.AllowOverlapIfRequired.__doc__ + '\n' + '* ``AllowOverlapAtNoCost``: ' + Qgis.LabelOverlapHandling.AllowOverlapAtNoCost.__doc__
# --
Qgis.LabelOverlapHandling.baseClass = Qgis
# monkey patching scoped based enum
Qgis.LabelPrioritization.PreferCloser.__doc__ = "Prefer closer labels, falling back to alternate positions before larger distances"
Qgis.LabelPrioritization.PreferPositionOrdering.__doc__ = "Prefer labels follow position ordering, falling back to more distance labels before alternate positions"
Qgis.LabelPrioritization.__doc__ = "Label prioritization.\n\n.. versionadded:: 3.38\n\n" + '* ``PreferCloser``: ' + Qgis.LabelPrioritization.PreferCloser.__doc__ + '\n' + '* ``PreferPositionOrdering``: ' + Qgis.LabelPrioritization.PreferPositionOrdering.__doc__
# --
Qgis.LabelPrioritization.baseClass = Qgis
QgsPalLayerSettings.Placement = Qgis.LabelPlacement
# monkey patching scoped based enum
QgsPalLayerSettings.AroundPoint = Qgis.LabelPlacement.AroundPoint

File diff suppressed because one or more lines are too long

View File

@ -57,6 +57,24 @@ For instance, this will permit a curved line label to fallback to a horizontal l
if the label cannot otherwise be placed on the line in a curved manner.
.. seealso:: :py:func:`allowDegradedPlacement`
%End
Qgis::LabelPrioritization prioritization() const;
%Docstring
Returns the label prioritization technique.
.. seealso:: :py:func:`setPrioritization`
.. versionadded:: 3.38
%End
void setPrioritization( Qgis::LabelPrioritization prioritization );
%Docstring
Sets the technique used to prioritize labels.
.. seealso:: :py:func:`prioritization`
.. versionadded:: 3.38
%End
void updateDataDefinedProperties( const QgsPropertyCollection &properties, QgsExpressionContext &context );

View File

@ -66,6 +66,84 @@ Positions earlier in the list will be prioritized over later positions.
Only used when the placement is set to :py:class:`Qgis`.LabelPlacement.OrderedPositionsAroundPoint.
.. seealso:: :py:func:`predefinedPositionOrder`
%End
double maximumDistance() const;
%Docstring
Returns the maximum distance which labels are allowed to be from their corresponding points.
This setting works alongside the standard label offset distance properties to define a permissible
range of distances at which labels can be placed from their points.
The default value is 0, which indicates that no maximum is set and the label's usual distance
from point will always be respected.
.. seealso:: :py:func:`setMaximumDistance`
.. seealso:: :py:func:`maximumDistanceUnit`
.. seealso:: :py:func:`maximumDistanceMapUnitScale`
%End
void setMaximumDistance( double distance );
%Docstring
Sets the maximum ``distance`` which labels are allowed to be from their corresponding points.
This setting works alongside the standard label offset distance properties to define a permissible
range of distances at which labels can be placed from their points.
Setting ``distance`` to 0 indicates that no maximum is set and the label's usual distance
from point will always be respected.
.. seealso:: :py:func:`maximumDistance`
.. seealso:: :py:func:`maximumDistanceUnit`
.. seealso:: :py:func:`maximumDistanceMapUnitScale`
%End
Qgis::RenderUnit maximumDistanceUnit() const;
%Docstring
Returns the units for label maximum distance.
.. seealso:: :py:func:`setMaximumDistanceUnit`
.. seealso:: :py:func:`maximumDistance`
.. seealso:: :py:func:`maximumDistanceMapUnitScale`
%End
void setMaximumDistanceUnit( Qgis::RenderUnit unit );
%Docstring
Sets the ``unit`` for label maximum distance.
.. seealso:: :py:func:`maximumDistanceUnit`
.. seealso:: :py:func:`maximumDistance`
.. seealso:: :py:func:`maximumDistanceMapUnitScale`
%End
QgsMapUnitScale maximumDistanceMapUnitScale() const;
%Docstring
Returns the map unit scale for label maximum distance.
.. seealso:: :py:func:`setMaximumDistanceMapUnitScale`
.. seealso:: :py:func:`maximumDistance`
.. seealso:: :py:func:`maximumDistanceUnit`
%End
void setMaximumDistanceMapUnitScale( const QgsMapUnitScale &scale );
%Docstring
Sets the map unit ``scale`` for label maximum distance.
.. seealso:: :py:func:`maximumDistanceMapUnitScale`
.. seealso:: :py:func:`maximumDistance`
.. seealso:: :py:func:`maximumDistanceUnit`
%End
};

View File

@ -121,6 +121,7 @@ Contains settings for how a map layer will be labeled.
OffsetXY,
OffsetUnits,
LabelDistance,
MaximumDistance,
DistanceUnits,
OffsetRotation,
CurvedCharAngleInOut,

View File

@ -550,6 +550,12 @@ The development version
AllowOverlapAtNoCost,
};
enum class LabelPrioritization
{
PreferCloser,
PreferPositionOrdering,
};
enum class LabelPlacement
{
AroundPoint,

View File

@ -299,15 +299,53 @@ class CORE_EXPORT QgsLabelFeature
/**
* Applies to "around point" placement strategy or linestring features.
* Distance of the label from the feature (in map units)
*
* \see setDistLabel()
* \see maximumDistance()
*/
double distLabel() const { return mDistLabel; }
/**
* Applies to "around point" placement strategy or linestring features.
* Set distance of the label from the feature (in map units)
*
* \see distLabel()
* \see setMaximumDistance()
*/
void setDistLabel( double dist ) { mDistLabel = dist; }
/**
* Returns the maximum distance which labels are allowed to be from their corresponding points.
*
* This setting works alongside distLabel() to define a permissible
* range of distances at which labels can be placed from their points.
*
* The default value is 0, which indicates that no maximum is set and the that distLabel()
* always be respected.
*
* \see setMaximumDistance()
* \see distLabel()
*
* \since QGIS 3.38
*/
double maximumDistance() const { return mMaximumDistance; }
/**
* Sets the maximum \a distance which labels are allowed to be from their corresponding points.
*
* This setting works alongside distLabel() to define a permissible
* range of distances at which labels can be placed from their points.
*
* The default value is 0, which indicates that no maximum is set and the that distLabel()
* always be respected.
*
* \see maximumDistance()
* \see setDistLabel()
*
* \since QGIS 3.38
*/
void setMaximumDistance( double distance ) { mMaximumDistance = distance; }
/**
* Returns the priority ordered list of predefined positions for label candidates. This property
* is only used for OrderedPositionsAroundPoint placements.
@ -624,6 +662,22 @@ class CORE_EXPORT QgsLabelFeature
*/
bool allowDegradedPlacement() const { return mAllowDegradedPlacement; }
/**
* Returns the label prioritization technique.
*
* \see setPrioritization()
* \since QGIS 3.38
*/
Qgis::LabelPrioritization prioritization() const { return mPrioritization; }
/**
* Sets the label prioritization technique.
*
* \see prioritization()
* \since QGIS 3.26
*/
void setPrioritization( Qgis::LabelPrioritization prioritization ) { mPrioritization = prioritization; }
/**
* Sets whether the label can be placed in inferior fallback positions if it cannot otherwise
* be placed.
@ -676,6 +730,10 @@ class CORE_EXPORT QgsLabelFeature
QgsPointXY mPositionOffset;
//! distance of label from the feature (only for "around point" placement or linestrings)
double mDistLabel = 0;
//! Maximum distance of label from the feature.
double mMaximumDistance = 0;
//! Offset type for certain placement modes
Qgis::LabelOffsetType mOffsetType = Qgis::LabelOffsetType::FromPoint;
//! Ordered list of predefined positions for label (only for OrderedPositionsAroundPoint placement)
@ -719,6 +777,7 @@ class CORE_EXPORT QgsLabelFeature
Qgis::LabelOverlapHandling mOverlapHandling = Qgis::LabelOverlapHandling::PreventOverlap;
bool mAllowDegradedPlacement = false;
Qgis::LabelPrioritization mPrioritization = Qgis::LabelPrioritization::PreferCloser;
QgsCoordinateReferenceSystem mOriginalFeatureCrs;

View File

@ -69,6 +69,24 @@ class CORE_EXPORT QgsLabelPlacementSettings
*/
void setAllowDegradedPlacement( bool allow ) { mAllowDegradedPlacement = allow; }
/**
* Returns the label prioritization technique.
*
* \see setPrioritization()
*
* \since QGIS 3.38
*/
Qgis::LabelPrioritization prioritization() const { return mPrioritization; }
/**
* Sets the technique used to prioritize labels.
*
* \see prioritization()
*
* \since QGIS 3.38
*/
void setPrioritization( Qgis::LabelPrioritization prioritization ) { mPrioritization = prioritization; }
/**
* Updates the placement settings to respect any data defined properties
* set within the specified \a properties collection.
@ -78,6 +96,7 @@ class CORE_EXPORT QgsLabelPlacementSettings
private:
Qgis::LabelOverlapHandling mOverlapHandling = Qgis::LabelOverlapHandling::PreventOverlap;
Qgis::LabelPrioritization mPrioritization = Qgis::LabelPrioritization::PreferCloser;
bool mAllowDegradedPlacement = false;

View File

@ -16,11 +16,14 @@
#include "qgslabelpointsettings.h"
#include "qgspropertycollection.h"
#include "qgsexpressioncontext.h"
#include "qgslabelingengine.h"
#include "qgspallabeling.h"
void QgsLabelPointSettings::updateDataDefinedProperties( const QgsPropertyCollection &, QgsExpressionContext & )
void QgsLabelPointSettings::updateDataDefinedProperties( const QgsPropertyCollection &properties, QgsExpressionContext &context )
{
// for future use when new additional properties are added to QgsLabelPointSettings
if ( properties.isActive( QgsPalLayerSettings::Property::MaximumDistance ) )
{
context.setOriginalValueVariable( mMaximumDistance );
mMaximumDistance = properties.valueAsDouble( QgsPalLayerSettings::Property::MaximumDistance, context, mMaximumDistance );
}
}

View File

@ -80,12 +80,82 @@ class CORE_EXPORT QgsLabelPointSettings
*/
void setPredefinedPositionOrder( const QVector< Qgis::LabelPredefinedPointPosition > &order ) { mPredefinedPositionOrder = order; }
/**
* Returns the maximum distance which labels are allowed to be from their corresponding points.
*
* This setting works alongside the standard label offset distance properties to define a permissible
* range of distances at which labels can be placed from their points.
*
* The default value is 0, which indicates that no maximum is set and the label's usual distance
* from point will always be respected.
*
* \see setMaximumDistance()
* \see maximumDistanceUnit()
* \see maximumDistanceMapUnitScale()
*/
double maximumDistance() const { return mMaximumDistance; }
/**
* Sets the maximum \a distance which labels are allowed to be from their corresponding points.
*
* This setting works alongside the standard label offset distance properties to define a permissible
* range of distances at which labels can be placed from their points.
*
* Setting \a distance to 0 indicates that no maximum is set and the label's usual distance
* from point will always be respected.
*
* \see maximumDistance()
* \see maximumDistanceUnit()
* \see maximumDistanceMapUnitScale()
*/
void setMaximumDistance( double distance ) { mMaximumDistance = distance; }
/**
* Returns the units for label maximum distance.
*
* \see setMaximumDistanceUnit()
* \see maximumDistance()
* \see maximumDistanceMapUnitScale()
*/
Qgis::RenderUnit maximumDistanceUnit() const { return mMaximumDistanceUnit; }
/**
* Sets the \a unit for label maximum distance.
*
* \see maximumDistanceUnit()
* \see maximumDistance()
* \see maximumDistanceMapUnitScale()
*/
void setMaximumDistanceUnit( Qgis::RenderUnit unit ) { mMaximumDistanceUnit = unit;}
/**
* Returns the map unit scale for label maximum distance.
*
* \see setMaximumDistanceMapUnitScale()
* \see maximumDistance()
* \see maximumDistanceUnit()
*/
QgsMapUnitScale maximumDistanceMapUnitScale() const { return mMaximumDistanceMapUnitScale; }
/**
* Sets the map unit \a scale for label maximum distance.
*
* \see maximumDistanceMapUnitScale()
* \see maximumDistance()
* \see maximumDistanceUnit()
*/
void setMaximumDistanceMapUnitScale( const QgsMapUnitScale &scale ) { mMaximumDistanceMapUnitScale = scale; }
private:
Qgis::LabelQuadrantPosition mQuadrant = Qgis::LabelQuadrantPosition::Over;
QVector< Qgis::LabelPredefinedPointPosition > mPredefinedPositionOrder;
double mMaximumDistance = 0;
Qgis::RenderUnit mMaximumDistanceUnit = Qgis::RenderUnit::Millimeters;
QgsMapUnitScale mMaximumDistanceMapUnitScale;
};
#endif // QGSLABELPOINTSETTINGS_H

View File

@ -253,6 +253,7 @@ void QgsPalLayerSettings::initPropertyDefinitions()
{ static_cast< int >( QgsPalLayerSettings::Property::LabelAllParts ), QgsPropertyDefinition( "LabelAllParts", QObject::tr( "Label all parts" ), QgsPropertyDefinition::Boolean, origin ) },
{ static_cast< int >( QgsPalLayerSettings::Property::AllowDegradedPlacement ), QgsPropertyDefinition( "AllowDegradedPlacement", QObject::tr( "Allow inferior fallback placements" ), QgsPropertyDefinition::Boolean, origin ) },
{ static_cast< int >( QgsPalLayerSettings::Property::OverlapHandling ), QgsPropertyDefinition( "OverlapHandling", QgsPropertyDefinition::DataTypeString, QObject::tr( "Overlap handing" ), QObject::tr( "string " ) + "[<b>Prevent</b>|<b>AllowIfNeeded</b>|<b>AlwaysAllow</b>]", origin ) },
{ static_cast< int >( QgsPalLayerSettings::Property::MaximumDistance ), QgsPropertyDefinition( "MaximumDistance", QObject::tr( "Maximum distance" ), QgsPropertyDefinition::DoublePositive, origin ) },
};
}
@ -1118,6 +1119,10 @@ void QgsPalLayerSettings::readXml( const QDomElement &elem, const QgsReadWriteCo
// when reading the anchor text point we default to center mode, to keep same result as for proejcts created in < 3.26
mLineSettings.setAnchorTextPoint( qgsEnumKeyToValue( placementElem.attribute( QStringLiteral( "lineAnchorTextPoint" ) ), QgsLabelLineSettings::AnchorTextPoint::CenterOfText ) );
mPointSettings.setMaximumDistance( placementElem.attribute( QStringLiteral( "maximumDistance" ), QStringLiteral( "0" ) ).toDouble() );
mPointSettings.setMaximumDistanceUnit( QgsUnitTypes::decodeRenderUnit( placementElem.attribute( QStringLiteral( "maximumDistanceUnit" ) ) ) );
mPointSettings.setMaximumDistanceMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( placementElem.attribute( QStringLiteral( "maximumDistanceMapUnitScale" ) ) ) );
geometryGenerator = placementElem.attribute( QStringLiteral( "geometryGenerator" ) );
geometryGeneratorEnabled = placementElem.attribute( QStringLiteral( "geometryGeneratorEnabled" ) ).toInt();
{
@ -1170,6 +1175,9 @@ void QgsPalLayerSettings::readXml( const QDomElement &elem, const QgsReadWriteCo
mPlacementSettings.setAllowDegradedPlacement( false );
}
}
mPlacementSettings.setPrioritization( qgsEnumKeyToValue( placementElem.attribute( QStringLiteral( "prioritization" ) ), Qgis::LabelPrioritization::PreferCloser ) );
upsidedownLabels = static_cast< Qgis::UpsideDownLabelHandling >( renderingElem.attribute( QStringLiteral( "upsidedownLabels" ), QString::number( static_cast< int >( Qgis::UpsideDownLabelHandling::FlipUpsideDownLabels ) ) ).toUInt() );
labelPerPart = renderingElem.attribute( QStringLiteral( "labelPerPart" ) ).toInt();
@ -1312,6 +1320,10 @@ QDomElement QgsPalLayerSettings::writeXml( QDomDocument &doc, const QgsReadWrite
placementElem.setAttribute( QStringLiteral( "lineAnchorClipping" ), static_cast< int >( mLineSettings.anchorClipping() ) );
placementElem.setAttribute( QStringLiteral( "lineAnchorTextPoint" ), qgsEnumValueToKey( mLineSettings.anchorTextPoint() ) );
placementElem.setAttribute( QStringLiteral( "maximumDistance" ), mPointSettings.maximumDistance() );
placementElem.setAttribute( QStringLiteral( "maximumDistanceUnit" ), QgsUnitTypes::encodeUnit( mPointSettings.maximumDistanceUnit() ) );
placementElem.setAttribute( QStringLiteral( "maximumDistanceMapUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mPointSettings.maximumDistanceMapUnitScale() ) );
placementElem.setAttribute( QStringLiteral( "geometryGenerator" ), geometryGenerator );
placementElem.setAttribute( QStringLiteral( "geometryGeneratorEnabled" ), geometryGeneratorEnabled );
placementElem.setAttribute( QStringLiteral( "geometryGeneratorType" ), qgsEnumValueToKey( geometryGeneratorType ) + QStringLiteral( "Geometry" ) );
@ -1319,6 +1331,7 @@ QDomElement QgsPalLayerSettings::writeXml( QDomDocument &doc, const QgsReadWrite
placementElem.setAttribute( QStringLiteral( "layerType" ), qgsEnumValueToKey( layerType ) + QStringLiteral( "Geometry" ) );
placementElem.setAttribute( QStringLiteral( "overlapHandling" ), qgsEnumValueToKey( mPlacementSettings.overlapHandling() ) );
placementElem.setAttribute( QStringLiteral( "prioritization" ), qgsEnumValueToKey( mPlacementSettings.prioritization() ) );
placementElem.setAttribute( QStringLiteral( "allowDegraded" ), mPlacementSettings.allowDegradedPlacement() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
// rendering
@ -2726,6 +2739,13 @@ std::unique_ptr<QgsLabelFeature> QgsPalLayerSettings::registerFeatureWithDetails
}
}
// maximum distance
double maximumDistanceEval = pointSettings.maximumDistance();
if ( !qgsDoubleNear( maximumDistanceEval, 0.0 ) )
{
maximumDistanceEval = context.convertToMapUnits( maximumDistanceEval, pointSettings.maximumDistanceUnit(), pointSettings.maximumDistanceMapUnitScale() );
}
// feature to the layer
std::unique_ptr< QgsTextLabelFeature > labelFeature = std::make_unique< QgsTextLabelFeature>( feature.id(), std::move( geos_geom_clone ), QSizeF( labelWidth, labelHeight ) );
labelFeature->setAnchorPosition( anchorPosition );
@ -2750,6 +2770,7 @@ std::unique_ptr<QgsLabelFeature> QgsPalLayerSettings::registerFeatureWithDetails
labelFeature->setPermissibleZone( permissibleZone );
labelFeature->setOverrunDistance( overrunDistanceEval );
labelFeature->setOverrunSmoothDistance( overrunSmoothDist );
labelFeature->setMaximumDistance( maximumDistanceEval );
labelFeature->setLineAnchorPercent( lineSettings.lineAnchorPercent() );
labelFeature->setLineAnchorType( lineSettings.anchorType() );
labelFeature->setLineAnchorTextPoint( lineSettings.anchorTextPoint() );
@ -2932,6 +2953,8 @@ std::unique_ptr<QgsLabelFeature> QgsPalLayerSettings::registerFeatureWithDetails
labelFeature->setOverlapHandling( overlapHandling );
}
labelFeature->setPrioritization( mPlacementSettings.prioritization() );
QgsLabelObstacleSettings os = mObstacleSettings;
os.setIsObstacle( isObstacle );
os.updateDataDefinedProperties( mDataDefinedProperties, context.expressionContext() );

View File

@ -191,6 +191,7 @@ class CORE_EXPORT QgsPalLayerSettings
OffsetXY = 78,
OffsetUnits = 80,
LabelDistance = 13,
MaximumDistance = 119, //!< Maximum distance of label from feature
DistanceUnits = 81,
OffsetRotation = 82,
CurvedCharAngleInOut = 83,

View File

@ -548,9 +548,11 @@ void createCandidateAtOrderedPositionOverPoint( double &labelX, double &labelY,
std::size_t FeaturePart::createCandidatesAtOrderedPositionsOverPoint( double x, double y, std::vector< std::unique_ptr< LabelPosition > > &lPos, double angle )
{
const QVector< Qgis::LabelPredefinedPointPosition > positions = mLF->predefinedPositionOrder();
double labelWidth = getLabelWidth( angle );
double labelHeight = getLabelHeight( angle );
const double labelWidth = getLabelWidth( angle );
const double labelHeight = getLabelHeight( angle );
double distanceToLabel = getLabelDistance();
const double maximumDistanceToLabel = mLF->maximumDistance();
const QgsMargins &visualMargin = mLF->visualMargin();
double symbolWidthOffset{ 0 };
@ -572,18 +574,33 @@ std::size_t FeaturePart::createCandidatesAtOrderedPositionsOverPoint( double x,
}
}
int candidatesPerPosition = 1;
double distanceStep = 0;
if ( maximumDistanceToLabel > distanceToLabel && !qgsDoubleNear( maximumDistanceToLabel, 0 ) )
{
// if we are placing labels over a distance range, we calculate the number of candidates
// based on the calculated valid area for labels
const double rayLength = maximumDistanceToLabel - distanceToLabel;
// we want at least two candidates per "ray", one at the min distance and one at the max
candidatesPerPosition = std::max( 2, static_cast< int >( std::ceil( mLF->layer()->mPal->maximumLineCandidatesPerMapUnit() * 1.5 * rayLength ) ) );
distanceStep = rayLength / ( candidatesPerPosition - 1 );
}
double cost = 0.0001;
std::size_t i = lPos.size();
const std::size_t maxNumberCandidates = mLF->layer()->maximumPointLabelCandidates();
const Qgis::LabelPrioritization prioritization = mLF->prioritization();
const std::size_t maxNumberCandidates = mLF->layer()->maximumPointLabelCandidates() * candidatesPerPosition;
std::size_t created = 0;
for ( Qgis::LabelPredefinedPointPosition position : positions )
auto addCandidate = [this, x, y, labelWidth, labelHeight, angle, visualMargin, symbolWidthOffset, symbolHeightOffset, &created, &cost, &lPos, &i, maxNumberCandidates]( Qgis::LabelPredefinedPointPosition position, double distance ) -> bool
{
LabelPosition::Quadrant quadrant = LabelPosition::QuadrantAboveLeft;
double labelX = 0;
double labelY = 0;
createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant, x, y, labelWidth, labelHeight, position, distanceToLabel, visualMargin, symbolWidthOffset, symbolHeightOffset, angle );
createCandidateAtOrderedPositionOverPoint( labelX, labelY, quadrant, x, y, labelWidth, labelHeight, position, distance, visualMargin, symbolWidthOffset, symbolHeightOffset, angle );
if ( ! mLF->permissibleZonePrepared() || GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), labelX, labelY, labelWidth, labelHeight, angle ) )
{
@ -592,35 +609,89 @@ std::size_t FeaturePart::createCandidatesAtOrderedPositionsOverPoint( double x,
//TODO - tweak
cost += 0.001;
if ( maxNumberCandidates > 0 && created >= maxNumberCandidates )
break;
return false;
}
++i;
}
return true;
};
switch ( prioritization )
{
// the two cases below are identical, we just change which loop is the outer and inner loop
// remember to keep these in sync!!
case Qgis::LabelPrioritization::PreferPositionOrdering:
{
for ( Qgis::LabelPredefinedPointPosition position : positions )
{
double currentDistance = distanceToLabel;
for ( int distanceIndex = 0; distanceIndex < candidatesPerPosition; ++distanceIndex, currentDistance += distanceStep )
{
if ( !addCandidate( position, currentDistance ) )
return created;
}
}
break;
}
case Qgis::LabelPrioritization::PreferCloser:
{
double currentDistance = distanceToLabel;
for ( int distanceIndex = 0; distanceIndex < candidatesPerPosition; ++distanceIndex, currentDistance += distanceStep )
{
for ( Qgis::LabelPredefinedPointPosition position : positions )
{
if ( !addCandidate( position, currentDistance ) )
return created;
}
}
break;
}
}
return created;
}
std::size_t FeaturePart::createCandidatesAroundPoint( double x, double y, std::vector< std::unique_ptr< LabelPosition > > &lPos, double angle )
{
double labelWidth = getLabelWidth( angle );
double labelHeight = getLabelHeight( angle );
double distanceToLabel = getLabelDistance();
const double labelWidth = getLabelWidth( angle );
const double labelHeight = getLabelHeight( angle );
const double distanceToLabel = getLabelDistance();
const double maximumDistanceToLabel = mLF->maximumDistance();
std::size_t maxNumberCandidates = mLF->layer()->maximumPointLabelCandidates();
if ( maxNumberCandidates == 0 )
maxNumberCandidates = 16;
// Take care of the label angle when creating candidates. See pr comments #44944 for details
// https://github.com/qgis/QGIS/pull/44944#issuecomment-914670088
QTransform transformRotation;
transformRotation.rotate( angle * 180 / M_PI );
int icost = 0;
int inc = 2;
int id = lPos.size();
int rayCount = static_cast< int >( mLF->layer()->maximumPointLabelCandidates() );
if ( rayCount == 0 )
rayCount = 16;
double candidateAngleIncrement = 2 * M_PI / maxNumberCandidates; /* angle bw 2 pos */
int candidatesPerRay = 0;
double rayStepDelta = 0;
if ( maximumDistanceToLabel > distanceToLabel && !qgsDoubleNear( maximumDistanceToLabel, 0 ) )
{
// if we are placing labels over a distance range, we calculate the number of candidates
// based on the calculated valid area for labels
const double rayLength = maximumDistanceToLabel - distanceToLabel;
// we want at least two candidates per "ray", one at the min distance and one at the max
candidatesPerRay = std::max( 2, static_cast< int >( std::ceil( mLF->layer()->mPal->maximumLineCandidatesPerMapUnit() * 1.5 * rayLength ) ) );
rayStepDelta = rayLength / ( candidatesPerRay - 1 );
}
else
{
candidatesPerRay = 1;
}
int id = static_cast< int >( lPos.size() );
const double candidateAngleIncrement = 2 * M_PI / static_cast< double >( rayCount ); /* angle bw 2 pos */
/* various angles */
double a90 = M_PI_2;
double a180 = M_PI;
double a270 = a180 + a90;
double a360 = 2 * M_PI;
constexpr double a90 = M_PI_2;
constexpr double a180 = M_PI;
constexpr double a270 = a180 + a90;
constexpr double a360 = 2 * M_PI;
double gamma1, gamma2;
@ -642,9 +713,10 @@ std::size_t FeaturePart::createCandidatesAroundPoint( double x, double y, std::v
std::size_t numberCandidatesGenerated = 0;
std::size_t i;
double angleToCandidate;
for ( i = 0, angleToCandidate = M_PI_4; i < maxNumberCandidates; i++, angleToCandidate += candidateAngleIncrement )
double angleToCandidate = M_PI_4;
for ( int rayIndex = 0; rayIndex < rayCount; ++rayIndex, angleToCandidate += candidateAngleIncrement )
{
double deltaX = 0.0;
double deltaY = 0.0;
@ -652,107 +724,110 @@ std::size_t FeaturePart::createCandidatesAroundPoint( double x, double y, std::v
if ( angleToCandidate > a360 )
angleToCandidate -= a360;
LabelPosition::Quadrant quadrant = LabelPosition::QuadrantOver;
double rayDistance = distanceToLabel;
if ( angleToCandidate < gamma1 || angleToCandidate > a360 - gamma1 ) // on the right
{
deltaX = distanceToLabel;
double iota = ( angleToCandidate + gamma1 );
if ( iota > a360 - gamma1 )
iota -= a360;
constexpr double RAY_ANGLE_COST_FACTOR = 0.0020;
// ray angle cost increases from 0 at 45 degrees up to 1 at 45 + 180, and then decreases
// back to 0 at angles greater than 45 + 180
// scale ray angle cost to range 0 to 1, and then adjust by a magic constant factor
const double scaledRayAngleCost = RAY_ANGLE_COST_FACTOR * ( ( rayIndex < rayCount / 2 )
? static_cast< double >( rayIndex * 2 ) / rayCount
: ( 2 - static_cast< double >( rayIndex * 2 ) / rayCount ) );
//ly += -yrm/2.0 + tan(alpha)*(distlabel + xrm/2);
deltaY = -labelHeight + labelHeight * iota / ( 2 * gamma1 );
for ( int j = 0; j < candidatesPerRay; ++j, rayDistance += rayStepDelta )
{
LabelPosition::Quadrant quadrant = LabelPosition::QuadrantOver;
quadrant = LabelPosition::QuadrantRight;
}
else if ( angleToCandidate < a90 - gamma2 ) // top-right
{
deltaX = distanceToLabel * std::cos( angleToCandidate );
deltaY = distanceToLabel * std::sin( angleToCandidate );
quadrant = LabelPosition::QuadrantAboveRight;
}
else if ( angleToCandidate < a90 + gamma2 ) // top
{
//lx += -xrm/2.0 - tan(alpha+a90)*(distlabel + yrm/2);
deltaX = -labelWidth * ( angleToCandidate - a90 + gamma2 ) / ( 2 * gamma2 );
deltaY = distanceToLabel;
quadrant = LabelPosition::QuadrantAbove;
}
else if ( angleToCandidate < a180 - gamma1 ) // top left
{
deltaX = distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
deltaY = distanceToLabel * std::sin( angleToCandidate );
quadrant = LabelPosition::QuadrantAboveLeft;
}
else if ( angleToCandidate < a180 + gamma1 ) // left
{
deltaX = -distanceToLabel - labelWidth;
//ly += -yrm/2.0 - tan(alpha)*(distlabel + xrm/2);
deltaY = - ( angleToCandidate - a180 + gamma1 ) * labelHeight / ( 2 * gamma1 );
quadrant = LabelPosition::QuadrantLeft;
}
else if ( angleToCandidate < a270 - gamma2 ) // down - left
{
deltaX = distanceToLabel * std::cos( angleToCandidate ) - labelWidth;
deltaY = distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
quadrant = LabelPosition::QuadrantBelowLeft;
}
else if ( angleToCandidate < a270 + gamma2 ) // down
{
deltaY = -distanceToLabel - labelHeight;
//lx += -xrm/2.0 + tan(alpha+a90)*(distlabel + yrm/2);
deltaX = -labelWidth + ( angleToCandidate - a270 + gamma2 ) * labelWidth / ( 2 * gamma2 );
quadrant = LabelPosition::QuadrantBelow;
}
else if ( angleToCandidate < a360 ) // down - right
{
deltaX = distanceToLabel * std::cos( angleToCandidate );
deltaY = distanceToLabel * std::sin( angleToCandidate ) - labelHeight;
quadrant = LabelPosition::QuadrantBelowRight;
}
// Take care of the label angle when creating candidates. See pr comments #44944 for details
// https://github.com/qgis/QGIS/pull/44944#issuecomment-914670088
QTransform transformRotation;
transformRotation.rotate( angle * 180 / M_PI );
transformRotation.map( deltaX, deltaY, &deltaX, &deltaY );
double labelX = x + deltaX;
double labelY = y + deltaY;
double cost;
if ( maxNumberCandidates == 1 )
cost = 0.0001;
else
cost = 0.0001 + 0.0020 * double( icost ) / double( maxNumberCandidates - 1 );
if ( mLF->permissibleZonePrepared() )
{
if ( !GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), labelX, labelY, labelWidth, labelHeight, angle ) )
if ( angleToCandidate < gamma1 || angleToCandidate > a360 - gamma1 ) // on the right
{
continue;
deltaX = rayDistance;
double iota = ( angleToCandidate + gamma1 );
if ( iota > a360 - gamma1 )
iota -= a360;
//ly += -yrm/2.0 + tan(alpha)*(distlabel + xrm/2);
deltaY = -labelHeight + labelHeight * iota / ( 2 * gamma1 );
quadrant = LabelPosition::QuadrantRight;
}
else if ( angleToCandidate < a90 - gamma2 ) // top-right
{
deltaX = rayDistance * std::cos( angleToCandidate );
deltaY = rayDistance * std::sin( angleToCandidate );
quadrant = LabelPosition::QuadrantAboveRight;
}
else if ( angleToCandidate < a90 + gamma2 ) // top
{
//lx += -xrm/2.0 - tan(alpha+a90)*(distlabel + yrm/2);
deltaX = -labelWidth * ( angleToCandidate - a90 + gamma2 ) / ( 2 * gamma2 );
deltaY = rayDistance;
quadrant = LabelPosition::QuadrantAbove;
}
else if ( angleToCandidate < a180 - gamma1 ) // top left
{
deltaX = rayDistance * std::cos( angleToCandidate ) - labelWidth;
deltaY = rayDistance * std::sin( angleToCandidate );
quadrant = LabelPosition::QuadrantAboveLeft;
}
else if ( angleToCandidate < a180 + gamma1 ) // left
{
deltaX = -rayDistance - labelWidth;
//ly += -yrm/2.0 - tan(alpha)*(distlabel + xrm/2);
deltaY = - ( angleToCandidate - a180 + gamma1 ) * labelHeight / ( 2 * gamma1 );
quadrant = LabelPosition::QuadrantLeft;
}
else if ( angleToCandidate < a270 - gamma2 ) // down - left
{
deltaX = rayDistance * std::cos( angleToCandidate ) - labelWidth;
deltaY = rayDistance * std::sin( angleToCandidate ) - labelHeight;
quadrant = LabelPosition::QuadrantBelowLeft;
}
else if ( angleToCandidate < a270 + gamma2 ) // down
{
deltaY = -rayDistance - labelHeight;
//lx += -xrm/2.0 + tan(alpha+a90)*(distlabel + yrm/2);
deltaX = -labelWidth + ( angleToCandidate - a270 + gamma2 ) * labelWidth / ( 2 * gamma2 );
quadrant = LabelPosition::QuadrantBelow;
}
else if ( angleToCandidate < a360 ) // down - right
{
deltaX = rayDistance * std::cos( angleToCandidate );
deltaY = rayDistance * std::sin( angleToCandidate ) - labelHeight;
quadrant = LabelPosition::QuadrantBelowRight;
}
transformRotation.map( deltaX, deltaY, &deltaX, &deltaY );
double labelX = x + deltaX;
double labelY = y + deltaY;
double cost;
if ( rayCount == 1 )
cost = 0.0001;
else
cost = 0.0001 + scaledRayAngleCost;
if ( j > 0 )
{
// cost increases with distance, such that the cost for placing the label at the optimal angle (45)
// but at a greater distance is more then the cost for placing the label at the worst angle (45+180)
// but at the minimum distance
cost += j * RAY_ANGLE_COST_FACTOR + RAY_ANGLE_COST_FACTOR / rayCount;
}
if ( mLF->permissibleZonePrepared() )
{
if ( !GeomFunction::containsCandidate( mLF->permissibleZonePrepared(), labelX, labelY, labelWidth, labelHeight, angle ) )
{
continue;
}
}
lPos.emplace_back( std::make_unique< LabelPosition >( id, labelX, labelY, labelWidth, labelHeight, angle, cost, this, false, quadrant ) );
id++;
numberCandidatesGenerated++;
}
lPos.emplace_back( std::make_unique< LabelPosition >( id + i, labelX, labelY, labelWidth, labelHeight, angle, cost, this, false, quadrant ) );
numberCandidatesGenerated++;
icost += inc;
if ( icost == static_cast< int >( maxNumberCandidates ) )
{
icost = static_cast< int >( maxNumberCandidates ) - 1;
inc = -2;
}
else if ( icost > static_cast< int >( maxNumberCandidates ) )
{
icost = static_cast< int >( maxNumberCandidates ) - 2;
inc = -2;
}
}
return numberCandidatesGenerated;

View File

@ -903,6 +903,18 @@ class CORE_EXPORT Qgis
};
Q_ENUM( LabelOverlapHandling )
/**
* Label prioritization.
*
* \since QGIS 3.38
*/
enum class LabelPrioritization : int
{
PreferCloser, //!< Prefer closer labels, falling back to alternate positions before larger distances
PreferPositionOrdering, //!< Prefer labels follow position ordering, falling back to more distance labels before alternate positions
};
Q_ENUM( LabelPrioritization )
/**
* Placement modes which determine how label candidates are generated for a feature.
*

View File

@ -389,6 +389,11 @@ void QgsLabelingGui::setLayer( QgsMapLayer *mapLayer )
mLineDistanceSpnBx->setValue( mSettings.dist );
mLineDistanceUnitWidget->setUnit( mSettings.distUnits );
mLineDistanceUnitWidget->setMapUnitScale( mSettings.distMapUnitScale );
mMaximumDistanceSpnBx->setValue( mSettings.pointSettings().maximumDistance() );
mMaximumDistanceUnitWidget->setUnit( mSettings.pointSettings().maximumDistanceUnit() );
mMaximumDistanceUnitWidget->setMapUnitScale( mSettings.pointSettings().maximumDistanceMapUnitScale() );
mOffsetTypeComboBox->setCurrentIndex( mOffsetTypeComboBox->findData( static_cast< int >( mSettings.offsetType ) ) );
mQuadrantBtnGrp->button( static_cast<int>( mSettings.pointSettings().quadrant() ) )->setChecked( true );
mPointOffsetXSpinBox->setValue( mSettings.xOffset );
@ -433,6 +438,7 @@ void QgsLabelingGui::setLayer( QgsMapLayer *mapLayer )
mComboOverlapHandling->setCurrentIndex( mComboOverlapHandling->findData( static_cast< int >( mSettings.placementSettings().overlapHandling() ) ) );
mCheckAllowDegradedPlacement->setChecked( mSettings.placementSettings().allowDegradedPlacement() );
mPrioritizationComboBox->setCurrentIndex( mPrioritizationComboBox->findData( QVariant::fromValue( mSettings.placementSettings().prioritization() ) ) );
chkMergeLines->setChecked( mSettings.lineSettings().mergeLines() );
mMinSizeSpinBox->setValue( mSettings.thinningSettings().minimumFeatureSize() );
@ -567,6 +573,11 @@ QgsPalLayerSettings QgsLabelingGui::layerSettings()
lyr.dist = mLineDistanceSpnBx->value();
lyr.distUnits = mLineDistanceUnitWidget->unit();
lyr.distMapUnitScale = mLineDistanceUnitWidget->getMapUnitScale();
lyr.pointSettings().setMaximumDistance( mMaximumDistanceSpnBx->value() );
lyr.pointSettings().setMaximumDistanceUnit( mMaximumDistanceUnitWidget->unit() );
lyr.pointSettings().setMaximumDistanceMapUnitScale( mMaximumDistanceUnitWidget->getMapUnitScale() );
lyr.offsetType = static_cast< Qgis::LabelOffsetType >( mOffsetTypeComboBox->currentData().toInt() );
if ( mQuadrantBtnGrp )
{
@ -612,6 +623,7 @@ QgsPalLayerSettings QgsLabelingGui::layerSettings()
lyr.labelPerPart = chkLabelPerFeaturePart->isChecked();
lyr.placementSettings().setOverlapHandling( static_cast< Qgis::LabelOverlapHandling>( mComboOverlapHandling->currentData().toInt() ) );
lyr.placementSettings().setAllowDegradedPlacement( mCheckAllowDegradedPlacement->isChecked() );
lyr.placementSettings().setPrioritization( mPrioritizationComboBox->currentData().value< Qgis::LabelPrioritization >() );
lyr.lineSettings().setMergeLines( chkMergeLines->isChecked() );

View File

@ -173,6 +173,8 @@ void QgsTextFormatWidget::initWidget()
<< Qgis::RenderUnit::Points << Qgis::RenderUnit::Inches );
mLineDistanceUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << Qgis::RenderUnit::Millimeters << Qgis::RenderUnit::MetersInMapUnits << Qgis::RenderUnit::MapUnits << Qgis::RenderUnit::Pixels
<< Qgis::RenderUnit::Points << Qgis::RenderUnit::Inches );
mMaximumDistanceUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << Qgis::RenderUnit::Millimeters << Qgis::RenderUnit::MetersInMapUnits << Qgis::RenderUnit::MapUnits << Qgis::RenderUnit::Pixels
<< Qgis::RenderUnit::Points << Qgis::RenderUnit::Inches );
mRepeatDistanceUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << Qgis::RenderUnit::Millimeters << Qgis::RenderUnit::MetersInMapUnits << Qgis::RenderUnit::MapUnits << Qgis::RenderUnit::Pixels
<< Qgis::RenderUnit::Points << Qgis::RenderUnit::Inches );
mOverrunDistanceUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << Qgis::RenderUnit::Millimeters << Qgis::RenderUnit::MetersInMapUnits << Qgis::RenderUnit::MapUnits << Qgis::RenderUnit::Pixels
@ -190,6 +192,8 @@ void QgsTextFormatWidget::initWidget()
mFontWordSpacingSpinBox->setClearValue( 0.0 );
mZIndexSpinBox->setClearValue( 0.0 );
mLineDistanceSpnBx->setClearValue( 0.0 );
mMaximumDistanceSpnBx->setMinimum( 0 );
mMaximumDistanceSpnBx->setClearValue( 0.0, tr( "Not set" ) );
mSpinStretch->setClearValue( 100 );
connect( mLineHeightUnitWidget, &QgsUnitSelectionWidget::changed, this, [ = ]
@ -214,6 +218,9 @@ void QgsTextFormatWidget::initWidget()
mComboOverlapHandling->addItem( tr( "Allow Overlaps if Required" ), static_cast< int >( Qgis::LabelOverlapHandling::AllowOverlapIfRequired ) );
mComboOverlapHandling->addItem( tr( "Allow Overlaps without Penalty" ), static_cast< int >( Qgis::LabelOverlapHandling::AllowOverlapAtNoCost ) );
mPrioritizationComboBox->addItem( tr( "Prefer Closer Labels" ), QVariant::fromValue( Qgis::LabelPrioritization::PreferCloser ) );
mPrioritizationComboBox->addItem( tr( "Prefer Position Ordering" ), QVariant::fromValue( Qgis::LabelPrioritization::PreferPositionOrdering ) );
updateAvailableShadowPositions();
mBackgroundMarkerSymbolButton->setSymbolType( Qgis::SymbolType::Marker );
@ -409,6 +416,8 @@ void QgsTextFormatWidget::initWidget()
<< mLimitLabelSpinBox
<< mLineDistanceSpnBx
<< mLineDistanceUnitWidget
<< mMaximumDistanceSpnBx
<< mMaximumDistanceUnitWidget
<< mMaxCharAngleInDSpinBox
<< mMaxCharAngleOutDSpinBox
<< mMinSizeSpinBox
@ -492,7 +501,8 @@ void QgsTextFormatWidget::initWidget()
<< mMaskBufferSizeSpinBox
<< mMaskOpacityWidget
<< mCheckAllowLabelsOutsidePolygons
<< mHtmlFormattingCheckBox;
<< mHtmlFormattingCheckBox
<< mPrioritizationComboBox;
connectValueChanged( widgets, SLOT( updatePreview() ) );
@ -817,6 +827,7 @@ void QgsTextFormatWidget::populateDataDefinedButtons()
registerDataDefinedButton( mPointOffsetUnitsDDBtn, QgsPalLayerSettings::Property::OffsetUnits );
registerDataDefinedButton( mLineDistanceDDBtn, QgsPalLayerSettings::Property::LabelDistance );
registerDataDefinedButton( mLineDistanceUnitDDBtn, QgsPalLayerSettings::Property::DistanceUnits );
registerDataDefinedButton( mMaximumDistanceDDBtn, QgsPalLayerSettings::Property::MaximumDistance );
registerDataDefinedButton( mPriorityDDBtn, QgsPalLayerSettings::Property::Priority );
registerDataDefinedButton( mAllowOutsidePolygonsDDBtn, QgsPalLayerSettings::Property::PolygonLabelOutside );
registerDataDefinedButton( mAllowInferiorPlacementDBtn, QgsPalLayerSettings::Property::AllowDegradedPlacement );
@ -1358,6 +1369,8 @@ void QgsTextFormatWidget::updatePlacementWidgets()
bool showOffsetTypeFrame = false;
bool showOffsetFrame = false;
bool showDistanceFrame = false;
bool showMaximumDistanceFrame = false;
bool showPrioritizationFrame = false;
bool showRotationFrame = false;
bool showMaxCharAngleFrame = false;
@ -1371,6 +1384,7 @@ void QgsTextFormatWidget::updatePlacementWidgets()
{
showCentroidFrame = currentGeometryType == Qgis::GeometryType::Polygon;
showDistanceFrame = true;
showMaximumDistanceFrame = true;
//showRotationFrame = true; // TODO: uncomment when supported
showQuadrantFrame = currentGeometryType == Qgis::GeometryType::Point;
}
@ -1386,7 +1400,9 @@ void QgsTextFormatWidget::updatePlacementWidgets()
else if ( currentGeometryType == Qgis::GeometryType::Point && currentPlacement == Qgis::LabelPlacement::OrderedPositionsAroundPoint )
{
showDistanceFrame = true;
showMaximumDistanceFrame = true;
showPlacementPriorityFrame = true;
showPrioritizationFrame = true;
showOffsetTypeFrame = true;
}
else if ( ( currentGeometryType == Qgis::GeometryType::Line && currentPlacement == Qgis::LabelPlacement::Line )
@ -1422,6 +1438,8 @@ void QgsTextFormatWidget::updatePlacementWidgets()
mPlacementCartographicFrame->setVisible( showPlacementPriorityFrame );
mPlacementOffsetFrame->setVisible( showOffsetFrame );
mPlacementDistanceFrame->setVisible( showDistanceFrame );
mPlacementMaximumDistanceFrame->setVisible( showMaximumDistanceFrame );
mPlacementPrioritizationFrame->setVisible( showPrioritizationFrame );
mPlacementOffsetTypeFrame->setVisible( showOffsetTypeFrame );
mPlacementRotationFrame->setVisible( showRotationFrame );
mPlacementRepeatGroupBox->setVisible( currentGeometryType == Qgis::GeometryType::Line || ( currentGeometryType == Qgis::GeometryType::Polygon &&

File diff suppressed because it is too large Load Diff