mirror of
https://github.com/qgis/QGIS.git
synced 2025-11-28 00:06:23 -05:00
[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:
parent
68e9ba151a
commit
fe74b300be
@ -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
@ -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 );
|
||||
|
||||
@ -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
|
||||
|
||||
};
|
||||
|
||||
@ -121,6 +121,7 @@ Contains settings for how a map layer will be labeled.
|
||||
OffsetXY,
|
||||
OffsetUnits,
|
||||
LabelDistance,
|
||||
MaximumDistance,
|
||||
DistanceUnits,
|
||||
OffsetRotation,
|
||||
CurvedCharAngleInOut,
|
||||
|
||||
@ -550,6 +550,12 @@ The development version
|
||||
AllowOverlapAtNoCost,
|
||||
};
|
||||
|
||||
enum class LabelPrioritization /BaseType=IntEnum/
|
||||
{
|
||||
PreferCloser,
|
||||
PreferPositionOrdering,
|
||||
};
|
||||
|
||||
enum class LabelPlacement /BaseType=IntEnum/
|
||||
{
|
||||
AroundPoint,
|
||||
|
||||
@ -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
@ -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 );
|
||||
|
||||
@ -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
|
||||
|
||||
};
|
||||
|
||||
@ -121,6 +121,7 @@ Contains settings for how a map layer will be labeled.
|
||||
OffsetXY,
|
||||
OffsetUnits,
|
||||
LabelDistance,
|
||||
MaximumDistance,
|
||||
DistanceUnits,
|
||||
OffsetRotation,
|
||||
CurvedCharAngleInOut,
|
||||
|
||||
@ -550,6 +550,12 @@ The development version
|
||||
AllowOverlapAtNoCost,
|
||||
};
|
||||
|
||||
enum class LabelPrioritization
|
||||
{
|
||||
PreferCloser,
|
||||
PreferPositionOrdering,
|
||||
};
|
||||
|
||||
enum class LabelPlacement
|
||||
{
|
||||
AroundPoint,
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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() );
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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.
|
||||
*
|
||||
|
||||
@ -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() );
|
||||
|
||||
|
||||
@ -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
Loading…
x
Reference in New Issue
Block a user