mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-22 00:06:12 -05:00
[api] Implementation of labeling engine rules
See https://github.com/qgis/QGIS-Enhancement-Proposals/issues/299 Implements the API framework for setting advanced labeling engine rules for a project, and implements 4 initial rule types: - QgsLabelingEngineRuleMinimumDistanceLabelToFeature: prevents labels being placed too *close* to features from a different layer - QgsLabelingEngineRuleMaximumDistanceLabelToFeature: prevents labels being placed too *far* from features from a different layer - QgsLabelingEngineRuleMinimumDistanceLabelToLabel: prevents labels being placed too close to labels from a different layer - QgsLabelingEngineRuleAvoidLabelOverlapWithFeature: prevents labels being placed overlapping features from a different layer (note that the first 3 rules require a build based on GEOS >= 3.10, they are not available for older GEOS builds) Also implements a registry for storing available rule classes, and serialization of rules and configuration in QGIS projects
This commit is contained in:
parent
5e1059946f
commit
15a7079a39
@ -69,6 +69,7 @@ if(WITH_APIDOC)
|
||||
${CMAKE_SOURCE_DIR}/src/core/geometry
|
||||
${CMAKE_SOURCE_DIR}/src/core/gps
|
||||
${CMAKE_SOURCE_DIR}/src/core/labeling
|
||||
${CMAKE_SOURCE_DIR}/src/core/labeling/rules
|
||||
${CMAKE_SOURCE_DIR}/src/core/layertree
|
||||
${CMAKE_SOURCE_DIR}/src/core/layout
|
||||
${CMAKE_SOURCE_DIR}/src/core/locator
|
||||
|
@ -133,6 +133,7 @@ try:
|
||||
QgsApplication.renderer3DRegistry = staticmethod(QgsApplication.renderer3DRegistry)
|
||||
QgsApplication.symbol3DRegistry = staticmethod(QgsApplication.symbol3DRegistry)
|
||||
QgsApplication.scaleBarRendererRegistry = staticmethod(QgsApplication.scaleBarRendererRegistry)
|
||||
QgsApplication.labelingEngineRuleRegistry = staticmethod(QgsApplication.labelingEngineRuleRegistry)
|
||||
QgsApplication.projectStorageRegistry = staticmethod(QgsApplication.projectStorageRegistry)
|
||||
QgsApplication.layerMetadataProviderRegistry = staticmethod(QgsApplication.layerMetadataProviderRegistry)
|
||||
QgsApplication.externalStorageRegistry = staticmethod(QgsApplication.externalStorageRegistry)
|
||||
|
@ -0,0 +1,9 @@
|
||||
# The following has been generated automatically from src/core/labeling/rules/qgslabelingenginerule.h
|
||||
try:
|
||||
QgsLabelingEngineContext.__group__ = ['labeling', 'rules']
|
||||
except NameError:
|
||||
pass
|
||||
try:
|
||||
QgsAbstractLabelingEngineRule.__group__ = ['labeling', 'rules']
|
||||
except NameError:
|
||||
pass
|
@ -0,0 +1,21 @@
|
||||
# The following has been generated automatically from src/core/labeling/rules/qgslabelingenginerule_impl.h
|
||||
try:
|
||||
QgsAbstractLabelingEngineRuleDistanceFromFeature.__group__ = ['labeling', 'rules']
|
||||
except NameError:
|
||||
pass
|
||||
try:
|
||||
QgsLabelingEngineRuleMinimumDistanceLabelToFeature.__group__ = ['labeling', 'rules']
|
||||
except NameError:
|
||||
pass
|
||||
try:
|
||||
QgsLabelingEngineRuleMaximumDistanceLabelToFeature.__group__ = ['labeling', 'rules']
|
||||
except NameError:
|
||||
pass
|
||||
try:
|
||||
QgsLabelingEngineRuleMinimumDistanceLabelToLabel.__group__ = ['labeling', 'rules']
|
||||
except NameError:
|
||||
pass
|
||||
try:
|
||||
QgsLabelingEngineRuleAvoidLabelOverlapWithFeature.__group__ = ['labeling', 'rules']
|
||||
except NameError:
|
||||
pass
|
@ -0,0 +1,5 @@
|
||||
# The following has been generated automatically from src/core/labeling/rules/qgslabelingengineruleregistry.h
|
||||
try:
|
||||
QgsLabelingEngineRuleRegistry.__group__ = ['labeling', 'rules']
|
||||
except NameError:
|
||||
pass
|
@ -30,6 +30,9 @@ Stores global configuration for labeling engine
|
||||
};
|
||||
|
||||
QgsLabelingEngineSettings();
|
||||
~QgsLabelingEngineSettings();
|
||||
|
||||
QgsLabelingEngineSettings( const QgsLabelingEngineSettings &other );
|
||||
|
||||
void clear();
|
||||
%Docstring
|
||||
@ -125,13 +128,66 @@ Which search method to use for removal collisions between labels
|
||||
Chain is always used.
|
||||
%End
|
||||
|
||||
|
||||
void readSettingsFromProject( QgsProject *project );
|
||||
%Docstring
|
||||
Read configuration of the labeling engine from a project
|
||||
|
||||
.. note::
|
||||
|
||||
Both this method and :py:func:`~QgsLabelingEngineSettings.readXml` must be called to completely restore the object's state from a project.
|
||||
%End
|
||||
|
||||
void writeSettingsToProject( QgsProject *project );
|
||||
%Docstring
|
||||
Write configuration of the labeling engine to a project
|
||||
Write configuration of the labeling engine to a project.
|
||||
|
||||
.. note::
|
||||
|
||||
Both this method and :py:func:`~QgsLabelingEngineSettings.writeXml` must be called to completely store the object's state in a project.
|
||||
%End
|
||||
|
||||
void writeXml( QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context ) const;
|
||||
%Docstring
|
||||
Writes the label engine settings to an XML ``element``.
|
||||
|
||||
.. note::
|
||||
|
||||
Both this method and :py:func:`~QgsLabelingEngineSettings.writeSettingsToProject` must be called to completely store the object's state in a project.
|
||||
|
||||
.. seealso:: :py:func:`readXml`
|
||||
|
||||
.. seealso:: :py:func:`writeSettingsToProject`
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
void readXml( const QDomElement &element, const QgsReadWriteContext &context );
|
||||
%Docstring
|
||||
Reads the label engine settings from an XML ``element``.
|
||||
|
||||
.. note::
|
||||
|
||||
Both this method and :py:func:`~QgsLabelingEngineSettings.readSettingsFromProject` must be called to completely restore the object's state from a project.
|
||||
|
||||
.. note::
|
||||
|
||||
:py:func:`~QgsLabelingEngineSettings.resolveReferences` must be called following this method.
|
||||
|
||||
.. seealso:: :py:func:`writeXml`
|
||||
|
||||
.. seealso:: :py:func:`readSettingsFromProject`
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
void resolveReferences( const QgsProject *project );
|
||||
%Docstring
|
||||
Resolves reference to layers from stored layer ID.
|
||||
|
||||
Should be called following a call :py:func:`~QgsLabelingEngineSettings.readXml`.
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
|
||||
@ -187,6 +243,47 @@ Sets the placement engine ``version``, which dictates how the label placement pr
|
||||
.. seealso:: :py:func:`placementVersion`
|
||||
|
||||
.. versionadded:: 3.10.2
|
||||
%End
|
||||
|
||||
QList< QgsAbstractLabelingEngineRule * > rules();
|
||||
%Docstring
|
||||
Returns a list of labeling engine rules which must be satifisfied
|
||||
while placing labels.
|
||||
|
||||
.. seealso:: :py:func:`addRule`
|
||||
|
||||
.. seealso:: :py:func:`setRules`
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
|
||||
void addRule( QgsAbstractLabelingEngineRule *rule /Transfer/ );
|
||||
%Docstring
|
||||
Adds a labeling engine ``rule`` which must be satifisfied
|
||||
while placing labels.
|
||||
|
||||
Ownership of the rule is transferred to the settings.
|
||||
|
||||
.. seealso:: :py:func:`rules`
|
||||
|
||||
.. seealso:: :py:func:`setRules`
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
void setRules( const QList< QgsAbstractLabelingEngineRule * > &rules /Transfer/ );
|
||||
%Docstring
|
||||
Sets the labeling engine ``rules`` which must be satifisfied
|
||||
while placing labels.
|
||||
|
||||
Ownership of the rules are transferred to the settings.
|
||||
|
||||
.. seealso:: :py:func:`addRule`
|
||||
|
||||
.. seealso:: :py:func:`rules`
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
};
|
||||
|
@ -0,0 +1,188 @@
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/labeling/rules/qgslabelingenginerule.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.py again *
|
||||
************************************************************************/
|
||||
|
||||
|
||||
|
||||
class QgsLabelingEngineContext
|
||||
{
|
||||
%Docstring(signature="appended")
|
||||
Encapsulates the context for a labeling engine run.
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgslabelingenginerule.h"
|
||||
%End
|
||||
public:
|
||||
|
||||
QgsLabelingEngineContext( QgsRenderContext &renderContext );
|
||||
%Docstring
|
||||
Constructor for QgsLabelingEngineContext.
|
||||
%End
|
||||
|
||||
|
||||
QgsRenderContext &renderContext();
|
||||
%Docstring
|
||||
Returns a reference to the context's render context.
|
||||
%End
|
||||
|
||||
|
||||
QgsRectangle extent() const;
|
||||
%Docstring
|
||||
Returns the map extent defining the limits for labeling.
|
||||
|
||||
.. seealso:: :py:func:`mapBoundaryGeometry`
|
||||
|
||||
.. seealso:: :py:func:`setExtent`
|
||||
%End
|
||||
|
||||
void setExtent( const QgsRectangle &extent );
|
||||
%Docstring
|
||||
Sets the map ``extent`` defining the limits for labeling.
|
||||
|
||||
.. seealso:: :py:func:`setMapBoundaryGeometry`
|
||||
|
||||
.. seealso:: :py:func:`extent`
|
||||
%End
|
||||
|
||||
QgsGeometry mapBoundaryGeometry() const;
|
||||
%Docstring
|
||||
Returns the map label boundary geometry, which defines the limits within which labels may be placed
|
||||
in the map.
|
||||
|
||||
The map boundary geometry specifies the actual geometry of the map
|
||||
boundary, which will be used to detect whether a label is visible (or partially visible) in
|
||||
the rendered map. This may differ from :py:func:`~QgsLabelingEngineContext.extent` in the case of rotated or non-rectangular
|
||||
maps.
|
||||
|
||||
.. seealso:: :py:func:`setMapBoundaryGeometry`
|
||||
|
||||
.. seealso:: :py:func:`extent`
|
||||
%End
|
||||
|
||||
void setMapBoundaryGeometry( const QgsGeometry &geometry );
|
||||
%Docstring
|
||||
Sets the map label boundary ``geometry``, which defines the limits within which labels may be placed
|
||||
in the map.
|
||||
|
||||
The map boundary geometry specifies the actual geometry of the map
|
||||
boundary, which will be used to detect whether a label is visible (or partially visible) in
|
||||
the rendered map. This may differ from :py:func:`~QgsLabelingEngineContext.extent` in the case of rotated or non-rectangular
|
||||
maps.
|
||||
|
||||
.. seealso:: :py:func:`setExtent`
|
||||
|
||||
.. seealso:: :py:func:`mapBoundaryGeometry`
|
||||
%End
|
||||
|
||||
private:
|
||||
QgsLabelingEngineContext( const QgsLabelingEngineContext &other );
|
||||
};
|
||||
|
||||
class QgsAbstractLabelingEngineRule
|
||||
{
|
||||
%Docstring(signature="appended")
|
||||
Abstract base class for labeling engine rules.
|
||||
|
||||
Labeling engine rules implement custom logic to modify the labeling solution for a map render,
|
||||
e.g. by preventing labels being placed which violate custom constraints.
|
||||
|
||||
.. note::
|
||||
|
||||
:py:class:`QgsAbstractLabelingEngineRule` cannot be subclassed in Python. Use one of the existing
|
||||
implementations of this class instead.
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgslabelingenginerule.h"
|
||||
%End
|
||||
%ConvertToSubClassCode
|
||||
if ( sipCpp->id() == "minimumDistanceLabelToFeature" )
|
||||
{
|
||||
sipType = sipType_QgsLabelingEngineRuleMinimumDistanceLabelToFeature;
|
||||
}
|
||||
else if ( sipCpp->id() == "minimumDistanceLabelToLabel" )
|
||||
{
|
||||
sipType = sipType_QgsLabelingEngineRuleMinimumDistanceLabelToLabel;
|
||||
}
|
||||
else if ( sipCpp->id() == "maximumDistanceLabelToFeature" )
|
||||
{
|
||||
sipType = sipType_QgsLabelingEngineRuleMaximumDistanceLabelToFeature;
|
||||
}
|
||||
else if ( sipCpp->id() == "avoidLabelOverlapWithFeature" )
|
||||
{
|
||||
sipType = sipType_QgsLabelingEngineRuleAvoidLabelOverlapWithFeature;
|
||||
}
|
||||
else
|
||||
{
|
||||
sipType = 0;
|
||||
}
|
||||
%End
|
||||
public:
|
||||
|
||||
virtual ~QgsAbstractLabelingEngineRule();
|
||||
|
||||
virtual QgsAbstractLabelingEngineRule *clone() const = 0 /Factory/;
|
||||
%Docstring
|
||||
Creates a clone of this rule.
|
||||
|
||||
The caller takes ownership of the returned object.
|
||||
%End
|
||||
|
||||
virtual QString id() const = 0;
|
||||
%Docstring
|
||||
Returns a string uniquely identifying the rule subclass.
|
||||
%End
|
||||
|
||||
virtual bool prepare( QgsRenderContext &context ) = 0;
|
||||
%Docstring
|
||||
Prepares the rule.
|
||||
|
||||
This must be called on the main render thread, prior to commencing the render operation. Thread sensitive
|
||||
logic (such as creation of feature sources) can be performed in this method.
|
||||
%End
|
||||
|
||||
virtual void writeXml( QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context ) const = 0;
|
||||
%Docstring
|
||||
Writes the rule properties to an XML ``element``.
|
||||
|
||||
.. seealso:: :py:func:`readXml`
|
||||
%End
|
||||
|
||||
virtual void readXml( const QDomElement &element, const QgsReadWriteContext &context ) = 0;
|
||||
%Docstring
|
||||
Reads the rule properties from an XML ``element``.
|
||||
|
||||
.. seealso:: :py:func:`resolveReferences`
|
||||
|
||||
.. seealso:: :py:func:`writeXml`
|
||||
%End
|
||||
|
||||
virtual void resolveReferences( const QgsProject *project );
|
||||
%Docstring
|
||||
Resolves reference to layers from stored layer ID.
|
||||
|
||||
Should be called following a call :py:func:`~QgsAbstractLabelingEngineRule.readXml`.
|
||||
%End
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/labeling/rules/qgslabelingenginerule.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.py again *
|
||||
************************************************************************/
|
@ -0,0 +1,393 @@
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/labeling/rules/qgslabelingenginerule_impl.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.py again *
|
||||
************************************************************************/
|
||||
|
||||
|
||||
|
||||
|
||||
class QgsAbstractLabelingEngineRuleDistanceFromFeature : QgsAbstractLabelingEngineRule
|
||||
{
|
||||
%Docstring(signature="appended")
|
||||
Base class for labeling engine rules which prevents labels being placed too close or to far from features from a different layer.
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgslabelingenginerule_impl.h"
|
||||
%End
|
||||
public:
|
||||
|
||||
QgsAbstractLabelingEngineRuleDistanceFromFeature();
|
||||
~QgsAbstractLabelingEngineRuleDistanceFromFeature();
|
||||
virtual bool prepare( QgsRenderContext &context );
|
||||
|
||||
virtual void writeXml( QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context ) const;
|
||||
|
||||
virtual void readXml( const QDomElement &element, const QgsReadWriteContext &context );
|
||||
|
||||
virtual void resolveReferences( const QgsProject *project );
|
||||
|
||||
|
||||
QgsVectorLayer *labeledLayer();
|
||||
%Docstring
|
||||
Returns the layer providing the labels.
|
||||
|
||||
.. seealso:: :py:func:`setLabeledLayer`
|
||||
%End
|
||||
|
||||
void setLabeledLayer( QgsVectorLayer *layer );
|
||||
%Docstring
|
||||
Sets the ``layer`` providing the labels.
|
||||
|
||||
.. seealso:: :py:func:`labeledLayer`
|
||||
%End
|
||||
|
||||
QgsVectorLayer *targetLayer();
|
||||
%Docstring
|
||||
Returns the layer providing the features which labels must be distant from (or close to).
|
||||
|
||||
.. seealso:: :py:func:`setTargetLayer`
|
||||
%End
|
||||
|
||||
void setTargetLayer( QgsVectorLayer *layer );
|
||||
%Docstring
|
||||
Sets the ``layer`` providing the features which labels must be distant from (or close to).
|
||||
|
||||
.. seealso:: :py:func:`targetLayer`
|
||||
%End
|
||||
|
||||
double distance() const;
|
||||
%Docstring
|
||||
Returns the acceptable distance threshold between labels and the features
|
||||
from the :py:func:`~QgsAbstractLabelingEngineRuleDistanceFromFeature.targetLayer`.
|
||||
|
||||
.. seealso:: :py:func:`setDistance`
|
||||
|
||||
.. seealso:: :py:func:`distanceUnits`
|
||||
%End
|
||||
|
||||
void setDistance( double distance );
|
||||
%Docstring
|
||||
Sets the acceptable ``distance`` threshold between labels and the features
|
||||
from the :py:func:`~QgsAbstractLabelingEngineRuleDistanceFromFeature.targetLayer`.
|
||||
|
||||
.. seealso:: :py:func:`distance`
|
||||
|
||||
.. seealso:: :py:func:`setDistanceUnits`
|
||||
%End
|
||||
|
||||
Qgis::RenderUnit distanceUnit() const;
|
||||
%Docstring
|
||||
Returns the units for the distance between labels and the features
|
||||
from the :py:func:`~QgsAbstractLabelingEngineRuleDistanceFromFeature.targetLayer`.
|
||||
|
||||
.. seealso:: :py:func:`setDistanceUnit`
|
||||
|
||||
.. seealso:: :py:func:`distance`
|
||||
%End
|
||||
|
||||
void setDistanceUnit( Qgis::RenderUnit unit );
|
||||
%Docstring
|
||||
Sets the ``unit`` for the distance between labels and the features
|
||||
from the :py:func:`~QgsAbstractLabelingEngineRuleDistanceFromFeature.targetLayer`.
|
||||
|
||||
.. seealso:: :py:func:`distanceUnit`
|
||||
|
||||
.. seealso:: :py:func:`setDistance`
|
||||
%End
|
||||
|
||||
const QgsMapUnitScale &distanceUnitScale() const;
|
||||
%Docstring
|
||||
Returns the scaling for the distance between labels and the features
|
||||
from the :py:func:`~QgsAbstractLabelingEngineRuleDistanceFromFeature.targetLayer`.
|
||||
|
||||
.. seealso:: :py:func:`setDistanceUnitScale`
|
||||
|
||||
.. seealso:: :py:func:`distance`
|
||||
%End
|
||||
|
||||
void setDistanceUnitScale( const QgsMapUnitScale &scale );
|
||||
%Docstring
|
||||
Sets the ``scale`` for the distance between labels and the features
|
||||
from the :py:func:`~QgsAbstractLabelingEngineRuleDistanceFromFeature.targetLayer`.
|
||||
|
||||
.. seealso:: :py:func:`distanceUnitScale`
|
||||
|
||||
.. seealso:: :py:func:`setDistance`
|
||||
%End
|
||||
|
||||
double cost() const;
|
||||
%Docstring
|
||||
Returns the penalty cost incurred when the rule is violated.
|
||||
|
||||
This is a value between 0 and 10, where 10 indicates that the rule must never be violated,
|
||||
and 1-9 = nice to have if possible, where higher numbers will try harder to avoid violating the rule.
|
||||
|
||||
.. seealso:: :py:func:`setCost`
|
||||
%End
|
||||
|
||||
void setCost( double cost );
|
||||
%Docstring
|
||||
Sets the penalty ``cost`` incurred when the rule is violated.
|
||||
|
||||
This is a value between 0 and 10, where 10 indicates that the rule must never be violated,
|
||||
and 1-9 = nice to have if possible, where higher numbers will try harder to avoid violating the rule.
|
||||
|
||||
.. seealso:: :py:func:`cost`
|
||||
%End
|
||||
|
||||
protected:
|
||||
|
||||
void copyCommonProperties( QgsAbstractLabelingEngineRuleDistanceFromFeature *other ) const;
|
||||
%Docstring
|
||||
Copies common properties from this object to an ``other``.
|
||||
%End
|
||||
|
||||
|
||||
private:
|
||||
QgsAbstractLabelingEngineRuleDistanceFromFeature( const QgsAbstractLabelingEngineRuleDistanceFromFeature &other );
|
||||
};
|
||||
|
||||
|
||||
class QgsLabelingEngineRuleMinimumDistanceLabelToFeature : QgsAbstractLabelingEngineRuleDistanceFromFeature
|
||||
{
|
||||
%Docstring(signature="appended")
|
||||
A labeling engine rule which prevents labels being placed too close to features from a different layer.
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgslabelingenginerule_impl.h"
|
||||
%End
|
||||
public:
|
||||
|
||||
QgsLabelingEngineRuleMinimumDistanceLabelToFeature();
|
||||
~QgsLabelingEngineRuleMinimumDistanceLabelToFeature();
|
||||
virtual QgsLabelingEngineRuleMinimumDistanceLabelToFeature *clone() const /Factory/;
|
||||
|
||||
virtual QString id() const;
|
||||
|
||||
|
||||
private:
|
||||
QgsLabelingEngineRuleMinimumDistanceLabelToFeature( const QgsLabelingEngineRuleMinimumDistanceLabelToFeature & );
|
||||
};
|
||||
|
||||
|
||||
class QgsLabelingEngineRuleMaximumDistanceLabelToFeature : QgsAbstractLabelingEngineRuleDistanceFromFeature
|
||||
{
|
||||
%Docstring(signature="appended")
|
||||
A labeling engine rule which prevents labels being placed too far from features from a different layer.
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgslabelingenginerule_impl.h"
|
||||
%End
|
||||
public:
|
||||
QgsLabelingEngineRuleMaximumDistanceLabelToFeature();
|
||||
~QgsLabelingEngineRuleMaximumDistanceLabelToFeature();
|
||||
virtual QgsLabelingEngineRuleMaximumDistanceLabelToFeature *clone() const /Factory/;
|
||||
|
||||
virtual QString id() const;
|
||||
|
||||
|
||||
private:
|
||||
QgsLabelingEngineRuleMaximumDistanceLabelToFeature( const QgsLabelingEngineRuleMaximumDistanceLabelToFeature & );
|
||||
};
|
||||
|
||||
class QgsLabelingEngineRuleMinimumDistanceLabelToLabel : QgsAbstractLabelingEngineRule
|
||||
{
|
||||
%Docstring(signature="appended")
|
||||
A labeling engine rule which prevents labels being placed too close to labels from a different layer.
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgslabelingenginerule_impl.h"
|
||||
%End
|
||||
public:
|
||||
QgsLabelingEngineRuleMinimumDistanceLabelToLabel();
|
||||
~QgsLabelingEngineRuleMinimumDistanceLabelToLabel();
|
||||
|
||||
virtual QgsLabelingEngineRuleMinimumDistanceLabelToLabel *clone() const /Factory/;
|
||||
|
||||
virtual QString id() const;
|
||||
|
||||
virtual void writeXml( QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context ) const;
|
||||
|
||||
virtual void readXml( const QDomElement &element, const QgsReadWriteContext &context );
|
||||
|
||||
virtual void resolveReferences( const QgsProject *project );
|
||||
|
||||
virtual bool prepare( QgsRenderContext &context );
|
||||
|
||||
|
||||
QgsVectorLayer *labeledLayer();
|
||||
%Docstring
|
||||
Returns the layer providing the labels.
|
||||
|
||||
.. seealso:: :py:func:`setLabeledLayer`
|
||||
%End
|
||||
|
||||
void setLabeledLayer( QgsVectorLayer *layer );
|
||||
%Docstring
|
||||
Sets the ``layer`` providing the labels.
|
||||
|
||||
.. seealso:: :py:func:`labeledLayer`
|
||||
%End
|
||||
|
||||
QgsVectorLayer *targetLayer();
|
||||
%Docstring
|
||||
Returns the layer providing the labels which labels must be distant from.
|
||||
|
||||
.. seealso:: :py:func:`setTargetLayer`
|
||||
%End
|
||||
|
||||
void setTargetLayer( QgsVectorLayer *layer );
|
||||
%Docstring
|
||||
Sets the ``layer`` providing the labels which labels must be distant from.
|
||||
|
||||
.. seealso:: :py:func:`targetLayer`
|
||||
%End
|
||||
|
||||
double distance() const;
|
||||
%Docstring
|
||||
Returns the minimum permitted distance between labels from the :py:func:`~QgsLabelingEngineRuleMinimumDistanceLabelToLabel.labeledLayer` and the labels
|
||||
from the :py:func:`~QgsLabelingEngineRuleMinimumDistanceLabelToLabel.targetLayer`.
|
||||
|
||||
.. seealso:: :py:func:`setDistance`
|
||||
|
||||
.. seealso:: :py:func:`distanceUnits`
|
||||
%End
|
||||
|
||||
void setDistance( double distance );
|
||||
%Docstring
|
||||
Sets the minimum permitted ``distance`` between labels from the :py:func:`~QgsLabelingEngineRuleMinimumDistanceLabelToLabel.labeledLayer` and the labels
|
||||
from the :py:func:`~QgsLabelingEngineRuleMinimumDistanceLabelToLabel.targetLayer`.
|
||||
|
||||
.. seealso:: :py:func:`distance`
|
||||
|
||||
.. seealso:: :py:func:`setDistanceUnits`
|
||||
%End
|
||||
|
||||
Qgis::RenderUnit distanceUnit() const;
|
||||
%Docstring
|
||||
Returns the units for the distance between labels from the :py:func:`~QgsLabelingEngineRuleMinimumDistanceLabelToLabel.labeledLayer` and the labels
|
||||
from the :py:func:`~QgsLabelingEngineRuleMinimumDistanceLabelToLabel.targetLayer`.
|
||||
|
||||
.. seealso:: :py:func:`setDistanceUnit`
|
||||
|
||||
.. seealso:: :py:func:`distance`
|
||||
%End
|
||||
|
||||
void setDistanceUnit( Qgis::RenderUnit unit );
|
||||
%Docstring
|
||||
Sets the ``unit`` for the distance between labels from the :py:func:`~QgsLabelingEngineRuleMinimumDistanceLabelToLabel.labeledLayer` and the labels
|
||||
from the :py:func:`~QgsLabelingEngineRuleMinimumDistanceLabelToLabel.targetLayer`.
|
||||
|
||||
.. seealso:: :py:func:`distanceUnit`
|
||||
|
||||
.. seealso:: :py:func:`setDistance`
|
||||
%End
|
||||
|
||||
const QgsMapUnitScale &distanceUnitScale() const;
|
||||
%Docstring
|
||||
Returns the scaling for the distance between labels from the :py:func:`~QgsLabelingEngineRuleMinimumDistanceLabelToLabel.labeledLayer` and the labels
|
||||
from the :py:func:`~QgsLabelingEngineRuleMinimumDistanceLabelToLabel.targetLayer`.
|
||||
|
||||
.. seealso:: :py:func:`setDistanceUnitScale`
|
||||
|
||||
.. seealso:: :py:func:`distance`
|
||||
%End
|
||||
|
||||
void setDistanceUnitScale( const QgsMapUnitScale &scale );
|
||||
%Docstring
|
||||
Sets the ``scale`` for the distance between labels from the :py:func:`~QgsLabelingEngineRuleMinimumDistanceLabelToLabel.labeledLayer` and the labels
|
||||
from the :py:func:`~QgsLabelingEngineRuleMinimumDistanceLabelToLabel.targetLayer`.
|
||||
|
||||
.. seealso:: :py:func:`distanceUnitScale`
|
||||
|
||||
.. seealso:: :py:func:`setDistance`
|
||||
%End
|
||||
|
||||
private:
|
||||
QgsLabelingEngineRuleMinimumDistanceLabelToLabel( const QgsLabelingEngineRuleMinimumDistanceLabelToLabel & );
|
||||
};
|
||||
|
||||
|
||||
class QgsLabelingEngineRuleAvoidLabelOverlapWithFeature : QgsAbstractLabelingEngineRule
|
||||
{
|
||||
%Docstring(signature="appended")
|
||||
A labeling engine rule which prevents labels being placed overlapping features from a different layer.
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgslabelingenginerule_impl.h"
|
||||
%End
|
||||
public:
|
||||
|
||||
QgsLabelingEngineRuleAvoidLabelOverlapWithFeature();
|
||||
~QgsLabelingEngineRuleAvoidLabelOverlapWithFeature();
|
||||
virtual QgsLabelingEngineRuleAvoidLabelOverlapWithFeature *clone() const /Factory/;
|
||||
|
||||
virtual QString id() const;
|
||||
|
||||
virtual bool prepare( QgsRenderContext &context );
|
||||
|
||||
virtual void writeXml( QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context ) const;
|
||||
|
||||
virtual void readXml( const QDomElement &element, const QgsReadWriteContext &context );
|
||||
|
||||
virtual void resolveReferences( const QgsProject *project );
|
||||
|
||||
|
||||
QgsVectorLayer *labeledLayer();
|
||||
%Docstring
|
||||
Returns the layer providing the labels.
|
||||
|
||||
.. seealso:: :py:func:`setLabeledLayer`
|
||||
%End
|
||||
|
||||
void setLabeledLayer( QgsVectorLayer *layer );
|
||||
%Docstring
|
||||
Sets the ``layer`` providing the labels.
|
||||
|
||||
.. seealso:: :py:func:`labeledLayer`
|
||||
%End
|
||||
|
||||
QgsVectorLayer *targetLayer();
|
||||
%Docstring
|
||||
Returns the layer providing the features which labels must not overlap.
|
||||
|
||||
.. seealso:: :py:func:`setTargetLayer`
|
||||
%End
|
||||
|
||||
void setTargetLayer( QgsVectorLayer *layer );
|
||||
%Docstring
|
||||
Sets the ``layer`` providing the features which labels must not overlap.
|
||||
|
||||
.. seealso:: :py:func:`targetLayer`
|
||||
%End
|
||||
|
||||
private:
|
||||
QgsLabelingEngineRuleAvoidLabelOverlapWithFeature( const QgsLabelingEngineRuleAvoidLabelOverlapWithFeature & );
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/labeling/rules/qgslabelingenginerule_impl.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.py again *
|
||||
************************************************************************/
|
@ -0,0 +1,82 @@
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/labeling/rules/qgslabelingengineruleregistry.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.py again *
|
||||
************************************************************************/
|
||||
|
||||
|
||||
|
||||
class QgsLabelingEngineRuleRegistry
|
||||
{
|
||||
%Docstring(signature="appended")
|
||||
A registry for labeling engine rules.
|
||||
|
||||
Labeling engine rules implement custom logic to modify the labeling solution for a map render,
|
||||
e.g. by preventing labels being placed which violate custom constraints.
|
||||
|
||||
This registry stores available rules and is responsible for creating rules.
|
||||
|
||||
:py:class:`QgsLabelingEngineRuleRegistry` is not usually directly created, but rather accessed through
|
||||
:py:func:`QgsApplication.labelEngineRuleRegistry()`.
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgslabelingengineruleregistry.h"
|
||||
%End
|
||||
public:
|
||||
|
||||
QgsLabelingEngineRuleRegistry();
|
||||
%Docstring
|
||||
Constructor for QgsLabelingEngineRuleRegistry, containing a set of
|
||||
default rules.
|
||||
%End
|
||||
~QgsLabelingEngineRuleRegistry();
|
||||
|
||||
|
||||
QStringList ruleIds() const;
|
||||
%Docstring
|
||||
Returns a list of the rule IDs for rules present in the registry.
|
||||
%End
|
||||
|
||||
QgsAbstractLabelingEngineRule *create( const QString &id ) const /TransferBack/;
|
||||
%Docstring
|
||||
Creates a new rule from the type with matching ``id``.
|
||||
|
||||
Returns ``None`` if no matching rule was found in the registry.
|
||||
|
||||
The caller takes ownership of the returned object.
|
||||
%End
|
||||
|
||||
bool addRule( QgsAbstractLabelingEngineRule *rule /Transfer/ );
|
||||
%Docstring
|
||||
Adds a new ``rule`` type to the registry.
|
||||
|
||||
The registry takes ownership of ``rule``.
|
||||
|
||||
:return: ``True`` if the rule was successfully added.
|
||||
|
||||
.. seealso:: :py:func:`removeRule`
|
||||
%End
|
||||
|
||||
void removeRule( const QString &id );
|
||||
%Docstring
|
||||
Removes the rule with matching ``id`` from the registry.
|
||||
|
||||
.. seealso:: :py:func:`addRule`
|
||||
%End
|
||||
|
||||
private:
|
||||
QgsLabelingEngineRuleRegistry( const QgsLabelingEngineRuleRegistry &other );
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/labeling/rules/qgslabelingengineruleregistry.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.py again *
|
||||
************************************************************************/
|
@ -944,6 +944,13 @@ Returns registry of available 3D symbols.
|
||||
Gets the registry of available scalebar renderers.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
%End
|
||||
|
||||
static QgsLabelingEngineRuleRegistry *labelingEngineRuleRegistry() /KeepReference/;
|
||||
%Docstring
|
||||
Gets the registry of available labeling engine rules.
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
static QgsProjectStorageRegistry *projectStorageRegistry() /KeepReference/;
|
||||
|
@ -391,6 +391,9 @@
|
||||
%Include auto_generated/labeling/qgspallabeling.sip
|
||||
%Include auto_generated/labeling/qgsrulebasedlabeling.sip
|
||||
%Include auto_generated/labeling/qgsvectorlayerlabeling.sip
|
||||
%Include auto_generated/labeling/rules/qgslabelingenginerule.sip
|
||||
%Include auto_generated/labeling/rules/qgslabelingenginerule_impl.sip
|
||||
%Include auto_generated/labeling/rules/qgslabelingengineruleregistry.sip
|
||||
%Include auto_generated/layertree/qgscolorramplegendnode.sip
|
||||
%Include auto_generated/layertree/qgscolorramplegendnodesettings.sip
|
||||
%Include auto_generated/layertree/qgslayertree.sip
|
||||
|
@ -122,6 +122,7 @@ try:
|
||||
QgsApplication.renderer3DRegistry = staticmethod(QgsApplication.renderer3DRegistry)
|
||||
QgsApplication.symbol3DRegistry = staticmethod(QgsApplication.symbol3DRegistry)
|
||||
QgsApplication.scaleBarRendererRegistry = staticmethod(QgsApplication.scaleBarRendererRegistry)
|
||||
QgsApplication.labelingEngineRuleRegistry = staticmethod(QgsApplication.labelingEngineRuleRegistry)
|
||||
QgsApplication.projectStorageRegistry = staticmethod(QgsApplication.projectStorageRegistry)
|
||||
QgsApplication.layerMetadataProviderRegistry = staticmethod(QgsApplication.layerMetadataProviderRegistry)
|
||||
QgsApplication.externalStorageRegistry = staticmethod(QgsApplication.externalStorageRegistry)
|
||||
|
9
python/core/auto_additions/qgslabelingenginerule.py
Normal file
9
python/core/auto_additions/qgslabelingenginerule.py
Normal file
@ -0,0 +1,9 @@
|
||||
# The following has been generated automatically from src/core/labeling/rules/qgslabelingenginerule.h
|
||||
try:
|
||||
QgsLabelingEngineContext.__group__ = ['labeling', 'rules']
|
||||
except NameError:
|
||||
pass
|
||||
try:
|
||||
QgsAbstractLabelingEngineRule.__group__ = ['labeling', 'rules']
|
||||
except NameError:
|
||||
pass
|
21
python/core/auto_additions/qgslabelingenginerule_impl.py
Normal file
21
python/core/auto_additions/qgslabelingenginerule_impl.py
Normal file
@ -0,0 +1,21 @@
|
||||
# The following has been generated automatically from src/core/labeling/rules/qgslabelingenginerule_impl.h
|
||||
try:
|
||||
QgsAbstractLabelingEngineRuleDistanceFromFeature.__group__ = ['labeling', 'rules']
|
||||
except NameError:
|
||||
pass
|
||||
try:
|
||||
QgsLabelingEngineRuleMinimumDistanceLabelToFeature.__group__ = ['labeling', 'rules']
|
||||
except NameError:
|
||||
pass
|
||||
try:
|
||||
QgsLabelingEngineRuleMaximumDistanceLabelToFeature.__group__ = ['labeling', 'rules']
|
||||
except NameError:
|
||||
pass
|
||||
try:
|
||||
QgsLabelingEngineRuleMinimumDistanceLabelToLabel.__group__ = ['labeling', 'rules']
|
||||
except NameError:
|
||||
pass
|
||||
try:
|
||||
QgsLabelingEngineRuleAvoidLabelOverlapWithFeature.__group__ = ['labeling', 'rules']
|
||||
except NameError:
|
||||
pass
|
@ -0,0 +1,5 @@
|
||||
# The following has been generated automatically from src/core/labeling/rules/qgslabelingengineruleregistry.h
|
||||
try:
|
||||
QgsLabelingEngineRuleRegistry.__group__ = ['labeling', 'rules']
|
||||
except NameError:
|
||||
pass
|
@ -30,6 +30,9 @@ Stores global configuration for labeling engine
|
||||
};
|
||||
|
||||
QgsLabelingEngineSettings();
|
||||
~QgsLabelingEngineSettings();
|
||||
|
||||
QgsLabelingEngineSettings( const QgsLabelingEngineSettings &other );
|
||||
|
||||
void clear();
|
||||
%Docstring
|
||||
@ -125,13 +128,66 @@ Which search method to use for removal collisions between labels
|
||||
Chain is always used.
|
||||
%End
|
||||
|
||||
|
||||
void readSettingsFromProject( QgsProject *project );
|
||||
%Docstring
|
||||
Read configuration of the labeling engine from a project
|
||||
|
||||
.. note::
|
||||
|
||||
Both this method and :py:func:`~QgsLabelingEngineSettings.readXml` must be called to completely restore the object's state from a project.
|
||||
%End
|
||||
|
||||
void writeSettingsToProject( QgsProject *project );
|
||||
%Docstring
|
||||
Write configuration of the labeling engine to a project
|
||||
Write configuration of the labeling engine to a project.
|
||||
|
||||
.. note::
|
||||
|
||||
Both this method and :py:func:`~QgsLabelingEngineSettings.writeXml` must be called to completely store the object's state in a project.
|
||||
%End
|
||||
|
||||
void writeXml( QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context ) const;
|
||||
%Docstring
|
||||
Writes the label engine settings to an XML ``element``.
|
||||
|
||||
.. note::
|
||||
|
||||
Both this method and :py:func:`~QgsLabelingEngineSettings.writeSettingsToProject` must be called to completely store the object's state in a project.
|
||||
|
||||
.. seealso:: :py:func:`readXml`
|
||||
|
||||
.. seealso:: :py:func:`writeSettingsToProject`
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
void readXml( const QDomElement &element, const QgsReadWriteContext &context );
|
||||
%Docstring
|
||||
Reads the label engine settings from an XML ``element``.
|
||||
|
||||
.. note::
|
||||
|
||||
Both this method and :py:func:`~QgsLabelingEngineSettings.readSettingsFromProject` must be called to completely restore the object's state from a project.
|
||||
|
||||
.. note::
|
||||
|
||||
:py:func:`~QgsLabelingEngineSettings.resolveReferences` must be called following this method.
|
||||
|
||||
.. seealso:: :py:func:`writeXml`
|
||||
|
||||
.. seealso:: :py:func:`readSettingsFromProject`
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
void resolveReferences( const QgsProject *project );
|
||||
%Docstring
|
||||
Resolves reference to layers from stored layer ID.
|
||||
|
||||
Should be called following a call :py:func:`~QgsLabelingEngineSettings.readXml`.
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
|
||||
@ -187,6 +243,47 @@ Sets the placement engine ``version``, which dictates how the label placement pr
|
||||
.. seealso:: :py:func:`placementVersion`
|
||||
|
||||
.. versionadded:: 3.10.2
|
||||
%End
|
||||
|
||||
QList< QgsAbstractLabelingEngineRule * > rules();
|
||||
%Docstring
|
||||
Returns a list of labeling engine rules which must be satifisfied
|
||||
while placing labels.
|
||||
|
||||
.. seealso:: :py:func:`addRule`
|
||||
|
||||
.. seealso:: :py:func:`setRules`
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
|
||||
void addRule( QgsAbstractLabelingEngineRule *rule /Transfer/ );
|
||||
%Docstring
|
||||
Adds a labeling engine ``rule`` which must be satifisfied
|
||||
while placing labels.
|
||||
|
||||
Ownership of the rule is transferred to the settings.
|
||||
|
||||
.. seealso:: :py:func:`rules`
|
||||
|
||||
.. seealso:: :py:func:`setRules`
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
void setRules( const QList< QgsAbstractLabelingEngineRule * > &rules /Transfer/ );
|
||||
%Docstring
|
||||
Sets the labeling engine ``rules`` which must be satifisfied
|
||||
while placing labels.
|
||||
|
||||
Ownership of the rules are transferred to the settings.
|
||||
|
||||
.. seealso:: :py:func:`addRule`
|
||||
|
||||
.. seealso:: :py:func:`rules`
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
};
|
||||
|
@ -0,0 +1,188 @@
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/labeling/rules/qgslabelingenginerule.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.py again *
|
||||
************************************************************************/
|
||||
|
||||
|
||||
|
||||
class QgsLabelingEngineContext
|
||||
{
|
||||
%Docstring(signature="appended")
|
||||
Encapsulates the context for a labeling engine run.
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgslabelingenginerule.h"
|
||||
%End
|
||||
public:
|
||||
|
||||
QgsLabelingEngineContext( QgsRenderContext &renderContext );
|
||||
%Docstring
|
||||
Constructor for QgsLabelingEngineContext.
|
||||
%End
|
||||
|
||||
|
||||
QgsRenderContext &renderContext();
|
||||
%Docstring
|
||||
Returns a reference to the context's render context.
|
||||
%End
|
||||
|
||||
|
||||
QgsRectangle extent() const;
|
||||
%Docstring
|
||||
Returns the map extent defining the limits for labeling.
|
||||
|
||||
.. seealso:: :py:func:`mapBoundaryGeometry`
|
||||
|
||||
.. seealso:: :py:func:`setExtent`
|
||||
%End
|
||||
|
||||
void setExtent( const QgsRectangle &extent );
|
||||
%Docstring
|
||||
Sets the map ``extent`` defining the limits for labeling.
|
||||
|
||||
.. seealso:: :py:func:`setMapBoundaryGeometry`
|
||||
|
||||
.. seealso:: :py:func:`extent`
|
||||
%End
|
||||
|
||||
QgsGeometry mapBoundaryGeometry() const;
|
||||
%Docstring
|
||||
Returns the map label boundary geometry, which defines the limits within which labels may be placed
|
||||
in the map.
|
||||
|
||||
The map boundary geometry specifies the actual geometry of the map
|
||||
boundary, which will be used to detect whether a label is visible (or partially visible) in
|
||||
the rendered map. This may differ from :py:func:`~QgsLabelingEngineContext.extent` in the case of rotated or non-rectangular
|
||||
maps.
|
||||
|
||||
.. seealso:: :py:func:`setMapBoundaryGeometry`
|
||||
|
||||
.. seealso:: :py:func:`extent`
|
||||
%End
|
||||
|
||||
void setMapBoundaryGeometry( const QgsGeometry &geometry );
|
||||
%Docstring
|
||||
Sets the map label boundary ``geometry``, which defines the limits within which labels may be placed
|
||||
in the map.
|
||||
|
||||
The map boundary geometry specifies the actual geometry of the map
|
||||
boundary, which will be used to detect whether a label is visible (or partially visible) in
|
||||
the rendered map. This may differ from :py:func:`~QgsLabelingEngineContext.extent` in the case of rotated or non-rectangular
|
||||
maps.
|
||||
|
||||
.. seealso:: :py:func:`setExtent`
|
||||
|
||||
.. seealso:: :py:func:`mapBoundaryGeometry`
|
||||
%End
|
||||
|
||||
private:
|
||||
QgsLabelingEngineContext( const QgsLabelingEngineContext &other );
|
||||
};
|
||||
|
||||
class QgsAbstractLabelingEngineRule
|
||||
{
|
||||
%Docstring(signature="appended")
|
||||
Abstract base class for labeling engine rules.
|
||||
|
||||
Labeling engine rules implement custom logic to modify the labeling solution for a map render,
|
||||
e.g. by preventing labels being placed which violate custom constraints.
|
||||
|
||||
.. note::
|
||||
|
||||
:py:class:`QgsAbstractLabelingEngineRule` cannot be subclassed in Python. Use one of the existing
|
||||
implementations of this class instead.
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgslabelingenginerule.h"
|
||||
%End
|
||||
%ConvertToSubClassCode
|
||||
if ( sipCpp->id() == "minimumDistanceLabelToFeature" )
|
||||
{
|
||||
sipType = sipType_QgsLabelingEngineRuleMinimumDistanceLabelToFeature;
|
||||
}
|
||||
else if ( sipCpp->id() == "minimumDistanceLabelToLabel" )
|
||||
{
|
||||
sipType = sipType_QgsLabelingEngineRuleMinimumDistanceLabelToLabel;
|
||||
}
|
||||
else if ( sipCpp->id() == "maximumDistanceLabelToFeature" )
|
||||
{
|
||||
sipType = sipType_QgsLabelingEngineRuleMaximumDistanceLabelToFeature;
|
||||
}
|
||||
else if ( sipCpp->id() == "avoidLabelOverlapWithFeature" )
|
||||
{
|
||||
sipType = sipType_QgsLabelingEngineRuleAvoidLabelOverlapWithFeature;
|
||||
}
|
||||
else
|
||||
{
|
||||
sipType = 0;
|
||||
}
|
||||
%End
|
||||
public:
|
||||
|
||||
virtual ~QgsAbstractLabelingEngineRule();
|
||||
|
||||
virtual QgsAbstractLabelingEngineRule *clone() const = 0 /Factory/;
|
||||
%Docstring
|
||||
Creates a clone of this rule.
|
||||
|
||||
The caller takes ownership of the returned object.
|
||||
%End
|
||||
|
||||
virtual QString id() const = 0;
|
||||
%Docstring
|
||||
Returns a string uniquely identifying the rule subclass.
|
||||
%End
|
||||
|
||||
virtual bool prepare( QgsRenderContext &context ) = 0;
|
||||
%Docstring
|
||||
Prepares the rule.
|
||||
|
||||
This must be called on the main render thread, prior to commencing the render operation. Thread sensitive
|
||||
logic (such as creation of feature sources) can be performed in this method.
|
||||
%End
|
||||
|
||||
virtual void writeXml( QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context ) const = 0;
|
||||
%Docstring
|
||||
Writes the rule properties to an XML ``element``.
|
||||
|
||||
.. seealso:: :py:func:`readXml`
|
||||
%End
|
||||
|
||||
virtual void readXml( const QDomElement &element, const QgsReadWriteContext &context ) = 0;
|
||||
%Docstring
|
||||
Reads the rule properties from an XML ``element``.
|
||||
|
||||
.. seealso:: :py:func:`resolveReferences`
|
||||
|
||||
.. seealso:: :py:func:`writeXml`
|
||||
%End
|
||||
|
||||
virtual void resolveReferences( const QgsProject *project );
|
||||
%Docstring
|
||||
Resolves reference to layers from stored layer ID.
|
||||
|
||||
Should be called following a call :py:func:`~QgsAbstractLabelingEngineRule.readXml`.
|
||||
%End
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/labeling/rules/qgslabelingenginerule.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.py again *
|
||||
************************************************************************/
|
@ -0,0 +1,393 @@
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/labeling/rules/qgslabelingenginerule_impl.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.py again *
|
||||
************************************************************************/
|
||||
|
||||
|
||||
|
||||
|
||||
class QgsAbstractLabelingEngineRuleDistanceFromFeature : QgsAbstractLabelingEngineRule
|
||||
{
|
||||
%Docstring(signature="appended")
|
||||
Base class for labeling engine rules which prevents labels being placed too close or to far from features from a different layer.
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgslabelingenginerule_impl.h"
|
||||
%End
|
||||
public:
|
||||
|
||||
QgsAbstractLabelingEngineRuleDistanceFromFeature();
|
||||
~QgsAbstractLabelingEngineRuleDistanceFromFeature();
|
||||
virtual bool prepare( QgsRenderContext &context );
|
||||
|
||||
virtual void writeXml( QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context ) const;
|
||||
|
||||
virtual void readXml( const QDomElement &element, const QgsReadWriteContext &context );
|
||||
|
||||
virtual void resolveReferences( const QgsProject *project );
|
||||
|
||||
|
||||
QgsVectorLayer *labeledLayer();
|
||||
%Docstring
|
||||
Returns the layer providing the labels.
|
||||
|
||||
.. seealso:: :py:func:`setLabeledLayer`
|
||||
%End
|
||||
|
||||
void setLabeledLayer( QgsVectorLayer *layer );
|
||||
%Docstring
|
||||
Sets the ``layer`` providing the labels.
|
||||
|
||||
.. seealso:: :py:func:`labeledLayer`
|
||||
%End
|
||||
|
||||
QgsVectorLayer *targetLayer();
|
||||
%Docstring
|
||||
Returns the layer providing the features which labels must be distant from (or close to).
|
||||
|
||||
.. seealso:: :py:func:`setTargetLayer`
|
||||
%End
|
||||
|
||||
void setTargetLayer( QgsVectorLayer *layer );
|
||||
%Docstring
|
||||
Sets the ``layer`` providing the features which labels must be distant from (or close to).
|
||||
|
||||
.. seealso:: :py:func:`targetLayer`
|
||||
%End
|
||||
|
||||
double distance() const;
|
||||
%Docstring
|
||||
Returns the acceptable distance threshold between labels and the features
|
||||
from the :py:func:`~QgsAbstractLabelingEngineRuleDistanceFromFeature.targetLayer`.
|
||||
|
||||
.. seealso:: :py:func:`setDistance`
|
||||
|
||||
.. seealso:: :py:func:`distanceUnits`
|
||||
%End
|
||||
|
||||
void setDistance( double distance );
|
||||
%Docstring
|
||||
Sets the acceptable ``distance`` threshold between labels and the features
|
||||
from the :py:func:`~QgsAbstractLabelingEngineRuleDistanceFromFeature.targetLayer`.
|
||||
|
||||
.. seealso:: :py:func:`distance`
|
||||
|
||||
.. seealso:: :py:func:`setDistanceUnits`
|
||||
%End
|
||||
|
||||
Qgis::RenderUnit distanceUnit() const;
|
||||
%Docstring
|
||||
Returns the units for the distance between labels and the features
|
||||
from the :py:func:`~QgsAbstractLabelingEngineRuleDistanceFromFeature.targetLayer`.
|
||||
|
||||
.. seealso:: :py:func:`setDistanceUnit`
|
||||
|
||||
.. seealso:: :py:func:`distance`
|
||||
%End
|
||||
|
||||
void setDistanceUnit( Qgis::RenderUnit unit );
|
||||
%Docstring
|
||||
Sets the ``unit`` for the distance between labels and the features
|
||||
from the :py:func:`~QgsAbstractLabelingEngineRuleDistanceFromFeature.targetLayer`.
|
||||
|
||||
.. seealso:: :py:func:`distanceUnit`
|
||||
|
||||
.. seealso:: :py:func:`setDistance`
|
||||
%End
|
||||
|
||||
const QgsMapUnitScale &distanceUnitScale() const;
|
||||
%Docstring
|
||||
Returns the scaling for the distance between labels and the features
|
||||
from the :py:func:`~QgsAbstractLabelingEngineRuleDistanceFromFeature.targetLayer`.
|
||||
|
||||
.. seealso:: :py:func:`setDistanceUnitScale`
|
||||
|
||||
.. seealso:: :py:func:`distance`
|
||||
%End
|
||||
|
||||
void setDistanceUnitScale( const QgsMapUnitScale &scale );
|
||||
%Docstring
|
||||
Sets the ``scale`` for the distance between labels and the features
|
||||
from the :py:func:`~QgsAbstractLabelingEngineRuleDistanceFromFeature.targetLayer`.
|
||||
|
||||
.. seealso:: :py:func:`distanceUnitScale`
|
||||
|
||||
.. seealso:: :py:func:`setDistance`
|
||||
%End
|
||||
|
||||
double cost() const;
|
||||
%Docstring
|
||||
Returns the penalty cost incurred when the rule is violated.
|
||||
|
||||
This is a value between 0 and 10, where 10 indicates that the rule must never be violated,
|
||||
and 1-9 = nice to have if possible, where higher numbers will try harder to avoid violating the rule.
|
||||
|
||||
.. seealso:: :py:func:`setCost`
|
||||
%End
|
||||
|
||||
void setCost( double cost );
|
||||
%Docstring
|
||||
Sets the penalty ``cost`` incurred when the rule is violated.
|
||||
|
||||
This is a value between 0 and 10, where 10 indicates that the rule must never be violated,
|
||||
and 1-9 = nice to have if possible, where higher numbers will try harder to avoid violating the rule.
|
||||
|
||||
.. seealso:: :py:func:`cost`
|
||||
%End
|
||||
|
||||
protected:
|
||||
|
||||
void copyCommonProperties( QgsAbstractLabelingEngineRuleDistanceFromFeature *other ) const;
|
||||
%Docstring
|
||||
Copies common properties from this object to an ``other``.
|
||||
%End
|
||||
|
||||
|
||||
private:
|
||||
QgsAbstractLabelingEngineRuleDistanceFromFeature( const QgsAbstractLabelingEngineRuleDistanceFromFeature &other );
|
||||
};
|
||||
|
||||
|
||||
class QgsLabelingEngineRuleMinimumDistanceLabelToFeature : QgsAbstractLabelingEngineRuleDistanceFromFeature
|
||||
{
|
||||
%Docstring(signature="appended")
|
||||
A labeling engine rule which prevents labels being placed too close to features from a different layer.
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgslabelingenginerule_impl.h"
|
||||
%End
|
||||
public:
|
||||
|
||||
QgsLabelingEngineRuleMinimumDistanceLabelToFeature();
|
||||
~QgsLabelingEngineRuleMinimumDistanceLabelToFeature();
|
||||
virtual QgsLabelingEngineRuleMinimumDistanceLabelToFeature *clone() const /Factory/;
|
||||
|
||||
virtual QString id() const;
|
||||
|
||||
|
||||
private:
|
||||
QgsLabelingEngineRuleMinimumDistanceLabelToFeature( const QgsLabelingEngineRuleMinimumDistanceLabelToFeature & );
|
||||
};
|
||||
|
||||
|
||||
class QgsLabelingEngineRuleMaximumDistanceLabelToFeature : QgsAbstractLabelingEngineRuleDistanceFromFeature
|
||||
{
|
||||
%Docstring(signature="appended")
|
||||
A labeling engine rule which prevents labels being placed too far from features from a different layer.
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgslabelingenginerule_impl.h"
|
||||
%End
|
||||
public:
|
||||
QgsLabelingEngineRuleMaximumDistanceLabelToFeature();
|
||||
~QgsLabelingEngineRuleMaximumDistanceLabelToFeature();
|
||||
virtual QgsLabelingEngineRuleMaximumDistanceLabelToFeature *clone() const /Factory/;
|
||||
|
||||
virtual QString id() const;
|
||||
|
||||
|
||||
private:
|
||||
QgsLabelingEngineRuleMaximumDistanceLabelToFeature( const QgsLabelingEngineRuleMaximumDistanceLabelToFeature & );
|
||||
};
|
||||
|
||||
class QgsLabelingEngineRuleMinimumDistanceLabelToLabel : QgsAbstractLabelingEngineRule
|
||||
{
|
||||
%Docstring(signature="appended")
|
||||
A labeling engine rule which prevents labels being placed too close to labels from a different layer.
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgslabelingenginerule_impl.h"
|
||||
%End
|
||||
public:
|
||||
QgsLabelingEngineRuleMinimumDistanceLabelToLabel();
|
||||
~QgsLabelingEngineRuleMinimumDistanceLabelToLabel();
|
||||
|
||||
virtual QgsLabelingEngineRuleMinimumDistanceLabelToLabel *clone() const /Factory/;
|
||||
|
||||
virtual QString id() const;
|
||||
|
||||
virtual void writeXml( QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context ) const;
|
||||
|
||||
virtual void readXml( const QDomElement &element, const QgsReadWriteContext &context );
|
||||
|
||||
virtual void resolveReferences( const QgsProject *project );
|
||||
|
||||
virtual bool prepare( QgsRenderContext &context );
|
||||
|
||||
|
||||
QgsVectorLayer *labeledLayer();
|
||||
%Docstring
|
||||
Returns the layer providing the labels.
|
||||
|
||||
.. seealso:: :py:func:`setLabeledLayer`
|
||||
%End
|
||||
|
||||
void setLabeledLayer( QgsVectorLayer *layer );
|
||||
%Docstring
|
||||
Sets the ``layer`` providing the labels.
|
||||
|
||||
.. seealso:: :py:func:`labeledLayer`
|
||||
%End
|
||||
|
||||
QgsVectorLayer *targetLayer();
|
||||
%Docstring
|
||||
Returns the layer providing the labels which labels must be distant from.
|
||||
|
||||
.. seealso:: :py:func:`setTargetLayer`
|
||||
%End
|
||||
|
||||
void setTargetLayer( QgsVectorLayer *layer );
|
||||
%Docstring
|
||||
Sets the ``layer`` providing the labels which labels must be distant from.
|
||||
|
||||
.. seealso:: :py:func:`targetLayer`
|
||||
%End
|
||||
|
||||
double distance() const;
|
||||
%Docstring
|
||||
Returns the minimum permitted distance between labels from the :py:func:`~QgsLabelingEngineRuleMinimumDistanceLabelToLabel.labeledLayer` and the labels
|
||||
from the :py:func:`~QgsLabelingEngineRuleMinimumDistanceLabelToLabel.targetLayer`.
|
||||
|
||||
.. seealso:: :py:func:`setDistance`
|
||||
|
||||
.. seealso:: :py:func:`distanceUnits`
|
||||
%End
|
||||
|
||||
void setDistance( double distance );
|
||||
%Docstring
|
||||
Sets the minimum permitted ``distance`` between labels from the :py:func:`~QgsLabelingEngineRuleMinimumDistanceLabelToLabel.labeledLayer` and the labels
|
||||
from the :py:func:`~QgsLabelingEngineRuleMinimumDistanceLabelToLabel.targetLayer`.
|
||||
|
||||
.. seealso:: :py:func:`distance`
|
||||
|
||||
.. seealso:: :py:func:`setDistanceUnits`
|
||||
%End
|
||||
|
||||
Qgis::RenderUnit distanceUnit() const;
|
||||
%Docstring
|
||||
Returns the units for the distance between labels from the :py:func:`~QgsLabelingEngineRuleMinimumDistanceLabelToLabel.labeledLayer` and the labels
|
||||
from the :py:func:`~QgsLabelingEngineRuleMinimumDistanceLabelToLabel.targetLayer`.
|
||||
|
||||
.. seealso:: :py:func:`setDistanceUnit`
|
||||
|
||||
.. seealso:: :py:func:`distance`
|
||||
%End
|
||||
|
||||
void setDistanceUnit( Qgis::RenderUnit unit );
|
||||
%Docstring
|
||||
Sets the ``unit`` for the distance between labels from the :py:func:`~QgsLabelingEngineRuleMinimumDistanceLabelToLabel.labeledLayer` and the labels
|
||||
from the :py:func:`~QgsLabelingEngineRuleMinimumDistanceLabelToLabel.targetLayer`.
|
||||
|
||||
.. seealso:: :py:func:`distanceUnit`
|
||||
|
||||
.. seealso:: :py:func:`setDistance`
|
||||
%End
|
||||
|
||||
const QgsMapUnitScale &distanceUnitScale() const;
|
||||
%Docstring
|
||||
Returns the scaling for the distance between labels from the :py:func:`~QgsLabelingEngineRuleMinimumDistanceLabelToLabel.labeledLayer` and the labels
|
||||
from the :py:func:`~QgsLabelingEngineRuleMinimumDistanceLabelToLabel.targetLayer`.
|
||||
|
||||
.. seealso:: :py:func:`setDistanceUnitScale`
|
||||
|
||||
.. seealso:: :py:func:`distance`
|
||||
%End
|
||||
|
||||
void setDistanceUnitScale( const QgsMapUnitScale &scale );
|
||||
%Docstring
|
||||
Sets the ``scale`` for the distance between labels from the :py:func:`~QgsLabelingEngineRuleMinimumDistanceLabelToLabel.labeledLayer` and the labels
|
||||
from the :py:func:`~QgsLabelingEngineRuleMinimumDistanceLabelToLabel.targetLayer`.
|
||||
|
||||
.. seealso:: :py:func:`distanceUnitScale`
|
||||
|
||||
.. seealso:: :py:func:`setDistance`
|
||||
%End
|
||||
|
||||
private:
|
||||
QgsLabelingEngineRuleMinimumDistanceLabelToLabel( const QgsLabelingEngineRuleMinimumDistanceLabelToLabel & );
|
||||
};
|
||||
|
||||
|
||||
class QgsLabelingEngineRuleAvoidLabelOverlapWithFeature : QgsAbstractLabelingEngineRule
|
||||
{
|
||||
%Docstring(signature="appended")
|
||||
A labeling engine rule which prevents labels being placed overlapping features from a different layer.
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgslabelingenginerule_impl.h"
|
||||
%End
|
||||
public:
|
||||
|
||||
QgsLabelingEngineRuleAvoidLabelOverlapWithFeature();
|
||||
~QgsLabelingEngineRuleAvoidLabelOverlapWithFeature();
|
||||
virtual QgsLabelingEngineRuleAvoidLabelOverlapWithFeature *clone() const /Factory/;
|
||||
|
||||
virtual QString id() const;
|
||||
|
||||
virtual bool prepare( QgsRenderContext &context );
|
||||
|
||||
virtual void writeXml( QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context ) const;
|
||||
|
||||
virtual void readXml( const QDomElement &element, const QgsReadWriteContext &context );
|
||||
|
||||
virtual void resolveReferences( const QgsProject *project );
|
||||
|
||||
|
||||
QgsVectorLayer *labeledLayer();
|
||||
%Docstring
|
||||
Returns the layer providing the labels.
|
||||
|
||||
.. seealso:: :py:func:`setLabeledLayer`
|
||||
%End
|
||||
|
||||
void setLabeledLayer( QgsVectorLayer *layer );
|
||||
%Docstring
|
||||
Sets the ``layer`` providing the labels.
|
||||
|
||||
.. seealso:: :py:func:`labeledLayer`
|
||||
%End
|
||||
|
||||
QgsVectorLayer *targetLayer();
|
||||
%Docstring
|
||||
Returns the layer providing the features which labels must not overlap.
|
||||
|
||||
.. seealso:: :py:func:`setTargetLayer`
|
||||
%End
|
||||
|
||||
void setTargetLayer( QgsVectorLayer *layer );
|
||||
%Docstring
|
||||
Sets the ``layer`` providing the features which labels must not overlap.
|
||||
|
||||
.. seealso:: :py:func:`targetLayer`
|
||||
%End
|
||||
|
||||
private:
|
||||
QgsLabelingEngineRuleAvoidLabelOverlapWithFeature( const QgsLabelingEngineRuleAvoidLabelOverlapWithFeature & );
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/labeling/rules/qgslabelingenginerule_impl.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.py again *
|
||||
************************************************************************/
|
@ -0,0 +1,82 @@
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/labeling/rules/qgslabelingengineruleregistry.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.py again *
|
||||
************************************************************************/
|
||||
|
||||
|
||||
|
||||
class QgsLabelingEngineRuleRegistry
|
||||
{
|
||||
%Docstring(signature="appended")
|
||||
A registry for labeling engine rules.
|
||||
|
||||
Labeling engine rules implement custom logic to modify the labeling solution for a map render,
|
||||
e.g. by preventing labels being placed which violate custom constraints.
|
||||
|
||||
This registry stores available rules and is responsible for creating rules.
|
||||
|
||||
:py:class:`QgsLabelingEngineRuleRegistry` is not usually directly created, but rather accessed through
|
||||
:py:func:`QgsApplication.labelEngineRuleRegistry()`.
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgslabelingengineruleregistry.h"
|
||||
%End
|
||||
public:
|
||||
|
||||
QgsLabelingEngineRuleRegistry();
|
||||
%Docstring
|
||||
Constructor for QgsLabelingEngineRuleRegistry, containing a set of
|
||||
default rules.
|
||||
%End
|
||||
~QgsLabelingEngineRuleRegistry();
|
||||
|
||||
|
||||
QStringList ruleIds() const;
|
||||
%Docstring
|
||||
Returns a list of the rule IDs for rules present in the registry.
|
||||
%End
|
||||
|
||||
QgsAbstractLabelingEngineRule *create( const QString &id ) const /TransferBack/;
|
||||
%Docstring
|
||||
Creates a new rule from the type with matching ``id``.
|
||||
|
||||
Returns ``None`` if no matching rule was found in the registry.
|
||||
|
||||
The caller takes ownership of the returned object.
|
||||
%End
|
||||
|
||||
bool addRule( QgsAbstractLabelingEngineRule *rule /Transfer/ );
|
||||
%Docstring
|
||||
Adds a new ``rule`` type to the registry.
|
||||
|
||||
The registry takes ownership of ``rule``.
|
||||
|
||||
:return: ``True`` if the rule was successfully added.
|
||||
|
||||
.. seealso:: :py:func:`removeRule`
|
||||
%End
|
||||
|
||||
void removeRule( const QString &id );
|
||||
%Docstring
|
||||
Removes the rule with matching ``id`` from the registry.
|
||||
|
||||
.. seealso:: :py:func:`addRule`
|
||||
%End
|
||||
|
||||
private:
|
||||
QgsLabelingEngineRuleRegistry( const QgsLabelingEngineRuleRegistry &other );
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
* This file has been generated automatically from *
|
||||
* *
|
||||
* src/core/labeling/rules/qgslabelingengineruleregistry.h *
|
||||
* *
|
||||
* Do not edit manually ! Edit header and run scripts/sipify.py again *
|
||||
************************************************************************/
|
@ -944,6 +944,13 @@ Returns registry of available 3D symbols.
|
||||
Gets the registry of available scalebar renderers.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
%End
|
||||
|
||||
static QgsLabelingEngineRuleRegistry *labelingEngineRuleRegistry() /KeepReference/;
|
||||
%Docstring
|
||||
Gets the registry of available labeling engine rules.
|
||||
|
||||
.. versionadded:: 3.40
|
||||
%End
|
||||
|
||||
static QgsProjectStorageRegistry *projectStorageRegistry() /KeepReference/;
|
||||
|
@ -391,6 +391,9 @@
|
||||
%Include auto_generated/labeling/qgspallabeling.sip
|
||||
%Include auto_generated/labeling/qgsrulebasedlabeling.sip
|
||||
%Include auto_generated/labeling/qgsvectorlayerlabeling.sip
|
||||
%Include auto_generated/labeling/rules/qgslabelingenginerule.sip
|
||||
%Include auto_generated/labeling/rules/qgslabelingenginerule_impl.sip
|
||||
%Include auto_generated/labeling/rules/qgslabelingengineruleregistry.sip
|
||||
%Include auto_generated/layertree/qgscolorramplegendnode.sip
|
||||
%Include auto_generated/layertree/qgscolorramplegendnodesettings.sip
|
||||
%Include auto_generated/layertree/qgslayertree.sip
|
||||
|
@ -875,6 +875,9 @@ set(QGIS_CORE_SRCS
|
||||
labeling/qgstextlabelfeature.cpp
|
||||
labeling/qgsvectorlayerlabeling.cpp
|
||||
labeling/qgsvectorlayerlabelprovider.cpp
|
||||
labeling/rules/qgslabelingenginerule.cpp
|
||||
labeling/rules/qgslabelingenginerule_impl.cpp
|
||||
labeling/rules/qgslabelingengineruleregistry.cpp
|
||||
|
||||
geometry/qgsabstractgeometry.cpp
|
||||
geometry/qgsbox3d.cpp
|
||||
@ -1538,6 +1541,9 @@ set(QGIS_CORE_HDRS
|
||||
labeling/qgstextlabelfeature.h
|
||||
labeling/qgsvectorlayerlabeling.h
|
||||
labeling/qgsvectorlayerlabelprovider.h
|
||||
labeling/rules/qgslabelingenginerule.h
|
||||
labeling/rules/qgslabelingenginerule_impl.h
|
||||
labeling/rules/qgslabelingengineruleregistry.h
|
||||
|
||||
layertree/qgscolorramplegendnode.h
|
||||
layertree/qgscolorramplegendnodesettings.h
|
||||
@ -2352,6 +2358,7 @@ target_include_directories(qgis_core PUBLIC
|
||||
geocoding
|
||||
gps
|
||||
labeling
|
||||
labeling/rules
|
||||
layertree
|
||||
layout
|
||||
locator
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "qgslabelingresults.h"
|
||||
#include "qgsfillsymbol.h"
|
||||
#include "qgsruntimeprofiler.h"
|
||||
#include "qgslabelingenginerule.h"
|
||||
|
||||
// helper function for checking for job cancellation within PAL
|
||||
static bool _palIsCanceled( void *ctx )
|
||||
@ -100,6 +101,19 @@ void QgsLabelingEngine::setMapSettings( const QgsMapSettings &mapSettings )
|
||||
mResults->setMapSettings( mapSettings );
|
||||
}
|
||||
|
||||
bool QgsLabelingEngine::prepare( QgsRenderContext &context )
|
||||
{
|
||||
const QList<const QgsAbstractLabelingEngineRule *> rules = mMapSettings.labelingEngineSettings().rules();
|
||||
bool res = true;
|
||||
for ( const QgsAbstractLabelingEngineRule *rule : rules )
|
||||
{
|
||||
std::unique_ptr< QgsAbstractLabelingEngineRule > ruleClone( rule->clone() );
|
||||
res = ruleClone->prepare( context ) && res;
|
||||
mEngineRules.emplace_back( std::move( ruleClone ) );
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
QList< QgsMapLayer * > QgsLabelingEngine::participatingLayers() const
|
||||
{
|
||||
QList< QgsMapLayer * > layers;
|
||||
|
@ -356,6 +356,15 @@ class CORE_EXPORT QgsLabelingEngine
|
||||
//! Gets associated labeling engine settings
|
||||
const QgsLabelingEngineSettings &engineSettings() const { return mMapSettings.labelingEngineSettings(); }
|
||||
|
||||
/**
|
||||
* Prepares the engine for rendering in the specified \a context.
|
||||
*
|
||||
* \warning This method must be called in advanced on the main rendering thread, not a background thread.
|
||||
*
|
||||
* \since QGIS 3.40
|
||||
*/
|
||||
bool prepare( QgsRenderContext &context );
|
||||
|
||||
/**
|
||||
* Returns a list of layers with providers in the engine.
|
||||
*/
|
||||
@ -436,6 +445,9 @@ class CORE_EXPORT QgsLabelingEngine
|
||||
QList<QgsAbstractLabelProvider *> mProviders;
|
||||
QList<QgsAbstractLabelProvider *> mSubProviders;
|
||||
|
||||
//!< List of labeling engine rules (owned by the labeling engine)
|
||||
std::vector< std::unique_ptr< QgsAbstractLabelingEngineRule > > mEngineRules;
|
||||
|
||||
//! Resulting labeling layout
|
||||
std::unique_ptr< QgsLabelingResults > mResults;
|
||||
|
||||
|
@ -17,11 +17,50 @@
|
||||
|
||||
#include "qgsproject.h"
|
||||
#include "qgscolorutils.h"
|
||||
#include "qgslabelingenginerule.h"
|
||||
#include "qgsapplication.h"
|
||||
#include "qgslabelingengineruleregistry.h"
|
||||
|
||||
QgsLabelingEngineSettings::QgsLabelingEngineSettings()
|
||||
{
|
||||
}
|
||||
|
||||
QgsLabelingEngineSettings::~QgsLabelingEngineSettings() = default;
|
||||
|
||||
QgsLabelingEngineSettings::QgsLabelingEngineSettings( const QgsLabelingEngineSettings &other )
|
||||
: mFlags( other.mFlags )
|
||||
, mSearchMethod( other.mSearchMethod )
|
||||
, mMaxLineCandidatesPerCm( other.mMaxLineCandidatesPerCm )
|
||||
, mMaxPolygonCandidatesPerCmSquared( other.mMaxPolygonCandidatesPerCmSquared )
|
||||
, mUnplacedLabelColor( other.mUnplacedLabelColor )
|
||||
, mPlacementVersion( other.mPlacementVersion )
|
||||
, mDefaultTextRenderFormat( other.mDefaultTextRenderFormat )
|
||||
{
|
||||
mEngineRules.reserve( other.mEngineRules.size() );
|
||||
for ( const auto &rule : other.mEngineRules )
|
||||
{
|
||||
mEngineRules.emplace_back( rule->clone() );
|
||||
}
|
||||
}
|
||||
|
||||
QgsLabelingEngineSettings &QgsLabelingEngineSettings::operator=( const QgsLabelingEngineSettings &other )
|
||||
{
|
||||
mFlags = other.mFlags;
|
||||
mSearchMethod = other.mSearchMethod;
|
||||
mMaxLineCandidatesPerCm = other.mMaxLineCandidatesPerCm;
|
||||
mMaxPolygonCandidatesPerCmSquared = other.mMaxPolygonCandidatesPerCmSquared;
|
||||
mUnplacedLabelColor = other.mUnplacedLabelColor;
|
||||
mPlacementVersion = other.mPlacementVersion;
|
||||
mDefaultTextRenderFormat = other.mDefaultTextRenderFormat;
|
||||
mEngineRules.clear();
|
||||
mEngineRules.reserve( other.mEngineRules.size() );
|
||||
for ( const auto &rule : other.mEngineRules )
|
||||
{
|
||||
mEngineRules.emplace_back( rule->clone() );
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void QgsLabelingEngineSettings::clear()
|
||||
{
|
||||
*this = QgsLabelingEngineSettings();
|
||||
@ -76,6 +115,50 @@ void QgsLabelingEngineSettings::writeSettingsToProject( QgsProject *project )
|
||||
project->writeEntry( QStringLiteral( "PAL" ), QStringLiteral( "/PlacementEngineVersion" ), static_cast< int >( mPlacementVersion ) );
|
||||
}
|
||||
|
||||
void QgsLabelingEngineSettings::writeXml( QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context ) const
|
||||
{
|
||||
if ( !mEngineRules.empty() )
|
||||
{
|
||||
QDomElement rulesElement = doc.createElement( QStringLiteral( "rules" ) );
|
||||
for ( const auto &rule : mEngineRules )
|
||||
{
|
||||
QDomElement ruleElement = doc.createElement( QStringLiteral( "rule" ) );
|
||||
ruleElement.setAttribute( QStringLiteral( "id" ), rule->id() );
|
||||
rule->writeXml( doc, ruleElement, context );
|
||||
rulesElement.appendChild( ruleElement );
|
||||
}
|
||||
element.appendChild( rulesElement );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsLabelingEngineSettings::readXml( const QDomElement &element, const QgsReadWriteContext &context )
|
||||
{
|
||||
mEngineRules.clear();
|
||||
{
|
||||
const QDomElement rulesElement = element.firstChildElement( QStringLiteral( "rules" ) );
|
||||
const QDomNodeList rules = rulesElement.childNodes();
|
||||
for ( int i = 0; i < rules.length(); i++ )
|
||||
{
|
||||
const QDomElement ruleElement = rules.at( i ).toElement();
|
||||
const QString id = ruleElement.attribute( QStringLiteral( "id" ) );
|
||||
std::unique_ptr< QgsAbstractLabelingEngineRule > rule( QgsApplication::labelingEngineRuleRegistry()->create( id ) );
|
||||
if ( rule )
|
||||
{
|
||||
rule->readXml( ruleElement, context );
|
||||
mEngineRules.emplace_back( std::move( rule ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QgsLabelingEngineSettings::resolveReferences( const QgsProject *project )
|
||||
{
|
||||
for ( const auto &rule : mEngineRules )
|
||||
{
|
||||
rule->resolveReferences( project );
|
||||
}
|
||||
}
|
||||
|
||||
QColor QgsLabelingEngineSettings::unplacedLabelColor() const
|
||||
{
|
||||
return mUnplacedLabelColor;
|
||||
@ -96,4 +179,38 @@ void QgsLabelingEngineSettings::setPlacementVersion( Qgis::LabelPlacementEngineV
|
||||
mPlacementVersion = placementVersion;
|
||||
}
|
||||
|
||||
QList<QgsAbstractLabelingEngineRule *> QgsLabelingEngineSettings::rules()
|
||||
{
|
||||
QList<QgsAbstractLabelingEngineRule *> res;
|
||||
for ( const auto &it : mEngineRules )
|
||||
{
|
||||
res << it.get();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
QList<const QgsAbstractLabelingEngineRule *> QgsLabelingEngineSettings::rules() const
|
||||
{
|
||||
QList<const QgsAbstractLabelingEngineRule *> res;
|
||||
for ( const auto &it : mEngineRules )
|
||||
{
|
||||
res << it.get();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void QgsLabelingEngineSettings::addRule( QgsAbstractLabelingEngineRule *rule )
|
||||
{
|
||||
mEngineRules.emplace_back( rule );
|
||||
}
|
||||
|
||||
void QgsLabelingEngineSettings::setRules( const QList<QgsAbstractLabelingEngineRule *> &rules )
|
||||
{
|
||||
mEngineRules.clear();
|
||||
for ( QgsAbstractLabelingEngineRule *rule : rules )
|
||||
{
|
||||
mEngineRules.emplace_back( rule );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -21,6 +21,10 @@
|
||||
#include <QColor>
|
||||
|
||||
class QgsProject;
|
||||
class QgsAbstractLabelingEngineRule;
|
||||
class QDomDocument;
|
||||
class QDomElement;
|
||||
class QgsReadWriteContext;
|
||||
|
||||
/**
|
||||
* \ingroup core
|
||||
@ -46,6 +50,10 @@ class CORE_EXPORT QgsLabelingEngineSettings
|
||||
};
|
||||
|
||||
QgsLabelingEngineSettings();
|
||||
~QgsLabelingEngineSettings();
|
||||
|
||||
QgsLabelingEngineSettings( const QgsLabelingEngineSettings &other );
|
||||
QgsLabelingEngineSettings &operator=( const QgsLabelingEngineSettings &other );
|
||||
|
||||
//! Returns the configuration to the defaults
|
||||
void clear();
|
||||
@ -125,11 +133,57 @@ class CORE_EXPORT QgsLabelingEngineSettings
|
||||
*/
|
||||
Q_DECL_DEPRECATED Search searchMethod() const SIP_DEPRECATED { return Chain; }
|
||||
|
||||
//! Read configuration of the labeling engine from a project
|
||||
// TODO QGIS 4.0 -- remove these, and just use read/writeXml directly:
|
||||
|
||||
/**
|
||||
* Read configuration of the labeling engine from a project
|
||||
*
|
||||
* \note Both this method and readXml() must be called to completely restore the object's state from a project.
|
||||
*/
|
||||
void readSettingsFromProject( QgsProject *project );
|
||||
//! Write configuration of the labeling engine to a project
|
||||
|
||||
/**
|
||||
* Write configuration of the labeling engine to a project.
|
||||
*
|
||||
* \note Both this method and writeXml() must be called to completely store the object's state in a project.
|
||||
*/
|
||||
void writeSettingsToProject( QgsProject *project );
|
||||
|
||||
/**
|
||||
* Writes the label engine settings to an XML \a element.
|
||||
*
|
||||
* \note Both this method and writeSettingsToProject() must be called to completely store the object's state in a project.
|
||||
*
|
||||
* \see readXml()
|
||||
* \see writeSettingsToProject()
|
||||
*
|
||||
* \since QGIS 3.40
|
||||
*/
|
||||
void writeXml( QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context ) const;
|
||||
|
||||
/**
|
||||
* Reads the label engine settings from an XML \a element.
|
||||
*
|
||||
* \note Both this method and readSettingsFromProject() must be called to completely restore the object's state from a project.
|
||||
*
|
||||
* \note resolveReferences() must be called following this method.
|
||||
*
|
||||
* \see writeXml()
|
||||
* \see readSettingsFromProject()
|
||||
*
|
||||
* \since QGIS 3.40
|
||||
*/
|
||||
void readXml( const QDomElement &element, const QgsReadWriteContext &context );
|
||||
|
||||
/**
|
||||
* Resolves reference to layers from stored layer ID.
|
||||
*
|
||||
* Should be called following a call readXml().
|
||||
*
|
||||
* \since QGIS 3.40
|
||||
*/
|
||||
void resolveReferences( const QgsProject *project );
|
||||
|
||||
// TODO QGIS 4.0: In reality the text render format settings don't only apply to labels, but also
|
||||
// ANY text rendered using QgsTextRenderer (including some non-label text items in layouts).
|
||||
// These methods should possibly be moved out of here and into the general QgsProject settings.
|
||||
@ -188,6 +242,50 @@ class CORE_EXPORT QgsLabelingEngineSettings
|
||||
*/
|
||||
void setPlacementVersion( Qgis::LabelPlacementEngineVersion version );
|
||||
|
||||
/**
|
||||
* Returns a list of labeling engine rules which must be satifisfied
|
||||
* while placing labels.
|
||||
*
|
||||
* \see addRule()
|
||||
* \see setRules()
|
||||
* \since QGIS 3.40
|
||||
*/
|
||||
QList< QgsAbstractLabelingEngineRule * > rules();
|
||||
|
||||
/**
|
||||
* Returns a list of labeling engine rules which must be satifisfied
|
||||
* while placing labels.
|
||||
*
|
||||
* \see addRule()
|
||||
* \see setRules()
|
||||
* \since QGIS 3.40
|
||||
*/
|
||||
QList< const QgsAbstractLabelingEngineRule * > rules() const SIP_SKIP;
|
||||
|
||||
/**
|
||||
* Adds a labeling engine \a rule which must be satifisfied
|
||||
* while placing labels.
|
||||
*
|
||||
* Ownership of the rule is transferred to the settings.
|
||||
*
|
||||
* \see rules()
|
||||
* \see setRules()
|
||||
* \since QGIS 3.40
|
||||
*/
|
||||
void addRule( QgsAbstractLabelingEngineRule *rule SIP_TRANSFER );
|
||||
|
||||
/**
|
||||
* Sets the labeling engine \a rules which must be satifisfied
|
||||
* while placing labels.
|
||||
*
|
||||
* Ownership of the rules are transferred to the settings.
|
||||
*
|
||||
* \see addRule()
|
||||
* \see rules()
|
||||
* \since QGIS 3.40
|
||||
*/
|
||||
void setRules( const QList< QgsAbstractLabelingEngineRule * > &rules SIP_TRANSFER );
|
||||
|
||||
private:
|
||||
//! Flags
|
||||
Qgis::LabelingFlags mFlags = Qgis::LabelingFlag::UsePartialCandidates;
|
||||
@ -204,6 +302,8 @@ class CORE_EXPORT QgsLabelingEngineSettings
|
||||
|
||||
Qgis::TextRenderFormat mDefaultTextRenderFormat = Qgis::TextRenderFormat::AlwaysOutlines;
|
||||
|
||||
std::vector< std::unique_ptr< QgsAbstractLabelingEngineRule > > mEngineRules;
|
||||
|
||||
};
|
||||
|
||||
#endif // QGSLABELINGENGINESETTINGS_H
|
||||
|
78
src/core/labeling/rules/qgslabelingenginerule.cpp
Normal file
78
src/core/labeling/rules/qgslabelingenginerule.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
/***************************************************************************
|
||||
qgslabelingenginerule.cpp
|
||||
---------------------
|
||||
Date : August 2024
|
||||
Copyright : (C) 2024 by Nyall Dawson
|
||||
Email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgslabelingenginerule.h"
|
||||
|
||||
|
||||
//
|
||||
// QgsLabelingEngineContext
|
||||
//
|
||||
|
||||
QgsLabelingEngineContext::QgsLabelingEngineContext( QgsRenderContext &renderContext )
|
||||
: mRenderContext( renderContext )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QgsGeometry QgsLabelingEngineContext::mapBoundaryGeometry() const
|
||||
{
|
||||
return mMapBoundaryGeometry;
|
||||
}
|
||||
|
||||
void QgsLabelingEngineContext::setMapBoundaryGeometry( const QgsGeometry &geometry )
|
||||
{
|
||||
mMapBoundaryGeometry = geometry;
|
||||
}
|
||||
|
||||
QgsRectangle QgsLabelingEngineContext::extent() const
|
||||
{
|
||||
return mExtent;
|
||||
}
|
||||
|
||||
void QgsLabelingEngineContext::setExtent( const QgsRectangle &extent )
|
||||
{
|
||||
mExtent = extent;
|
||||
}
|
||||
|
||||
//
|
||||
// QgsAbstractLabelingEngineRule
|
||||
//
|
||||
|
||||
QgsAbstractLabelingEngineRule::~QgsAbstractLabelingEngineRule() = default;
|
||||
|
||||
void QgsAbstractLabelingEngineRule::resolveReferences( const QgsProject * )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool QgsAbstractLabelingEngineRule::candidatesAreConflicting( const pal::LabelPosition *, const pal::LabelPosition * ) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
QgsRectangle QgsAbstractLabelingEngineRule::modifyCandidateConflictSearchBoundingBox( const QgsRectangle &candidateBounds ) const
|
||||
{
|
||||
return candidateBounds;
|
||||
}
|
||||
|
||||
bool QgsAbstractLabelingEngineRule::candidateIsIllegal( const pal::LabelPosition *, QgsLabelingEngineContext & ) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void QgsAbstractLabelingEngineRule::alterCandidateCost( pal::LabelPosition *, QgsLabelingEngineContext & ) const
|
||||
{
|
||||
|
||||
}
|
238
src/core/labeling/rules/qgslabelingenginerule.h
Normal file
238
src/core/labeling/rules/qgslabelingenginerule.h
Normal file
@ -0,0 +1,238 @@
|
||||
/***************************************************************************
|
||||
qgslabelingenginerule.h
|
||||
---------------------
|
||||
Date : August 2024
|
||||
Copyright : (C) 2024 by Nyall Dawson
|
||||
Email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#ifndef QGSLABELINGENGINERULE_H
|
||||
#define QGSLABELINGENGINERULE_H
|
||||
|
||||
#include "qgis_core.h"
|
||||
#include "qgis_sip.h"
|
||||
#include "qgis.h"
|
||||
#include "qgsgeometry.h"
|
||||
|
||||
class QgsRenderContext;
|
||||
class QDomDocument;
|
||||
class QDomElement;
|
||||
class QgsReadWriteContext;
|
||||
class QgsProject;
|
||||
#ifndef SIP_RUN
|
||||
namespace pal
|
||||
{
|
||||
class LabelPosition;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \ingroup core
|
||||
* \brief Encapsulates the context for a labeling engine run.
|
||||
* \since QGIS 3.40
|
||||
*/
|
||||
class CORE_EXPORT QgsLabelingEngineContext
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for QgsLabelingEngineContext.
|
||||
*/
|
||||
QgsLabelingEngineContext( QgsRenderContext &renderContext );
|
||||
|
||||
#ifndef SIP_RUN
|
||||
QgsLabelingEngineContext( const QgsLabelingEngineContext &other ) = delete;
|
||||
QgsLabelingEngineContext &operator=( const QgsLabelingEngineContext &other ) = delete;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns a reference to the context's render context.
|
||||
*/
|
||||
QgsRenderContext &renderContext() { return mRenderContext; }
|
||||
|
||||
/**
|
||||
* Returns a reference to the context's render context.
|
||||
* \note Not available in Python bindings.
|
||||
*/
|
||||
const QgsRenderContext &renderContext() const { return mRenderContext; } SIP_SKIP
|
||||
|
||||
/**
|
||||
* Returns the map extent defining the limits for labeling.
|
||||
*
|
||||
* \see mapBoundaryGeometry()
|
||||
* \see setExtent()
|
||||
*/
|
||||
QgsRectangle extent() const;
|
||||
|
||||
/**
|
||||
* Sets the map \a extent defining the limits for labeling.
|
||||
*
|
||||
* \see setMapBoundaryGeometry()
|
||||
* \see extent()
|
||||
*/
|
||||
void setExtent( const QgsRectangle &extent );
|
||||
|
||||
/**
|
||||
* Returns the map label boundary geometry, which defines the limits within which labels may be placed
|
||||
* in the map.
|
||||
*
|
||||
* The map boundary geometry specifies the actual geometry of the map
|
||||
* boundary, which will be used to detect whether a label is visible (or partially visible) in
|
||||
* the rendered map. This may differ from extent() in the case of rotated or non-rectangular
|
||||
* maps.
|
||||
*
|
||||
* \see setMapBoundaryGeometry()
|
||||
* \see extent()
|
||||
*/
|
||||
QgsGeometry mapBoundaryGeometry() const;
|
||||
|
||||
/**
|
||||
* Sets the map label boundary \a geometry, which defines the limits within which labels may be placed
|
||||
* in the map.
|
||||
*
|
||||
* The map boundary geometry specifies the actual geometry of the map
|
||||
* boundary, which will be used to detect whether a label is visible (or partially visible) in
|
||||
* the rendered map. This may differ from extent() in the case of rotated or non-rectangular
|
||||
* maps.
|
||||
*
|
||||
* \see setExtent()
|
||||
* \see mapBoundaryGeometry()
|
||||
*/
|
||||
void setMapBoundaryGeometry( const QgsGeometry &geometry );
|
||||
|
||||
private:
|
||||
|
||||
#ifdef SIP_RUN
|
||||
QgsLabelingEngineContext( const QgsLabelingEngineContext &other );
|
||||
#endif
|
||||
|
||||
QgsRenderContext &mRenderContext;
|
||||
QgsRectangle mExtent;
|
||||
QgsGeometry mMapBoundaryGeometry;
|
||||
};
|
||||
|
||||
/**
|
||||
* Abstract base class for labeling engine rules.
|
||||
*
|
||||
* Labeling engine rules implement custom logic to modify the labeling solution for a map render,
|
||||
* e.g. by preventing labels being placed which violate custom constraints.
|
||||
*
|
||||
* \note QgsAbstractLabelingEngineRule cannot be subclassed in Python. Use one of the existing
|
||||
* implementations of this class instead.
|
||||
*
|
||||
* \ingroup core
|
||||
* \since QGIS 3.40
|
||||
*/
|
||||
class CORE_EXPORT QgsAbstractLabelingEngineRule
|
||||
{
|
||||
|
||||
#ifdef SIP_RUN
|
||||
SIP_CONVERT_TO_SUBCLASS_CODE
|
||||
if ( sipCpp->id() == "minimumDistanceLabelToFeature" )
|
||||
{
|
||||
sipType = sipType_QgsLabelingEngineRuleMinimumDistanceLabelToFeature;
|
||||
}
|
||||
else if ( sipCpp->id() == "minimumDistanceLabelToLabel" )
|
||||
{
|
||||
sipType = sipType_QgsLabelingEngineRuleMinimumDistanceLabelToLabel;
|
||||
}
|
||||
else if ( sipCpp->id() == "maximumDistanceLabelToFeature" )
|
||||
{
|
||||
sipType = sipType_QgsLabelingEngineRuleMaximumDistanceLabelToFeature;
|
||||
}
|
||||
else if ( sipCpp->id() == "avoidLabelOverlapWithFeature" )
|
||||
{
|
||||
sipType = sipType_QgsLabelingEngineRuleAvoidLabelOverlapWithFeature;
|
||||
}
|
||||
else
|
||||
{
|
||||
sipType = 0;
|
||||
}
|
||||
SIP_END
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
virtual ~QgsAbstractLabelingEngineRule();
|
||||
|
||||
/**
|
||||
* Creates a clone of this rule.
|
||||
*
|
||||
* The caller takes ownership of the returned object.
|
||||
*/
|
||||
virtual QgsAbstractLabelingEngineRule *clone() const = 0 SIP_FACTORY;
|
||||
|
||||
/**
|
||||
* Returns a string uniquely identifying the rule subclass.
|
||||
*/
|
||||
virtual QString id() const = 0;
|
||||
|
||||
/**
|
||||
* Prepares the rule.
|
||||
*
|
||||
* This must be called on the main render thread, prior to commencing the render operation. Thread sensitive
|
||||
* logic (such as creation of feature sources) can be performed in this method.
|
||||
*/
|
||||
virtual bool prepare( QgsRenderContext &context ) = 0;
|
||||
|
||||
/**
|
||||
* Writes the rule properties to an XML \a element.
|
||||
*
|
||||
* \see readXml()
|
||||
*/
|
||||
virtual void writeXml( QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context ) const = 0;
|
||||
|
||||
/**
|
||||
* Reads the rule properties from an XML \a element.
|
||||
*
|
||||
* \see resolveReferences()
|
||||
* \see writeXml()
|
||||
*/
|
||||
virtual void readXml( const QDomElement &element, const QgsReadWriteContext &context ) = 0;
|
||||
|
||||
/**
|
||||
* Resolves reference to layers from stored layer ID.
|
||||
*
|
||||
* Should be called following a call readXml().
|
||||
*/
|
||||
virtual void resolveReferences( const QgsProject *project );
|
||||
|
||||
/**
|
||||
* Returns TRUE if a labeling candidate \a lp1 conflicts with \a lp2 after applying the rule.
|
||||
*
|
||||
* The default implementation returns FALSE.
|
||||
*/
|
||||
virtual bool candidatesAreConflicting( const pal::LabelPosition *lp1, const pal::LabelPosition *lp2 ) const SIP_SKIP;
|
||||
|
||||
/**
|
||||
* Returns a (possibly expanded) bounding box to use when searching for conflicts for a candidate.
|
||||
*
|
||||
* The return value is permitted to grow the bounding box, but may NOT shrink it.
|
||||
*
|
||||
* The default implementation returns the same bounds.
|
||||
*/
|
||||
virtual QgsRectangle modifyCandidateConflictSearchBoundingBox( const QgsRectangle &candidateBounds ) const SIP_SKIP;
|
||||
|
||||
/**
|
||||
* Returns TRUE if a labeling \a candidate violates the rule and should be eliminated.
|
||||
*
|
||||
* The default implementation returns FALSE.
|
||||
*/
|
||||
virtual bool candidateIsIllegal( const pal::LabelPosition *candidate, QgsLabelingEngineContext &context ) const SIP_SKIP;
|
||||
|
||||
/**
|
||||
* Provides an opportunity for the rule to alter the cost for a \a candidate.
|
||||
*
|
||||
* The default implementation does nothing.
|
||||
*/
|
||||
virtual void alterCandidateCost( pal::LabelPosition *candidate, QgsLabelingEngineContext &context ) const SIP_SKIP;
|
||||
|
||||
};
|
||||
|
||||
#endif // QGSLABELINGENGINESETTINGS_H
|
548
src/core/labeling/rules/qgslabelingenginerule_impl.cpp
Normal file
548
src/core/labeling/rules/qgslabelingenginerule_impl.cpp
Normal file
@ -0,0 +1,548 @@
|
||||
/***************************************************************************
|
||||
qgslabelingenginerule_impl.cpp
|
||||
---------------------
|
||||
Date : August 2024
|
||||
Copyright : (C) 2024 by Nyall Dawson
|
||||
Email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgslabelingenginerule_impl.h"
|
||||
#include "qgsunittypes.h"
|
||||
#include "qgssymbollayerutils.h"
|
||||
#include "labelposition.h"
|
||||
#include "feature.h"
|
||||
#include "qgsvectorlayerfeatureiterator.h"
|
||||
#include "qgsthreadingutils.h"
|
||||
#include "qgsspatialindex.h"
|
||||
#include "qgsgeos.h"
|
||||
|
||||
//
|
||||
// QgsAbstractLabelingEngineRuleDistanceFromFeature
|
||||
//
|
||||
|
||||
QgsAbstractLabelingEngineRuleDistanceFromFeature::QgsAbstractLabelingEngineRuleDistanceFromFeature() = default;
|
||||
QgsAbstractLabelingEngineRuleDistanceFromFeature::~QgsAbstractLabelingEngineRuleDistanceFromFeature() = default;
|
||||
|
||||
bool QgsAbstractLabelingEngineRuleDistanceFromFeature::prepare( QgsRenderContext &context )
|
||||
{
|
||||
if ( !mTargetLayer )
|
||||
return false;
|
||||
|
||||
QGIS_CHECK_OTHER_QOBJECT_THREAD_ACCESS( mTargetLayer );
|
||||
mTargetLayerSource = std::make_unique< QgsVectorLayerFeatureSource >( mTargetLayer.get() );
|
||||
|
||||
mDistanceMapUnits = context.convertToMapUnits( mDistance, mDistanceUnit, mDistanceUnitScale );
|
||||
return true;
|
||||
}
|
||||
|
||||
void QgsAbstractLabelingEngineRuleDistanceFromFeature::writeXml( QDomDocument &, QDomElement &element, const QgsReadWriteContext & ) const
|
||||
{
|
||||
element.setAttribute( QStringLiteral( "distance" ), mDistance );
|
||||
element.setAttribute( QStringLiteral( "distanceUnit" ), QgsUnitTypes::encodeUnit( mDistanceUnit ) );
|
||||
element.setAttribute( QStringLiteral( "distanceUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDistanceUnitScale ) );
|
||||
element.setAttribute( QStringLiteral( "cost" ), mCost );
|
||||
|
||||
if ( mLabeledLayer )
|
||||
{
|
||||
element.setAttribute( QStringLiteral( "labeledLayer" ), mLabeledLayer.layerId );
|
||||
element.setAttribute( QStringLiteral( "labeledLayerName" ), mLabeledLayer.name );
|
||||
element.setAttribute( QStringLiteral( "labeledLayerSource" ), mLabeledLayer.source );
|
||||
element.setAttribute( QStringLiteral( "labeledLayerProvider" ), mLabeledLayer.provider );
|
||||
}
|
||||
if ( mTargetLayer )
|
||||
{
|
||||
element.setAttribute( QStringLiteral( "targetLayer" ), mTargetLayer.layerId );
|
||||
element.setAttribute( QStringLiteral( "targetLayerName" ), mTargetLayer.name );
|
||||
element.setAttribute( QStringLiteral( "targetLayerSource" ), mTargetLayer.source );
|
||||
element.setAttribute( QStringLiteral( "targetLayerProvider" ), mTargetLayer.provider );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsAbstractLabelingEngineRuleDistanceFromFeature::readXml( const QDomElement &element, const QgsReadWriteContext & )
|
||||
{
|
||||
mDistance = element.attribute( QStringLiteral( "distance" ), QStringLiteral( "0" ) ).toDouble();
|
||||
mDistanceUnit = QgsUnitTypes::decodeRenderUnit( element.attribute( QStringLiteral( "distanceUnit" ) ) );
|
||||
mDistanceUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( element.attribute( QStringLiteral( "distanceUnitScale" ) ) );
|
||||
mCost = element.attribute( QStringLiteral( "cost" ), QStringLiteral( "0" ) ).toDouble();
|
||||
|
||||
{
|
||||
const QString layerId = element.attribute( QStringLiteral( "labeledLayer" ) );
|
||||
const QString layerName = element.attribute( QStringLiteral( "labeledLayerName" ) );
|
||||
const QString layerSource = element.attribute( QStringLiteral( "labeledLayerSource" ) );
|
||||
const QString layerProvider = element.attribute( QStringLiteral( "labeledLayerProvider" ) );
|
||||
mLabeledLayer = QgsVectorLayerRef( layerId, layerName, layerSource, layerProvider );
|
||||
}
|
||||
{
|
||||
const QString layerId = element.attribute( QStringLiteral( "targetLayer" ) );
|
||||
const QString layerName = element.attribute( QStringLiteral( "targetLayerName" ) );
|
||||
const QString layerSource = element.attribute( QStringLiteral( "targetLayerSource" ) );
|
||||
const QString layerProvider = element.attribute( QStringLiteral( "targetLayerProvider" ) );
|
||||
mTargetLayer = QgsVectorLayerRef( layerId, layerName, layerSource, layerProvider );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsAbstractLabelingEngineRuleDistanceFromFeature::resolveReferences( const QgsProject *project )
|
||||
{
|
||||
mLabeledLayer.resolve( project );
|
||||
mTargetLayer.resolve( project );
|
||||
}
|
||||
|
||||
bool QgsAbstractLabelingEngineRuleDistanceFromFeature::candidateIsIllegal( const pal::LabelPosition *candidate, QgsLabelingEngineContext &context ) const
|
||||
{
|
||||
// hard blocks on candidates only apply when cost == 10
|
||||
if ( mCost < 10 )
|
||||
return false;
|
||||
|
||||
if ( candidate->getFeaturePart()->feature()->provider()->layerId() != mLabeledLayer.layerId )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !mTargetLayerSource )
|
||||
return false;
|
||||
|
||||
return candidateExceedsTolerance( candidate, context );
|
||||
}
|
||||
|
||||
void QgsAbstractLabelingEngineRuleDistanceFromFeature::alterCandidateCost( pal::LabelPosition *candidate, QgsLabelingEngineContext &context ) const
|
||||
{
|
||||
// cost of 10 = hard block, handled in candidateIsIllegal
|
||||
if ( mCost >= 10 )
|
||||
return;
|
||||
|
||||
if ( candidate->getFeaturePart()->feature()->provider()->layerId() != mLabeledLayer.layerId )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !mTargetLayerSource )
|
||||
return;
|
||||
|
||||
if ( candidateExceedsTolerance( candidate, context ) )
|
||||
{
|
||||
// magic number alert! / 1000 here is completely arbitrary, an attempt to balance against the cost scaling of other factors
|
||||
// assigned by the inscrutible logic of the pal engine internals
|
||||
candidate->setCost( candidate->cost() + mCost / 1000 );
|
||||
}
|
||||
}
|
||||
|
||||
QgsVectorLayer *QgsAbstractLabelingEngineRuleDistanceFromFeature::labeledLayer()
|
||||
{
|
||||
return mLabeledLayer.get();
|
||||
}
|
||||
|
||||
void QgsAbstractLabelingEngineRuleDistanceFromFeature::setLabeledLayer( QgsVectorLayer *layer )
|
||||
{
|
||||
mLabeledLayer = layer;
|
||||
}
|
||||
|
||||
QgsVectorLayer *QgsAbstractLabelingEngineRuleDistanceFromFeature::targetLayer()
|
||||
{
|
||||
return mTargetLayer.get();
|
||||
}
|
||||
|
||||
void QgsAbstractLabelingEngineRuleDistanceFromFeature::setTargetLayer( QgsVectorLayer *layer )
|
||||
{
|
||||
mTargetLayer = layer;
|
||||
}
|
||||
|
||||
void QgsAbstractLabelingEngineRuleDistanceFromFeature::copyCommonProperties( QgsAbstractLabelingEngineRuleDistanceFromFeature *other ) const
|
||||
{
|
||||
other->mLabeledLayer = mLabeledLayer;
|
||||
other->mTargetLayer = mTargetLayer;
|
||||
other->mDistance = mDistance;
|
||||
other->mDistanceUnit = mDistanceUnit;
|
||||
other->mDistanceUnitScale = mDistanceUnitScale;
|
||||
other->mCost = mCost;
|
||||
}
|
||||
|
||||
void QgsAbstractLabelingEngineRuleDistanceFromFeature::initialize( QgsLabelingEngineContext &context )
|
||||
{
|
||||
QgsFeatureRequest req;
|
||||
req.setDestinationCrs( context.renderContext().coordinateTransform().destinationCrs(), context.renderContext().transformContext() );
|
||||
req.setFilterRect( context.extent() );
|
||||
req.setNoAttributes();
|
||||
|
||||
QgsFeatureIterator it = mTargetLayerSource->getFeatures( req );
|
||||
|
||||
mIndex = std::make_unique< QgsSpatialIndex >( it, context.renderContext().feedback(), QgsSpatialIndex::Flag::FlagStoreFeatureGeometries );
|
||||
|
||||
mInitialized = true;
|
||||
}
|
||||
|
||||
bool QgsAbstractLabelingEngineRuleDistanceFromFeature::candidateExceedsTolerance( const pal::LabelPosition *candidate, QgsLabelingEngineContext &context ) const
|
||||
{
|
||||
if ( !mInitialized )
|
||||
const_cast< QgsAbstractLabelingEngineRuleDistanceFromFeature * >( this )->initialize( context );
|
||||
|
||||
const QgsRectangle candidateBounds = candidate->boundingBox();
|
||||
const QgsRectangle expandedBounds = candidateBounds.buffered( mDistanceMapUnits );
|
||||
|
||||
const QList<QgsFeatureId> overlapCandidates = mIndex->intersects( expandedBounds );
|
||||
if ( overlapCandidates.empty() )
|
||||
return !mMustBeDistant;
|
||||
|
||||
GEOSContextHandle_t geosctxt = QgsGeosContext::get();
|
||||
|
||||
const GEOSPreparedGeometry *candidateGeos = candidate->preparedMultiPartGeom();
|
||||
for ( const QgsFeatureId overlapCandidateId : overlapCandidates )
|
||||
{
|
||||
if ( context.renderContext().feedback() && context.renderContext().feedback()->isCanceled() )
|
||||
break;
|
||||
|
||||
try
|
||||
{
|
||||
geos::unique_ptr featureCandidate = QgsGeos::asGeos( mIndex->geometry( overlapCandidateId ).constGet() );
|
||||
#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
|
||||
if ( GEOSPreparedDistanceWithin_r( geosctxt, candidateGeos, featureCandidate.get(), mDistanceMapUnits ) )
|
||||
{
|
||||
return mMustBeDistant;
|
||||
}
|
||||
#else
|
||||
QgsDebugError( QStringLiteral( "This rule requires GEOS 3.10+" ) );
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
catch ( GEOSException &e )
|
||||
{
|
||||
QgsDebugError( QStringLiteral( "GEOS exception: %1" ).arg( e.what() ) );
|
||||
}
|
||||
}
|
||||
|
||||
return !mMustBeDistant;
|
||||
}
|
||||
|
||||
//
|
||||
// QgsLabelingEngineRuleMinimumDistanceLabelToFeature
|
||||
//
|
||||
|
||||
QgsLabelingEngineRuleMinimumDistanceLabelToFeature::QgsLabelingEngineRuleMinimumDistanceLabelToFeature() = default;
|
||||
QgsLabelingEngineRuleMinimumDistanceLabelToFeature::~QgsLabelingEngineRuleMinimumDistanceLabelToFeature() = default;
|
||||
|
||||
QgsLabelingEngineRuleMinimumDistanceLabelToFeature *QgsLabelingEngineRuleMinimumDistanceLabelToFeature::clone() const
|
||||
{
|
||||
std::unique_ptr< QgsLabelingEngineRuleMinimumDistanceLabelToFeature> res = std::make_unique< QgsLabelingEngineRuleMinimumDistanceLabelToFeature >();
|
||||
copyCommonProperties( res.get() );
|
||||
return res.release();
|
||||
}
|
||||
|
||||
QString QgsLabelingEngineRuleMinimumDistanceLabelToFeature::id() const
|
||||
{
|
||||
return QStringLiteral( "minimumDistanceLabelToFeature" );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// QgsLabelingEngineRuleMaximumDistanceLabelToFeature
|
||||
//
|
||||
|
||||
QgsLabelingEngineRuleMaximumDistanceLabelToFeature::QgsLabelingEngineRuleMaximumDistanceLabelToFeature()
|
||||
{
|
||||
mMustBeDistant = false;
|
||||
}
|
||||
|
||||
QgsLabelingEngineRuleMaximumDistanceLabelToFeature::~QgsLabelingEngineRuleMaximumDistanceLabelToFeature() = default;
|
||||
|
||||
QgsLabelingEngineRuleMaximumDistanceLabelToFeature *QgsLabelingEngineRuleMaximumDistanceLabelToFeature::clone() const
|
||||
{
|
||||
std::unique_ptr< QgsLabelingEngineRuleMaximumDistanceLabelToFeature > res = std::make_unique< QgsLabelingEngineRuleMaximumDistanceLabelToFeature >();
|
||||
copyCommonProperties( res.get() );
|
||||
return res.release();
|
||||
}
|
||||
|
||||
QString QgsLabelingEngineRuleMaximumDistanceLabelToFeature::id() const
|
||||
{
|
||||
return QStringLiteral( "maximumDistanceLabelToFeature" );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// QgsLabelingEngineRuleMinimumDistanceLabelToLabel
|
||||
//
|
||||
|
||||
QgsLabelingEngineRuleMinimumDistanceLabelToLabel::QgsLabelingEngineRuleMinimumDistanceLabelToLabel() = default;
|
||||
QgsLabelingEngineRuleMinimumDistanceLabelToLabel::~QgsLabelingEngineRuleMinimumDistanceLabelToLabel() = default;
|
||||
|
||||
QgsLabelingEngineRuleMinimumDistanceLabelToLabel *QgsLabelingEngineRuleMinimumDistanceLabelToLabel::clone() const
|
||||
{
|
||||
std::unique_ptr< QgsLabelingEngineRuleMinimumDistanceLabelToLabel> res = std::make_unique< QgsLabelingEngineRuleMinimumDistanceLabelToLabel >();
|
||||
res->mLabeledLayer = mLabeledLayer;
|
||||
res->mTargetLayer = mTargetLayer;
|
||||
res->mDistance = mDistance;
|
||||
res->mDistanceUnit = mDistanceUnit;
|
||||
res->mDistanceUnitScale = mDistanceUnitScale;
|
||||
return res.release();
|
||||
}
|
||||
|
||||
QString QgsLabelingEngineRuleMinimumDistanceLabelToLabel::id() const
|
||||
{
|
||||
return QStringLiteral( "minimumDistanceLabelToLabel" );
|
||||
}
|
||||
|
||||
void QgsLabelingEngineRuleMinimumDistanceLabelToLabel::writeXml( QDomDocument &, QDomElement &element, const QgsReadWriteContext & ) const
|
||||
{
|
||||
element.setAttribute( QStringLiteral( "distance" ), mDistance );
|
||||
element.setAttribute( QStringLiteral( "distanceUnit" ), QgsUnitTypes::encodeUnit( mDistanceUnit ) );
|
||||
element.setAttribute( QStringLiteral( "distanceUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mDistanceUnitScale ) );
|
||||
|
||||
if ( mLabeledLayer )
|
||||
{
|
||||
element.setAttribute( QStringLiteral( "labeledLayer" ), mLabeledLayer.layerId );
|
||||
element.setAttribute( QStringLiteral( "labeledLayerName" ), mLabeledLayer.name );
|
||||
element.setAttribute( QStringLiteral( "labeledLayerSource" ), mLabeledLayer.source );
|
||||
element.setAttribute( QStringLiteral( "labeledLayerProvider" ), mLabeledLayer.provider );
|
||||
}
|
||||
if ( mTargetLayer )
|
||||
{
|
||||
element.setAttribute( QStringLiteral( "targetLayer" ), mTargetLayer.layerId );
|
||||
element.setAttribute( QStringLiteral( "targetLayerName" ), mTargetLayer.name );
|
||||
element.setAttribute( QStringLiteral( "targetLayerSource" ), mTargetLayer.source );
|
||||
element.setAttribute( QStringLiteral( "targetLayerProvider" ), mTargetLayer.provider );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsLabelingEngineRuleMinimumDistanceLabelToLabel::readXml( const QDomElement &element, const QgsReadWriteContext & )
|
||||
{
|
||||
mDistance = element.attribute( QStringLiteral( "distance" ), QStringLiteral( "0" ) ).toDouble();
|
||||
mDistanceUnit = QgsUnitTypes::decodeRenderUnit( element.attribute( QStringLiteral( "distanceUnit" ) ) );
|
||||
mDistanceUnitScale = QgsSymbolLayerUtils::decodeMapUnitScale( element.attribute( QStringLiteral( "distanceUnitScale" ) ) );
|
||||
|
||||
{
|
||||
const QString layerId = element.attribute( QStringLiteral( "labeledLayer" ) );
|
||||
const QString layerName = element.attribute( QStringLiteral( "labeledLayerName" ) );
|
||||
const QString layerSource = element.attribute( QStringLiteral( "labeledLayerSource" ) );
|
||||
const QString layerProvider = element.attribute( QStringLiteral( "labeledLayerProvider" ) );
|
||||
mLabeledLayer = QgsVectorLayerRef( layerId, layerName, layerSource, layerProvider );
|
||||
}
|
||||
{
|
||||
const QString layerId = element.attribute( QStringLiteral( "targetLayer" ) );
|
||||
const QString layerName = element.attribute( QStringLiteral( "targetLayerName" ) );
|
||||
const QString layerSource = element.attribute( QStringLiteral( "targetLayerSource" ) );
|
||||
const QString layerProvider = element.attribute( QStringLiteral( "targetLayerProvider" ) );
|
||||
mTargetLayer = QgsVectorLayerRef( layerId, layerName, layerSource, layerProvider );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsLabelingEngineRuleMinimumDistanceLabelToLabel::resolveReferences( const QgsProject *project )
|
||||
{
|
||||
mLabeledLayer.resolve( project );
|
||||
mTargetLayer.resolve( project );
|
||||
}
|
||||
|
||||
bool QgsLabelingEngineRuleMinimumDistanceLabelToLabel::prepare( QgsRenderContext &context )
|
||||
{
|
||||
mDistanceMapUnits = context.convertToMapUnits( mDistance, mDistanceUnit, mDistanceUnitScale );
|
||||
return true;
|
||||
}
|
||||
|
||||
QgsRectangle QgsLabelingEngineRuleMinimumDistanceLabelToLabel::modifyCandidateConflictSearchBoundingBox( const QgsRectangle &candidateBounds ) const
|
||||
{
|
||||
return candidateBounds.buffered( mDistanceMapUnits );
|
||||
}
|
||||
|
||||
bool QgsLabelingEngineRuleMinimumDistanceLabelToLabel::candidatesAreConflicting( const pal::LabelPosition *lp1, const pal::LabelPosition *lp2 ) const
|
||||
{
|
||||
// conflicts are commutative -- we need to check both layers
|
||||
if (
|
||||
( lp1->getFeaturePart()->feature()->provider()->layerId() == mLabeledLayer.layerId
|
||||
&& lp2->getFeaturePart()->feature()->provider()->layerId() == mTargetLayer.layerId )
|
||||
||
|
||||
( lp2->getFeaturePart()->feature()->provider()->layerId() == mLabeledLayer.layerId
|
||||
&& lp1->getFeaturePart()->feature()->provider()->layerId() == mTargetLayer.layerId )
|
||||
)
|
||||
{
|
||||
GEOSContextHandle_t geosctxt = QgsGeosContext::get();
|
||||
try
|
||||
{
|
||||
#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
|
||||
if ( GEOSPreparedDistanceWithin_r( geosctxt, lp1->preparedMultiPartGeom(), lp2->multiPartGeom(), mDistanceMapUnits ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
QgsDebugError( QStringLiteral( "This rule requires GEOS 3.10+" ) );
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
catch ( GEOSException &e )
|
||||
{
|
||||
QgsDebugError( QStringLiteral( "GEOS exception: %1" ).arg( e.what() ) );
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QgsVectorLayer *QgsLabelingEngineRuleMinimumDistanceLabelToLabel::labeledLayer()
|
||||
{
|
||||
return mLabeledLayer.get();
|
||||
}
|
||||
|
||||
void QgsLabelingEngineRuleMinimumDistanceLabelToLabel::setLabeledLayer( QgsVectorLayer *layer )
|
||||
{
|
||||
mLabeledLayer = layer;
|
||||
}
|
||||
|
||||
QgsVectorLayer *QgsLabelingEngineRuleMinimumDistanceLabelToLabel::targetLayer()
|
||||
{
|
||||
return mTargetLayer.get();
|
||||
}
|
||||
|
||||
void QgsLabelingEngineRuleMinimumDistanceLabelToLabel::setTargetLayer( QgsVectorLayer *layer )
|
||||
{
|
||||
mTargetLayer = layer;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// QgsLabelingEngineRuleAvoidLabelOverlapWithFeature
|
||||
//
|
||||
|
||||
QgsLabelingEngineRuleAvoidLabelOverlapWithFeature::QgsLabelingEngineRuleAvoidLabelOverlapWithFeature() = default;
|
||||
QgsLabelingEngineRuleAvoidLabelOverlapWithFeature::~QgsLabelingEngineRuleAvoidLabelOverlapWithFeature() = default;
|
||||
|
||||
QgsLabelingEngineRuleAvoidLabelOverlapWithFeature *QgsLabelingEngineRuleAvoidLabelOverlapWithFeature::clone() const
|
||||
{
|
||||
std::unique_ptr< QgsLabelingEngineRuleAvoidLabelOverlapWithFeature> res = std::make_unique< QgsLabelingEngineRuleAvoidLabelOverlapWithFeature >();
|
||||
res->mLabeledLayer = mLabeledLayer;
|
||||
res->mTargetLayer = mTargetLayer;
|
||||
return res.release();
|
||||
}
|
||||
|
||||
QString QgsLabelingEngineRuleAvoidLabelOverlapWithFeature::id() const
|
||||
{
|
||||
return QStringLiteral( "avoidLabelOverlapWithFeature" );
|
||||
}
|
||||
|
||||
bool QgsLabelingEngineRuleAvoidLabelOverlapWithFeature::prepare( QgsRenderContext & )
|
||||
{
|
||||
if ( !mTargetLayer )
|
||||
return false;
|
||||
|
||||
QGIS_CHECK_OTHER_QOBJECT_THREAD_ACCESS( mTargetLayer );
|
||||
mTargetLayerSource = std::make_unique< QgsVectorLayerFeatureSource >( mTargetLayer.get() );
|
||||
return true;
|
||||
}
|
||||
|
||||
void QgsLabelingEngineRuleAvoidLabelOverlapWithFeature::writeXml( QDomDocument &, QDomElement &element, const QgsReadWriteContext & ) const
|
||||
{
|
||||
if ( mLabeledLayer )
|
||||
{
|
||||
element.setAttribute( QStringLiteral( "labeledLayer" ), mLabeledLayer.layerId );
|
||||
element.setAttribute( QStringLiteral( "labeledLayerName" ), mLabeledLayer.name );
|
||||
element.setAttribute( QStringLiteral( "labeledLayerSource" ), mLabeledLayer.source );
|
||||
element.setAttribute( QStringLiteral( "labeledLayerProvider" ), mLabeledLayer.provider );
|
||||
}
|
||||
if ( mTargetLayer )
|
||||
{
|
||||
element.setAttribute( QStringLiteral( "targetLayer" ), mTargetLayer.layerId );
|
||||
element.setAttribute( QStringLiteral( "targetLayerName" ), mTargetLayer.name );
|
||||
element.setAttribute( QStringLiteral( "targetLayerSource" ), mTargetLayer.source );
|
||||
element.setAttribute( QStringLiteral( "targetLayerProvider" ), mTargetLayer.provider );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsLabelingEngineRuleAvoidLabelOverlapWithFeature::readXml( const QDomElement &element, const QgsReadWriteContext & )
|
||||
{
|
||||
{
|
||||
const QString layerId = element.attribute( QStringLiteral( "labeledLayer" ) );
|
||||
const QString layerName = element.attribute( QStringLiteral( "labeledLayerName" ) );
|
||||
const QString layerSource = element.attribute( QStringLiteral( "labeledLayerSource" ) );
|
||||
const QString layerProvider = element.attribute( QStringLiteral( "labeledLayerProvider" ) );
|
||||
mLabeledLayer = QgsVectorLayerRef( layerId, layerName, layerSource, layerProvider );
|
||||
}
|
||||
{
|
||||
const QString layerId = element.attribute( QStringLiteral( "targetLayer" ) );
|
||||
const QString layerName = element.attribute( QStringLiteral( "targetLayerName" ) );
|
||||
const QString layerSource = element.attribute( QStringLiteral( "targetLayerSource" ) );
|
||||
const QString layerProvider = element.attribute( QStringLiteral( "targetLayerProvider" ) );
|
||||
mTargetLayer = QgsVectorLayerRef( layerId, layerName, layerSource, layerProvider );
|
||||
}
|
||||
}
|
||||
|
||||
void QgsLabelingEngineRuleAvoidLabelOverlapWithFeature::resolveReferences( const QgsProject *project )
|
||||
{
|
||||
mLabeledLayer.resolve( project );
|
||||
mTargetLayer.resolve( project );
|
||||
}
|
||||
|
||||
bool QgsLabelingEngineRuleAvoidLabelOverlapWithFeature::candidateIsIllegal( const pal::LabelPosition *candidate, QgsLabelingEngineContext &context ) const
|
||||
{
|
||||
if ( candidate->getFeaturePart()->feature()->provider()->layerId() != mLabeledLayer.layerId )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !mTargetLayerSource )
|
||||
return false;
|
||||
|
||||
if ( !mInitialized )
|
||||
const_cast< QgsLabelingEngineRuleAvoidLabelOverlapWithFeature * >( this )->initialize( context );
|
||||
|
||||
const QList<QgsFeatureId> overlapCandidates = mIndex->intersects( candidate->boundingBox() );
|
||||
if ( overlapCandidates.empty() )
|
||||
return false;
|
||||
|
||||
GEOSContextHandle_t geosctxt = QgsGeosContext::get();
|
||||
|
||||
const GEOSPreparedGeometry *candidateGeos = candidate->preparedMultiPartGeom();
|
||||
for ( const QgsFeatureId overlapCandidateId : overlapCandidates )
|
||||
{
|
||||
if ( context.renderContext().feedback() && context.renderContext().feedback()->isCanceled() )
|
||||
break;
|
||||
|
||||
try
|
||||
{
|
||||
geos::unique_ptr featureCandidate = QgsGeos::asGeos( mIndex->geometry( overlapCandidateId ).constGet() );
|
||||
if ( GEOSPreparedIntersects_r( geosctxt, candidateGeos, featureCandidate.get() ) == 1 )
|
||||
return true;
|
||||
}
|
||||
catch ( GEOSException &e )
|
||||
{
|
||||
QgsDebugError( QStringLiteral( "GEOS exception: %1" ).arg( e.what() ) );
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QgsVectorLayer *QgsLabelingEngineRuleAvoidLabelOverlapWithFeature::labeledLayer()
|
||||
{
|
||||
return mLabeledLayer.get();
|
||||
}
|
||||
|
||||
void QgsLabelingEngineRuleAvoidLabelOverlapWithFeature::setLabeledLayer( QgsVectorLayer *layer )
|
||||
{
|
||||
mLabeledLayer = layer;
|
||||
}
|
||||
|
||||
QgsVectorLayer *QgsLabelingEngineRuleAvoidLabelOverlapWithFeature::targetLayer()
|
||||
{
|
||||
return mTargetLayer.get();
|
||||
}
|
||||
|
||||
void QgsLabelingEngineRuleAvoidLabelOverlapWithFeature::setTargetLayer( QgsVectorLayer *layer )
|
||||
{
|
||||
mTargetLayer = layer;
|
||||
}
|
||||
|
||||
void QgsLabelingEngineRuleAvoidLabelOverlapWithFeature::initialize( QgsLabelingEngineContext &context )
|
||||
{
|
||||
QgsFeatureRequest req;
|
||||
req.setDestinationCrs( context.renderContext().coordinateTransform().destinationCrs(), context.renderContext().transformContext() );
|
||||
req.setFilterRect( context.extent() );
|
||||
req.setNoAttributes();
|
||||
|
||||
QgsFeatureIterator it = mTargetLayerSource->getFeatures( req );
|
||||
|
||||
mIndex = std::make_unique< QgsSpatialIndex >( it, context.renderContext().feedback(), QgsSpatialIndex::Flag::FlagStoreFeatureGeometries );
|
||||
|
||||
mInitialized = true;
|
||||
}
|
416
src/core/labeling/rules/qgslabelingenginerule_impl.h
Normal file
416
src/core/labeling/rules/qgslabelingenginerule_impl.h
Normal file
@ -0,0 +1,416 @@
|
||||
/***************************************************************************
|
||||
qgslabelingenginerule_impl.h
|
||||
---------------------
|
||||
Date : August 2024
|
||||
Copyright : (C) 2024 by Nyall Dawson
|
||||
Email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#ifndef QGSLABELINGENGINERULEIMPL_H
|
||||
#define QGSLABELINGENGINERULEIMPL_H
|
||||
|
||||
#include "qgis_core.h"
|
||||
#include "qgis_sip.h"
|
||||
#include "qgis.h"
|
||||
#include "qgslabelingenginerule.h"
|
||||
#include "qgsvectorlayerref.h"
|
||||
#include "qgsmapunitscale.h"
|
||||
|
||||
class QgsSpatialIndex;
|
||||
|
||||
|
||||
/**
|
||||
* Base class for labeling engine rules which prevents labels being placed too close or to far from features from a different layer.
|
||||
*
|
||||
* \ingroup core
|
||||
* \since QGIS 3.40
|
||||
*/
|
||||
class CORE_EXPORT QgsAbstractLabelingEngineRuleDistanceFromFeature : public QgsAbstractLabelingEngineRule
|
||||
{
|
||||
public:
|
||||
|
||||
QgsAbstractLabelingEngineRuleDistanceFromFeature();
|
||||
~QgsAbstractLabelingEngineRuleDistanceFromFeature() override;
|
||||
bool prepare( QgsRenderContext &context ) override;
|
||||
void writeXml( QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context ) const override;
|
||||
void readXml( const QDomElement &element, const QgsReadWriteContext &context ) override;
|
||||
void resolveReferences( const QgsProject *project ) override;
|
||||
bool candidateIsIllegal( const pal::LabelPosition *candidate, QgsLabelingEngineContext &context ) const override SIP_SKIP;
|
||||
void alterCandidateCost( pal::LabelPosition *candidate, QgsLabelingEngineContext &context ) const override SIP_SKIP;
|
||||
|
||||
/**
|
||||
* Returns the layer providing the labels.
|
||||
*
|
||||
* \see setLabeledLayer()
|
||||
*/
|
||||
QgsVectorLayer *labeledLayer();
|
||||
|
||||
/**
|
||||
* Sets the \a layer providing the labels.
|
||||
*
|
||||
* \see labeledLayer()
|
||||
*/
|
||||
void setLabeledLayer( QgsVectorLayer *layer );
|
||||
|
||||
/**
|
||||
* Returns the layer providing the features which labels must be distant from (or close to).
|
||||
*
|
||||
* \see setTargetLayer()
|
||||
*/
|
||||
QgsVectorLayer *targetLayer();
|
||||
|
||||
/**
|
||||
* Sets the \a layer providing the features which labels must be distant from (or close to).
|
||||
*
|
||||
* \see targetLayer()
|
||||
*/
|
||||
void setTargetLayer( QgsVectorLayer *layer );
|
||||
|
||||
/**
|
||||
* Returns the acceptable distance threshold between labels and the features
|
||||
* from the targetLayer().
|
||||
*
|
||||
* \see setDistance()
|
||||
* \see distanceUnits()
|
||||
*/
|
||||
double distance() const { return mDistance; }
|
||||
|
||||
/**
|
||||
* Sets the acceptable \a distance threshold between labels and the features
|
||||
* from the targetLayer().
|
||||
*
|
||||
* \see distance()
|
||||
* \see setDistanceUnits()
|
||||
*/
|
||||
void setDistance( double distance ) { mDistance = distance; }
|
||||
|
||||
/**
|
||||
* Returns the units for the distance between labels and the features
|
||||
* from the targetLayer().
|
||||
*
|
||||
* \see setDistanceUnit()
|
||||
* \see distance()
|
||||
*/
|
||||
Qgis::RenderUnit distanceUnit() const { return mDistanceUnit; }
|
||||
|
||||
/**
|
||||
* Sets the \a unit for the distance between labels and the features
|
||||
* from the targetLayer().
|
||||
*
|
||||
* \see distanceUnit()
|
||||
* \see setDistance()
|
||||
*/
|
||||
void setDistanceUnit( Qgis::RenderUnit unit ) { mDistanceUnit = unit; }
|
||||
|
||||
/**
|
||||
* Returns the scaling for the distance between labels and the features
|
||||
* from the targetLayer().
|
||||
*
|
||||
* \see setDistanceUnitScale()
|
||||
* \see distance()
|
||||
*/
|
||||
const QgsMapUnitScale &distanceUnitScale() const { return mDistanceUnitScale; }
|
||||
|
||||
/**
|
||||
* Sets the \a scale for the distance between labels and the features
|
||||
* from the targetLayer().
|
||||
*
|
||||
* \see distanceUnitScale()
|
||||
* \see setDistance()
|
||||
*/
|
||||
void setDistanceUnitScale( const QgsMapUnitScale &scale ) { mDistanceUnitScale = scale; }
|
||||
|
||||
/**
|
||||
* Returns the penalty cost incurred when the rule is violated.
|
||||
*
|
||||
* This is a value between 0 and 10, where 10 indicates that the rule must never be violated,
|
||||
* and 1-9 = nice to have if possible, where higher numbers will try harder to avoid violating the rule.
|
||||
*
|
||||
* \see setCost()
|
||||
*/
|
||||
double cost() const { return mCost; }
|
||||
|
||||
/**
|
||||
* Sets the penalty \a cost incurred when the rule is violated.
|
||||
*
|
||||
* This is a value between 0 and 10, where 10 indicates that the rule must never be violated,
|
||||
* and 1-9 = nice to have if possible, where higher numbers will try harder to avoid violating the rule.
|
||||
*
|
||||
* \see cost()
|
||||
*/
|
||||
void setCost( double cost ) { mCost = cost; }
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Copies common properties from this object to an \a other.
|
||||
*/
|
||||
void copyCommonProperties( QgsAbstractLabelingEngineRuleDistanceFromFeature *other ) const;
|
||||
|
||||
//! TRUE if labels must be distant from features, FALSE if they must be close
|
||||
bool mMustBeDistant = true;
|
||||
|
||||
private:
|
||||
#ifdef SIP_RUN
|
||||
QgsAbstractLabelingEngineRuleDistanceFromFeature( const QgsAbstractLabelingEngineRuleDistanceFromFeature &other );
|
||||
#endif
|
||||
|
||||
void initialize( QgsLabelingEngineContext &context );
|
||||
|
||||
//! Returns TRUE if \a candidate is too close / too far from features from target layer
|
||||
bool candidateExceedsTolerance( const pal::LabelPosition *candidate, QgsLabelingEngineContext &context ) const;
|
||||
|
||||
//! Labeled layer
|
||||
QgsVectorLayerRef mLabeledLayer;
|
||||
//! Target layer
|
||||
QgsVectorLayerRef mTargetLayer;
|
||||
//! Distance threshold
|
||||
double mDistance = 0;
|
||||
//! Distance threshold unit
|
||||
Qgis::RenderUnit mDistanceUnit = Qgis::RenderUnit::Millimeters;
|
||||
//! Distance threshold map unit scale
|
||||
QgsMapUnitScale mDistanceUnitScale;
|
||||
//! Associated cost
|
||||
double mCost = 0;
|
||||
|
||||
// cached variables
|
||||
double mDistanceMapUnits = 0;
|
||||
std::unique_ptr< QgsAbstractFeatureSource > mTargetLayerSource;
|
||||
std::unique_ptr< QgsSpatialIndex > mIndex;
|
||||
bool mInitialized = false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A labeling engine rule which prevents labels being placed too close to features from a different layer.
|
||||
*
|
||||
* \ingroup core
|
||||
* \since QGIS 3.40
|
||||
*/
|
||||
class CORE_EXPORT QgsLabelingEngineRuleMinimumDistanceLabelToFeature : public QgsAbstractLabelingEngineRuleDistanceFromFeature
|
||||
{
|
||||
public:
|
||||
|
||||
QgsLabelingEngineRuleMinimumDistanceLabelToFeature();
|
||||
~QgsLabelingEngineRuleMinimumDistanceLabelToFeature() override;
|
||||
QgsLabelingEngineRuleMinimumDistanceLabelToFeature *clone() const override SIP_FACTORY;
|
||||
QString id() const override;
|
||||
|
||||
private:
|
||||
#ifdef SIP_RUN
|
||||
QgsLabelingEngineRuleMinimumDistanceLabelToFeature( const QgsLabelingEngineRuleMinimumDistanceLabelToFeature & );
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A labeling engine rule which prevents labels being placed too far from features from a different layer.
|
||||
*
|
||||
* \ingroup core
|
||||
* \since QGIS 3.40
|
||||
*/
|
||||
class CORE_EXPORT QgsLabelingEngineRuleMaximumDistanceLabelToFeature : public QgsAbstractLabelingEngineRuleDistanceFromFeature
|
||||
{
|
||||
public:
|
||||
QgsLabelingEngineRuleMaximumDistanceLabelToFeature();
|
||||
~QgsLabelingEngineRuleMaximumDistanceLabelToFeature() override;
|
||||
QgsLabelingEngineRuleMaximumDistanceLabelToFeature *clone() const override SIP_FACTORY;
|
||||
QString id() const override;
|
||||
|
||||
private:
|
||||
#ifdef SIP_RUN
|
||||
QgsLabelingEngineRuleMaximumDistanceLabelToFeature( const QgsLabelingEngineRuleMaximumDistanceLabelToFeature & );
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* A labeling engine rule which prevents labels being placed too close to labels from a different layer.
|
||||
*
|
||||
* \ingroup core
|
||||
* \since QGIS 3.40
|
||||
*/
|
||||
class CORE_EXPORT QgsLabelingEngineRuleMinimumDistanceLabelToLabel : public QgsAbstractLabelingEngineRule
|
||||
{
|
||||
public:
|
||||
QgsLabelingEngineRuleMinimumDistanceLabelToLabel();
|
||||
~QgsLabelingEngineRuleMinimumDistanceLabelToLabel() override;
|
||||
|
||||
QgsLabelingEngineRuleMinimumDistanceLabelToLabel *clone() const override SIP_FACTORY;
|
||||
QString id() const override;
|
||||
void writeXml( QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context ) const override;
|
||||
void readXml( const QDomElement &element, const QgsReadWriteContext &context ) override;
|
||||
void resolveReferences( const QgsProject *project ) override;
|
||||
bool prepare( QgsRenderContext &context ) override;
|
||||
QgsRectangle modifyCandidateConflictSearchBoundingBox( const QgsRectangle &candidateBounds ) const override SIP_SKIP;
|
||||
bool candidatesAreConflicting( const pal::LabelPosition *lp1, const pal::LabelPosition *lp2 ) const override SIP_SKIP;
|
||||
|
||||
/**
|
||||
* Returns the layer providing the labels.
|
||||
*
|
||||
* \see setLabeledLayer()
|
||||
*/
|
||||
QgsVectorLayer *labeledLayer();
|
||||
|
||||
/**
|
||||
* Sets the \a layer providing the labels.
|
||||
*
|
||||
* \see labeledLayer()
|
||||
*/
|
||||
void setLabeledLayer( QgsVectorLayer *layer );
|
||||
|
||||
/**
|
||||
* Returns the layer providing the labels which labels must be distant from.
|
||||
*
|
||||
* \see setTargetLayer()
|
||||
*/
|
||||
QgsVectorLayer *targetLayer();
|
||||
|
||||
/**
|
||||
* Sets the \a layer providing the labels which labels must be distant from.
|
||||
*
|
||||
* \see targetLayer()
|
||||
*/
|
||||
void setTargetLayer( QgsVectorLayer *layer );
|
||||
|
||||
/**
|
||||
* Returns the minimum permitted distance between labels from the labeledLayer() and the labels
|
||||
* from the targetLayer().
|
||||
*
|
||||
* \see setDistance()
|
||||
* \see distanceUnits()
|
||||
*/
|
||||
double distance() const { return mDistance; }
|
||||
|
||||
/**
|
||||
* Sets the minimum permitted \a distance between labels from the labeledLayer() and the labels
|
||||
* from the targetLayer().
|
||||
*
|
||||
* \see distance()
|
||||
* \see setDistanceUnits()
|
||||
*/
|
||||
void setDistance( double distance ) { mDistance = distance; }
|
||||
|
||||
/**
|
||||
* Returns the units for the distance between labels from the labeledLayer() and the labels
|
||||
* from the targetLayer().
|
||||
*
|
||||
* \see setDistanceUnit()
|
||||
* \see distance()
|
||||
*/
|
||||
Qgis::RenderUnit distanceUnit() const { return mDistanceUnit; }
|
||||
|
||||
/**
|
||||
* Sets the \a unit for the distance between labels from the labeledLayer() and the labels
|
||||
* from the targetLayer().
|
||||
*
|
||||
* \see distanceUnit()
|
||||
* \see setDistance()
|
||||
*/
|
||||
void setDistanceUnit( Qgis::RenderUnit unit ) { mDistanceUnit = unit; }
|
||||
|
||||
/**
|
||||
* Returns the scaling for the distance between labels from the labeledLayer() and the labels
|
||||
* from the targetLayer().
|
||||
*
|
||||
* \see setDistanceUnitScale()
|
||||
* \see distance()
|
||||
*/
|
||||
const QgsMapUnitScale &distanceUnitScale() const { return mDistanceUnitScale; }
|
||||
|
||||
/**
|
||||
* Sets the \a scale for the distance between labels from the labeledLayer() and the labels
|
||||
* from the targetLayer().
|
||||
*
|
||||
* \see distanceUnitScale()
|
||||
* \see setDistance()
|
||||
*/
|
||||
void setDistanceUnitScale( const QgsMapUnitScale &scale ) { mDistanceUnitScale = scale; }
|
||||
|
||||
private:
|
||||
#ifdef SIP_RUN
|
||||
QgsLabelingEngineRuleMinimumDistanceLabelToLabel( const QgsLabelingEngineRuleMinimumDistanceLabelToLabel & );
|
||||
#endif
|
||||
|
||||
QgsVectorLayerRef mLabeledLayer;
|
||||
QgsVectorLayerRef mTargetLayer;
|
||||
double mDistance = 0;
|
||||
Qgis::RenderUnit mDistanceUnit = Qgis::RenderUnit::Millimeters;
|
||||
QgsMapUnitScale mDistanceUnitScale;
|
||||
|
||||
// cached variables
|
||||
double mDistanceMapUnits = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A labeling engine rule which prevents labels being placed overlapping features from a different layer.
|
||||
*
|
||||
* \ingroup core
|
||||
* \since QGIS 3.40
|
||||
*/
|
||||
class CORE_EXPORT QgsLabelingEngineRuleAvoidLabelOverlapWithFeature : public QgsAbstractLabelingEngineRule
|
||||
{
|
||||
public:
|
||||
|
||||
QgsLabelingEngineRuleAvoidLabelOverlapWithFeature();
|
||||
~QgsLabelingEngineRuleAvoidLabelOverlapWithFeature() override;
|
||||
QgsLabelingEngineRuleAvoidLabelOverlapWithFeature *clone() const override SIP_FACTORY;
|
||||
QString id() const override;
|
||||
bool prepare( QgsRenderContext &context ) override;
|
||||
void writeXml( QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context ) const override;
|
||||
void readXml( const QDomElement &element, const QgsReadWriteContext &context ) override;
|
||||
void resolveReferences( const QgsProject *project ) override;
|
||||
bool candidateIsIllegal( const pal::LabelPosition *candidate, QgsLabelingEngineContext &context ) const override SIP_SKIP;
|
||||
|
||||
/**
|
||||
* Returns the layer providing the labels.
|
||||
*
|
||||
* \see setLabeledLayer()
|
||||
*/
|
||||
QgsVectorLayer *labeledLayer();
|
||||
|
||||
/**
|
||||
* Sets the \a layer providing the labels.
|
||||
*
|
||||
* \see labeledLayer()
|
||||
*/
|
||||
void setLabeledLayer( QgsVectorLayer *layer );
|
||||
|
||||
/**
|
||||
* Returns the layer providing the features which labels must not overlap.
|
||||
*
|
||||
* \see setTargetLayer()
|
||||
*/
|
||||
QgsVectorLayer *targetLayer();
|
||||
|
||||
/**
|
||||
* Sets the \a layer providing the features which labels must not overlap.
|
||||
*
|
||||
* \see targetLayer()
|
||||
*/
|
||||
void setTargetLayer( QgsVectorLayer *layer );
|
||||
|
||||
private:
|
||||
#ifdef SIP_RUN
|
||||
QgsLabelingEngineRuleAvoidLabelOverlapWithFeature( const QgsLabelingEngineRuleAvoidLabelOverlapWithFeature & );
|
||||
#endif
|
||||
void initialize( QgsLabelingEngineContext &context );
|
||||
|
||||
QgsVectorLayerRef mLabeledLayer;
|
||||
QgsVectorLayerRef mTargetLayer;
|
||||
|
||||
// cached variables
|
||||
std::unique_ptr< QgsAbstractFeatureSource > mTargetLayerSource;
|
||||
std::unique_ptr< QgsSpatialIndex > mIndex;
|
||||
bool mInitialized = false;
|
||||
};
|
||||
|
||||
|
||||
#endif // QGSLABELINGENGINERULEIMPL_H
|
72
src/core/labeling/rules/qgslabelingengineruleregistry.cpp
Normal file
72
src/core/labeling/rules/qgslabelingengineruleregistry.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
/***************************************************************************
|
||||
qgslabelingengineruleregistry.cpp
|
||||
---------------------
|
||||
Date : August 2024
|
||||
Copyright : (C) 2024 by Nyall Dawson
|
||||
Email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#include "qgslabelingengineruleregistry.h"
|
||||
#include "qgslabelingenginerule.h"
|
||||
#include "qgslabelingenginerule_impl.h"
|
||||
#include <geos_c.h>
|
||||
|
||||
QgsLabelingEngineRuleRegistry::QgsLabelingEngineRuleRegistry()
|
||||
{
|
||||
#if GEOS_VERSION_MAJOR>3 || ( GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR>=10 )
|
||||
addRule( new QgsLabelingEngineRuleMinimumDistanceLabelToFeature() );
|
||||
addRule( new QgsLabelingEngineRuleMaximumDistanceLabelToFeature() );
|
||||
addRule( new QgsLabelingEngineRuleMinimumDistanceLabelToLabel() );
|
||||
#endif
|
||||
|
||||
addRule( new QgsLabelingEngineRuleAvoidLabelOverlapWithFeature() );
|
||||
}
|
||||
|
||||
QgsLabelingEngineRuleRegistry::~QgsLabelingEngineRuleRegistry() = default;
|
||||
|
||||
QStringList QgsLabelingEngineRuleRegistry::ruleIds() const
|
||||
{
|
||||
QStringList res;
|
||||
res.reserve( static_cast< int >( mRules.size() ) );
|
||||
for ( auto &it : mRules )
|
||||
{
|
||||
res.append( it.first );
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
QgsAbstractLabelingEngineRule *QgsLabelingEngineRuleRegistry::create( const QString &id ) const
|
||||
{
|
||||
auto it = mRules.find( id );
|
||||
if ( it == mRules.end() )
|
||||
return nullptr;
|
||||
|
||||
return it->second->clone();
|
||||
}
|
||||
|
||||
bool QgsLabelingEngineRuleRegistry::addRule( QgsAbstractLabelingEngineRule *rule )
|
||||
{
|
||||
if ( !rule )
|
||||
return false;
|
||||
|
||||
if ( mRules.find( rule->id() ) != mRules.end() )
|
||||
{
|
||||
delete rule;
|
||||
return false;
|
||||
}
|
||||
|
||||
mRules[ rule->id() ] = std::unique_ptr< QgsAbstractLabelingEngineRule >( rule );
|
||||
return true;
|
||||
}
|
||||
|
||||
void QgsLabelingEngineRuleRegistry::removeRule( const QString &id )
|
||||
{
|
||||
mRules.erase( id );
|
||||
}
|
||||
|
96
src/core/labeling/rules/qgslabelingengineruleregistry.h
Normal file
96
src/core/labeling/rules/qgslabelingengineruleregistry.h
Normal file
@ -0,0 +1,96 @@
|
||||
/***************************************************************************
|
||||
qgslabelingengineruleregistry.h
|
||||
---------------------
|
||||
Date : August 2024
|
||||
Copyright : (C) 2024 by Nyall Dawson
|
||||
Email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
#ifndef QGSLABELINGENGINERULEREGISTRY_H
|
||||
#define QGSLABELINGENGINERULEREGISTRY_H
|
||||
|
||||
#include "qgis_core.h"
|
||||
#include "qgis_sip.h"
|
||||
#include "qgis.h"
|
||||
|
||||
class QgsAbstractLabelingEngineRule;
|
||||
|
||||
/**
|
||||
* A registry for labeling engine rules.
|
||||
*
|
||||
* Labeling engine rules implement custom logic to modify the labeling solution for a map render,
|
||||
* e.g. by preventing labels being placed which violate custom constraints.
|
||||
*
|
||||
* This registry stores available rules and is responsible for creating rules.
|
||||
*
|
||||
* QgsLabelingEngineRuleRegistry is not usually directly created, but rather accessed through
|
||||
* QgsApplication::labelEngineRuleRegistry().
|
||||
*
|
||||
* \ingroup core
|
||||
* \since QGIS 3.40
|
||||
*/
|
||||
class CORE_EXPORT QgsLabelingEngineRuleRegistry
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for QgsLabelingEngineRuleRegistry, containing a set of
|
||||
* default rules.
|
||||
*/
|
||||
QgsLabelingEngineRuleRegistry();
|
||||
~QgsLabelingEngineRuleRegistry();
|
||||
|
||||
//! QgsLabelingEngineRuleRegistry cannot be copied
|
||||
QgsLabelingEngineRuleRegistry( const QgsLabelingEngineRuleRegistry &other ) = delete;
|
||||
//! QgsLabelingEngineRuleRegistry cannot be copied
|
||||
QgsLabelingEngineRuleRegistry &operator=( const QgsLabelingEngineRuleRegistry &other ) = delete;
|
||||
|
||||
/**
|
||||
* Returns a list of the rule IDs for rules present in the registry.
|
||||
*/
|
||||
QStringList ruleIds() const;
|
||||
|
||||
/**
|
||||
* Creates a new rule from the type with matching \a id.
|
||||
*
|
||||
* Returns NULLPTR if no matching rule was found in the registry.
|
||||
*
|
||||
* The caller takes ownership of the returned object.
|
||||
*/
|
||||
QgsAbstractLabelingEngineRule *create( const QString &id ) const SIP_TRANSFERBACK;
|
||||
|
||||
/**
|
||||
* Adds a new \a rule type to the registry.
|
||||
*
|
||||
* The registry takes ownership of \a rule.
|
||||
*
|
||||
* \returns TRUE if the rule was successfully added.
|
||||
*
|
||||
* \see removeRule()
|
||||
*/
|
||||
bool addRule( QgsAbstractLabelingEngineRule *rule SIP_TRANSFER );
|
||||
|
||||
/**
|
||||
* Removes the rule with matching \a id from the registry.
|
||||
*
|
||||
* \see addRule()
|
||||
*/
|
||||
void removeRule( const QString &id );
|
||||
|
||||
private:
|
||||
|
||||
#ifdef SIP_RUN
|
||||
QgsLabelingEngineRuleRegistry( const QgsLabelingEngineRuleRegistry &other );
|
||||
#endif
|
||||
|
||||
std::map< QString, std::unique_ptr< QgsAbstractLabelingEngineRule > > mRules;
|
||||
|
||||
};
|
||||
|
||||
#endif // QGSLABELINGENGINERULEREGISTRY_H
|
@ -1041,6 +1041,8 @@ LabelRenderJob QgsMapRendererJob::prepareLabelingJob( QPainter *painter, QgsLabe
|
||||
job.context.setPainter( painter );
|
||||
job.context.setLabelingEngine( labelingEngine2 );
|
||||
job.context.setFeedback( mLabelingEngineFeedback );
|
||||
if ( labelingEngine2 )
|
||||
job.context.labelingEngine()->prepare( job.context );
|
||||
|
||||
QgsRectangle r1 = mSettings.visibleExtent();
|
||||
r1.grow( mSettings.extentBuffer() );
|
||||
|
@ -2496,6 +2496,12 @@ bool QgsProject::readProjectFile( const QString &filename, Qgis::ProjectReadFlag
|
||||
|
||||
profile.switchTask( tr( "Loading label settings" ) );
|
||||
mLabelingEngineSettings->readSettingsFromProject( this );
|
||||
{
|
||||
const QDomElement labelEngineSettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "labelEngineSettings" ) );
|
||||
mLabelingEngineSettings->readXml( labelEngineSettingsElement, context );
|
||||
}
|
||||
mLabelingEngineSettings->resolveReferences( this );
|
||||
|
||||
emit labelingEngineSettingsChanged();
|
||||
|
||||
profile.switchTask( tr( "Loading annotations" ) );
|
||||
@ -3365,6 +3371,11 @@ bool QgsProject::writeProjectFile( const QString &filename )
|
||||
qgisNode.appendChild( layerOrderNode );
|
||||
|
||||
mLabelingEngineSettings->writeSettingsToProject( this );
|
||||
{
|
||||
QDomElement labelEngineSettingsElement = doc->createElement( QStringLiteral( "labelEngineSettings" ) );
|
||||
mLabelingEngineSettings->writeXml( *doc, labelEngineSettingsElement, context );
|
||||
qgisNode.appendChild( labelEngineSettingsElement );
|
||||
}
|
||||
|
||||
writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorRedPart" ), mBackgroundColor.red() );
|
||||
writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorGreenPart" ), mBackgroundColor.green() );
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "qgsnumericformatregistry.h"
|
||||
#include "qgsfieldformatterregistry.h"
|
||||
#include "qgsscalebarrendererregistry.h"
|
||||
#include "qgslabelingengineruleregistry.h"
|
||||
#include "qgssvgcache.h"
|
||||
#include "qgsimagecache.h"
|
||||
#include "qgssourcecache.h"
|
||||
@ -2631,6 +2632,11 @@ QgsScaleBarRendererRegistry *QgsApplication::scaleBarRendererRegistry()
|
||||
return members()->mScaleBarRendererRegistry;
|
||||
}
|
||||
|
||||
QgsLabelingEngineRuleRegistry *QgsApplication::labelingEngineRuleRegistry()
|
||||
{
|
||||
return members()->mLabelingEngineRuleRegistry;
|
||||
}
|
||||
|
||||
QgsProjectStorageRegistry *QgsApplication::projectStorageRegistry()
|
||||
{
|
||||
return members()->mProjectStorageRegistry;
|
||||
@ -2808,6 +2814,11 @@ QgsApplication::ApplicationMembers::ApplicationMembers()
|
||||
mAnnotationItemRegistry->populate();
|
||||
profiler->end();
|
||||
}
|
||||
{
|
||||
profiler->start( tr( "Setup labeling engine rule registry" ) );
|
||||
mLabelingEngineRuleRegistry = new QgsLabelingEngineRuleRegistry();
|
||||
profiler->end();
|
||||
}
|
||||
{
|
||||
profiler->start( tr( "Setup sensor registry" ) );
|
||||
mSensorRegistry = new QgsSensorRegistry();
|
||||
@ -2897,6 +2908,7 @@ QgsApplication::ApplicationMembers::~ApplicationMembers()
|
||||
delete mSourceCache;
|
||||
delete mCalloutRegistry;
|
||||
delete mRecentStyleHandler;
|
||||
delete mLabelingEngineRuleRegistry;
|
||||
delete mSymbolLayerRegistry;
|
||||
delete mExternalStorageRegistry;
|
||||
delete mProfileSourceRegistry;
|
||||
|
@ -78,6 +78,7 @@ class QgsDatabaseQueryLog;
|
||||
class QgsFontManager;
|
||||
class QgsSensorRegistry;
|
||||
class QgsProfileSourceRegistry;
|
||||
class QgsLabelingEngineRuleRegistry;
|
||||
|
||||
/**
|
||||
* \ingroup core
|
||||
@ -930,6 +931,13 @@ class CORE_EXPORT QgsApplication : public QApplication
|
||||
*/
|
||||
static QgsScaleBarRendererRegistry *scaleBarRendererRegistry() SIP_KEEPREFERENCE;
|
||||
|
||||
/**
|
||||
* Gets the registry of available labeling engine rules.
|
||||
*
|
||||
* \since QGIS 3.40
|
||||
*/
|
||||
static QgsLabelingEngineRuleRegistry *labelingEngineRuleRegistry() SIP_KEEPREFERENCE;
|
||||
|
||||
/**
|
||||
* Returns registry of available project storage implementations.
|
||||
* \since QGIS 3.2
|
||||
@ -1146,6 +1154,7 @@ class CORE_EXPORT QgsApplication : public QApplication
|
||||
QgsBabelFormatRegistry *mGpsBabelFormatRegistry = nullptr;
|
||||
QgsNetworkContentFetcherRegistry *mNetworkContentFetcherRegistry = nullptr;
|
||||
QgsScaleBarRendererRegistry *mScaleBarRendererRegistry = nullptr;
|
||||
QgsLabelingEngineRuleRegistry *mLabelingEngineRuleRegistry = nullptr;
|
||||
QgsValidityCheckRegistry *mValidityCheckRegistry = nullptr;
|
||||
QgsMessageLog *mMessageLog = nullptr;
|
||||
QgsPaintEffectRegistry *mPaintEffectRegistry = nullptr;
|
||||
|
@ -117,6 +117,7 @@ ADD_PYTHON_TEST(PyQgsImageCache test_qgsimagecache.py)
|
||||
ADD_PYTHON_TEST(PyQgsInterpolatedLineSymbolLayer test_qgsinterpolatedlinesymbollayers.py)
|
||||
ADD_PYTHON_TEST(PyQgsInterval test_qgsinterval.py)
|
||||
ADD_PYTHON_TEST(PyQgsJsonUtils test_qgsjsonutils.py)
|
||||
ADD_PYTHON_TEST(PyQgsLabelingEngineRule test_qgslabelingenginerule.py)
|
||||
ADD_PYTHON_TEST(PyQgsLabelLineSettings test_qgslabellinesettings.py)
|
||||
ADD_PYTHON_TEST(PyQgsLabelObstacleSettings test_qgslabelobstaclesettings.py)
|
||||
ADD_PYTHON_TEST(PyQgsLabelPlacementSettings test_qgslabelplacementsettings.py)
|
||||
|
313
tests/src/python/test_qgslabelingenginerule.py
Normal file
313
tests/src/python/test_qgslabelingenginerule.py
Normal file
@ -0,0 +1,313 @@
|
||||
"""QGIS Unit tests for labeling engine rules
|
||||
|
||||
.. note:: This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
"""
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
from qgis.PyQt.QtXml import QDomDocument
|
||||
|
||||
from qgis.core import (
|
||||
Qgis,
|
||||
QgsLabelingEngineRuleRegistry,
|
||||
QgsAbstractLabelingEngineRule,
|
||||
QgsLabelingEngineRuleMinimumDistanceLabelToFeature,
|
||||
QgsLabelingEngineRuleMinimumDistanceLabelToLabel,
|
||||
QgsLabelingEngineRuleMaximumDistanceLabelToFeature,
|
||||
QgsLabelingEngineRuleAvoidLabelOverlapWithFeature,
|
||||
QgsProject,
|
||||
QgsVectorLayer,
|
||||
QgsMapUnitScale,
|
||||
QgsReadWriteContext,
|
||||
QgsLabelingEngineSettings
|
||||
)
|
||||
import unittest
|
||||
from qgis.testing import start_app, QgisTestCase
|
||||
|
||||
start_app()
|
||||
|
||||
|
||||
class TestRule(QgsAbstractLabelingEngineRule):
|
||||
|
||||
def id(self):
|
||||
return 'test'
|
||||
|
||||
def prepare(self, context):
|
||||
pass
|
||||
|
||||
def writeXml(self, doc, element, context):
|
||||
pass
|
||||
|
||||
def modifyProblem(self):
|
||||
pass
|
||||
|
||||
def readXml(self, element, context):
|
||||
pass
|
||||
|
||||
def clone(self):
|
||||
return TestRule()
|
||||
|
||||
|
||||
class TestQgsLabelingEngineRule(QgisTestCase):
|
||||
|
||||
def testRegistry(self):
|
||||
registry = QgsLabelingEngineRuleRegistry()
|
||||
self.assertTrue(registry.ruleIds())
|
||||
for rule_id in registry.ruleIds():
|
||||
self.assertEqual(registry.create(rule_id).id(), rule_id)
|
||||
|
||||
self.assertIsNone(registry.create('bad'))
|
||||
|
||||
self.assertIn('minimumDistanceLabelToFeature', registry.ruleIds())
|
||||
|
||||
self.assertFalse(registry.addRule(None))
|
||||
|
||||
self.assertTrue(registry.addRule(TestRule()))
|
||||
|
||||
self.assertIn('test', registry.ruleIds())
|
||||
self.assertIsInstance(registry.create('test'), TestRule)
|
||||
|
||||
# no duplicates
|
||||
self.assertFalse(registry.addRule(TestRule()))
|
||||
|
||||
registry.removeRule('test')
|
||||
|
||||
self.assertNotIn('test', registry.ruleIds())
|
||||
self.assertIsNone(registry.create('test'))
|
||||
|
||||
registry.removeRule('test')
|
||||
|
||||
def testMinimumDistanceLabelToFeature(self):
|
||||
p = QgsProject()
|
||||
vl = QgsVectorLayer('Point', 'layer 1', 'memory')
|
||||
vl2 = QgsVectorLayer('Point', 'layer 2', 'memory')
|
||||
p.addMapLayers([vl, vl2])
|
||||
|
||||
rule = QgsLabelingEngineRuleMinimumDistanceLabelToFeature()
|
||||
rule.setLabeledLayer(vl)
|
||||
rule.setTargetLayer(vl2)
|
||||
rule.setDistance(14)
|
||||
rule.setDistanceUnit(Qgis.RenderUnit.Inches)
|
||||
rule.setDistanceUnitScale(QgsMapUnitScale(15, 25))
|
||||
rule.setCost(6.6)
|
||||
|
||||
self.assertEqual(rule.labeledLayer(), vl)
|
||||
self.assertEqual(rule.targetLayer(), vl2)
|
||||
self.assertEqual(rule.distance(), 14)
|
||||
self.assertEqual(rule.distanceUnit(), Qgis.RenderUnit.Inches)
|
||||
self.assertEqual(rule.distanceUnitScale().minScale, 15)
|
||||
self.assertEqual(rule.distanceUnitScale().maxScale, 25)
|
||||
self.assertEqual(rule.cost(), 6.6)
|
||||
|
||||
rule2 = rule.clone()
|
||||
self.assertEqual(rule2.labeledLayer(), vl)
|
||||
self.assertEqual(rule2.targetLayer(), vl2)
|
||||
self.assertEqual(rule2.distance(), 14)
|
||||
self.assertEqual(rule2.distanceUnit(), Qgis.RenderUnit.Inches)
|
||||
self.assertEqual(rule2.distanceUnitScale().minScale, 15)
|
||||
self.assertEqual(rule2.distanceUnitScale().maxScale, 25)
|
||||
self.assertEqual(rule2.cost(), 6.6)
|
||||
|
||||
doc = QDomDocument("testdoc")
|
||||
elem = doc.createElement("test")
|
||||
rule.writeXml(doc, elem, QgsReadWriteContext())
|
||||
|
||||
rule3 = QgsLabelingEngineRuleMinimumDistanceLabelToFeature()
|
||||
rule3.readXml(elem, QgsReadWriteContext())
|
||||
rule3.resolveReferences(p)
|
||||
self.assertEqual(rule3.labeledLayer(), vl)
|
||||
self.assertEqual(rule3.targetLayer(), vl2)
|
||||
self.assertEqual(rule3.distance(), 14)
|
||||
self.assertEqual(rule3.distanceUnit(), Qgis.RenderUnit.Inches)
|
||||
self.assertEqual(rule3.distanceUnitScale().minScale, 15)
|
||||
self.assertEqual(rule3.distanceUnitScale().maxScale, 25)
|
||||
self.assertEqual(rule3.cost(), 6.6)
|
||||
|
||||
def testMinimumDistanceLabelToLabel(self):
|
||||
p = QgsProject()
|
||||
vl = QgsVectorLayer('Point', 'layer 1', 'memory')
|
||||
vl2 = QgsVectorLayer('Point', 'layer 2', 'memory')
|
||||
p.addMapLayers([vl, vl2])
|
||||
|
||||
rule = QgsLabelingEngineRuleMinimumDistanceLabelToLabel()
|
||||
rule.setLabeledLayer(vl)
|
||||
rule.setTargetLayer(vl2)
|
||||
rule.setDistance(14)
|
||||
rule.setDistanceUnit(Qgis.RenderUnit.Inches)
|
||||
rule.setDistanceUnitScale(QgsMapUnitScale(15, 25))
|
||||
|
||||
self.assertEqual(rule.labeledLayer(), vl)
|
||||
self.assertEqual(rule.targetLayer(), vl2)
|
||||
self.assertEqual(rule.distance(), 14)
|
||||
self.assertEqual(rule.distanceUnit(), Qgis.RenderUnit.Inches)
|
||||
self.assertEqual(rule.distanceUnitScale().minScale, 15)
|
||||
self.assertEqual(rule.distanceUnitScale().maxScale, 25)
|
||||
|
||||
rule2 = rule.clone()
|
||||
self.assertEqual(rule2.labeledLayer(), vl)
|
||||
self.assertEqual(rule2.targetLayer(), vl2)
|
||||
self.assertEqual(rule2.distance(), 14)
|
||||
self.assertEqual(rule2.distanceUnit(), Qgis.RenderUnit.Inches)
|
||||
self.assertEqual(rule2.distanceUnitScale().minScale, 15)
|
||||
self.assertEqual(rule2.distanceUnitScale().maxScale, 25)
|
||||
|
||||
doc = QDomDocument("testdoc")
|
||||
elem = doc.createElement("test")
|
||||
rule.writeXml(doc, elem, QgsReadWriteContext())
|
||||
|
||||
rule3 = QgsLabelingEngineRuleMinimumDistanceLabelToLabel()
|
||||
rule3.readXml(elem, QgsReadWriteContext())
|
||||
rule3.resolveReferences(p)
|
||||
self.assertEqual(rule3.labeledLayer(), vl)
|
||||
self.assertEqual(rule3.targetLayer(), vl2)
|
||||
self.assertEqual(rule3.distance(), 14)
|
||||
self.assertEqual(rule3.distanceUnit(), Qgis.RenderUnit.Inches)
|
||||
self.assertEqual(rule3.distanceUnitScale().minScale, 15)
|
||||
self.assertEqual(rule3.distanceUnitScale().maxScale, 25)
|
||||
|
||||
def testMaximumDistanceLabelToFeature(self):
|
||||
p = QgsProject()
|
||||
vl = QgsVectorLayer('Point', 'layer 1', 'memory')
|
||||
vl2 = QgsVectorLayer('Point', 'layer 2', 'memory')
|
||||
p.addMapLayers([vl, vl2])
|
||||
|
||||
rule = QgsLabelingEngineRuleMaximumDistanceLabelToFeature()
|
||||
rule.setLabeledLayer(vl)
|
||||
rule.setTargetLayer(vl2)
|
||||
rule.setDistance(14)
|
||||
rule.setDistanceUnit(Qgis.RenderUnit.Inches)
|
||||
rule.setDistanceUnitScale(QgsMapUnitScale(15, 25))
|
||||
rule.setCost(6.6)
|
||||
|
||||
self.assertEqual(rule.labeledLayer(), vl)
|
||||
self.assertEqual(rule.targetLayer(), vl2)
|
||||
self.assertEqual(rule.distance(), 14)
|
||||
self.assertEqual(rule.distanceUnit(), Qgis.RenderUnit.Inches)
|
||||
self.assertEqual(rule.distanceUnitScale().minScale, 15)
|
||||
self.assertEqual(rule.distanceUnitScale().maxScale, 25)
|
||||
self.assertEqual(rule.cost(), 6.6)
|
||||
|
||||
rule2 = rule.clone()
|
||||
self.assertEqual(rule2.labeledLayer(), vl)
|
||||
self.assertEqual(rule2.targetLayer(), vl2)
|
||||
self.assertEqual(rule2.distance(), 14)
|
||||
self.assertEqual(rule2.distanceUnit(), Qgis.RenderUnit.Inches)
|
||||
self.assertEqual(rule2.distanceUnitScale().minScale, 15)
|
||||
self.assertEqual(rule2.distanceUnitScale().maxScale, 25)
|
||||
self.assertEqual(rule2.cost(), 6.6)
|
||||
|
||||
doc = QDomDocument("testdoc")
|
||||
elem = doc.createElement("test")
|
||||
rule.writeXml(doc, elem, QgsReadWriteContext())
|
||||
|
||||
rule3 = QgsLabelingEngineRuleMaximumDistanceLabelToFeature()
|
||||
rule3.readXml(elem, QgsReadWriteContext())
|
||||
rule3.resolveReferences(p)
|
||||
self.assertEqual(rule3.labeledLayer(), vl)
|
||||
self.assertEqual(rule3.targetLayer(), vl2)
|
||||
self.assertEqual(rule3.distance(), 14)
|
||||
self.assertEqual(rule3.distanceUnit(), Qgis.RenderUnit.Inches)
|
||||
self.assertEqual(rule3.distanceUnitScale().minScale, 15)
|
||||
self.assertEqual(rule3.distanceUnitScale().maxScale, 25)
|
||||
self.assertEqual(rule3.cost(), 6.6)
|
||||
|
||||
def testAvoidLabelOverlapWithFeature(self):
|
||||
p = QgsProject()
|
||||
vl = QgsVectorLayer('Point', 'layer 1', 'memory')
|
||||
vl2 = QgsVectorLayer('Point', 'layer 2', 'memory')
|
||||
p.addMapLayers([vl, vl2])
|
||||
|
||||
rule = QgsLabelingEngineRuleAvoidLabelOverlapWithFeature()
|
||||
rule.setLabeledLayer(vl)
|
||||
rule.setTargetLayer(vl2)
|
||||
|
||||
self.assertEqual(rule.labeledLayer(), vl)
|
||||
self.assertEqual(rule.targetLayer(), vl2)
|
||||
|
||||
rule2 = rule.clone()
|
||||
self.assertEqual(rule2.labeledLayer(), vl)
|
||||
self.assertEqual(rule2.targetLayer(), vl2)
|
||||
|
||||
doc = QDomDocument("testdoc")
|
||||
elem = doc.createElement("test")
|
||||
rule.writeXml(doc, elem, QgsReadWriteContext())
|
||||
|
||||
rule3 = QgsLabelingEngineRuleAvoidLabelOverlapWithFeature()
|
||||
rule3.readXml(elem, QgsReadWriteContext())
|
||||
rule3.resolveReferences(p)
|
||||
self.assertEqual(rule3.labeledLayer(), vl)
|
||||
self.assertEqual(rule3.targetLayer(), vl2)
|
||||
|
||||
def test_settings(self):
|
||||
"""
|
||||
Test attaching rules to QgsLabelingEngineSettings
|
||||
"""
|
||||
p = QgsProject()
|
||||
vl = QgsVectorLayer('Point', 'layer 1', 'memory')
|
||||
vl2 = QgsVectorLayer('Point', 'layer 2', 'memory')
|
||||
p.addMapLayers([vl, vl2])
|
||||
|
||||
self.assertFalse(p.labelingEngineSettings().rules())
|
||||
|
||||
rule = QgsLabelingEngineRuleMaximumDistanceLabelToFeature()
|
||||
rule.setLabeledLayer(vl)
|
||||
rule.setTargetLayer(vl2)
|
||||
rule.setCost(6.6)
|
||||
|
||||
label_engine_settings = p.labelingEngineSettings()
|
||||
label_engine_settings.addRule(rule)
|
||||
self.assertEqual([r.id() for r in label_engine_settings.rules()], ['maximumDistanceLabelToFeature'])
|
||||
|
||||
rule2 = QgsLabelingEngineRuleAvoidLabelOverlapWithFeature()
|
||||
rule2.setLabeledLayer(vl2)
|
||||
rule2.setTargetLayer(vl)
|
||||
label_engine_settings.addRule(rule2)
|
||||
self.assertEqual([r.id() for r in label_engine_settings.rules()], ['maximumDistanceLabelToFeature', 'avoidLabelOverlapWithFeature'])
|
||||
|
||||
p.setLabelingEngineSettings(label_engine_settings)
|
||||
|
||||
label_engine_settings = p.labelingEngineSettings()
|
||||
self.assertEqual([r.id() for r in label_engine_settings.rules()],
|
||||
['maximumDistanceLabelToFeature',
|
||||
'avoidLabelOverlapWithFeature'])
|
||||
|
||||
# save, restore project
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
self.assertTrue(p.write(os.path.join(temp_dir, 'p.qgs')))
|
||||
|
||||
p2 = QgsProject()
|
||||
self.assertTrue(p2.read(os.path.join(temp_dir, 'p.qgs')))
|
||||
|
||||
label_engine_settings = p2.labelingEngineSettings()
|
||||
self.assertEqual([r.id() for r in label_engine_settings.rules()],
|
||||
['maximumDistanceLabelToFeature',
|
||||
'avoidLabelOverlapWithFeature'])
|
||||
|
||||
# check layers, settings
|
||||
rule1 = label_engine_settings.rules()[0]
|
||||
self.assertIsInstance(rule1, QgsLabelingEngineRuleMaximumDistanceLabelToFeature)
|
||||
self.assertEqual(rule1.cost(), 6.6)
|
||||
self.assertEqual(rule1.labeledLayer().name(), 'layer 1')
|
||||
self.assertEqual(rule1.targetLayer().name(), 'layer 2')
|
||||
|
||||
rule2 = label_engine_settings.rules()[1]
|
||||
self.assertIsInstance(rule2, QgsLabelingEngineRuleAvoidLabelOverlapWithFeature)
|
||||
self.assertEqual(rule2.labeledLayer().name(), 'layer 2')
|
||||
self.assertEqual(rule2.targetLayer().name(), 'layer 1')
|
||||
|
||||
# test setRules
|
||||
rule = QgsLabelingEngineRuleMinimumDistanceLabelToFeature()
|
||||
rule.setLabeledLayer(vl)
|
||||
rule.setTargetLayer(vl2)
|
||||
rule.setCost(6.6)
|
||||
|
||||
label_engine_settings.setRules([rule])
|
||||
self.assertEqual([r.id() for r in label_engine_settings.rules()],
|
||||
['minimumDistanceLabelToFeature'])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Loading…
x
Reference in New Issue
Block a user