Skip to content

Commit

Permalink
[labeling] Move settings related to removing labels (e.g. max no
Browse files Browse the repository at this point in the history
of labels, min size for labels) to a new class QgsLabelThinningSettings

This new class is designed to contain settings related to how the label
engine removes candidate label positions and reduces the number of
displayed labels.
  • Loading branch information
nyalldawson committed Dec 11, 2019
1 parent 2e25151 commit b1bbc85
Show file tree
Hide file tree
Showing 12 changed files with 366 additions and 48 deletions.
@@ -0,0 +1,93 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/labeling/qgslabelthinningsettings.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/




class QgsLabelThinningSettings
{
%Docstring

Contains settings related to how the label engine removes candidate label positions and reduces the number
of displayed labels.

.. versionadded:: 3.10.2
%End

%TypeHeaderCode
#include "qgslabelthinningsettings.h"
%End
public:

bool limitNumberOfLabelsEnabled() const;
%Docstring
Returns ``True`` if the number of labels drawn for the layer should be limited.

.. seealso:: :py:func:`maximumNumberLabels`

.. seealso:: :py:func:`setLimitNumberLabelsEnabled`
%End

void setLimitNumberLabelsEnabled( bool enabled );
%Docstring
Sets wheter the the number of labels drawn for the layer should be limited.

.. seealso:: :py:func:`setMaximumNumberLabels`

.. seealso:: :py:func:`limitNumberOfLabelsEnabled`
%End

int maximumNumberLabels() const;
%Docstring
Returns the maximum number of labels which should be drawn for this layer.
This only has an effect if limitNumberOfLabelsEnabled() is ``True``.

.. seealso:: :py:func:`limitNumberOfLabelsEnabled`

.. seealso:: :py:func:`setMaximumNumberLabels`
%End

void setMaximumNumberLabels( int number );
%Docstring
Sets the maximum ``number`` of labels which should be drawn for this layer.
This only has an effect if limitNumberOfLabelsEnabled() is ``True``.

.. seealso:: :py:func:`setLimitNumberLabelsEnabled`

.. seealso:: :py:func:`maximumNumberLabels`
%End

double minimumFeatureSize() const;
%Docstring
Returns the minimum feature size (in millimeters) for a feature to be labelled.

.. seealso:: :py:func:`setMinimumFeatureSize`
%End

void setMinimumFeatureSize( double size );
%Docstring
Sets the minimum feature ``size`` (in millimeters) for a feature to be labelled.

.. seealso:: :py:func:`minimumFeatureSize`
%End

void updateDataDefinedProperties( const QgsPropertyCollection &properties, QgsExpressionContext &context );
%Docstring
Updates the thinning settings to respect any data defined properties
set within the specified ``properties`` collection.
%End

};

/************************************************************************
* This file has been generated automatically from *
* *
* src/core/labeling/qgslabelthinningsettings.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
34 changes: 28 additions & 6 deletions python/core/auto_generated/labeling/qgspallabeling.sip.in
Expand Up @@ -462,17 +462,20 @@ Returns the QgsExpression for this label settings. May be ``None`` if isExpressi

bool mergeLines;

bool limitNumLabels;

int maxNumLabels;

double minFeatureSize;


%Property( name = limitNumLabels, get = _limitNumLabels, set = _setLimitNumLabels )
%Property( name = maxNumLabels, get = _maxNumLabels, set = _setMaxNumLabels )
%Property( name = minFeatureSize, get = _minFeatureSize, set = _setMinFeatureSize )
%Property( name = obstacle, get = _getIsObstacle, set = _setIsObstacle )
%Property( name = obstacleFactor, get = _getObstacleFactor, set = _setObstacleFactor )
%Property( name = obstacleType, get = _getObstacleType, set = _setObstacleType )

bool _limitNumLabels() const;
void _setLimitNumLabels( bool limit );
int _maxNumLabels() const;
void _setMaxNumLabels( int max );
double _minFeatureSize() const;
void _setMinFeatureSize( double size );
bool _getIsObstacle() const;
void _setIsObstacle( bool obstacle );
double _getObstacleFactor() const;
Expand Down Expand Up @@ -610,6 +613,25 @@ Sets the label obstacle ``settings``.
.. versionadded:: 3.10.2
%End


QgsLabelThinningSettings &thinningSettings();
%Docstring
Returns the label thinning settings.

.. seealso:: :py:func:`setThinningSettings`

.. versionadded:: 3.12
%End

void setThinningSettings( const QgsLabelThinningSettings &settings );
%Docstring
Sets the label thinning ``settings``.

.. seealso:: :py:func:`thinningSettings`

.. versionadded:: 3.12
%End

static QPixmap labelSettingsPreviewPixmap( const QgsPalLayerSettings &settings, QSize size, const QString &previewText = QString(), int padding = 0 );
%Docstring
Returns a pixmap preview for label ``settings``.
Expand Down
1 change: 1 addition & 0 deletions python/core/core_auto.sip
Expand Up @@ -311,6 +311,7 @@
%Include auto_generated/labeling/qgslabelingenginesettings.sip
%Include auto_generated/labeling/qgslabelobstaclesettings.sip
%Include auto_generated/labeling/qgslabelsearchtree.sip
%Include auto_generated/labeling/qgslabelthinningsettings.sip
%Include auto_generated/labeling/qgspallabeling.sip
%Include auto_generated/labeling/qgsrulebasedlabeling.sip
%Include auto_generated/labeling/qgsvectorlayerlabeling.sip
Expand Down
2 changes: 2 additions & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -538,6 +538,7 @@ SET(QGIS_CORE_SRCS
labeling/qgslabelingenginesettings.cpp
labeling/qgslabelobstaclesettings.cpp
labeling/qgslabelsearchtree.cpp
labeling/qgslabelthinningsettings.cpp
labeling/qgspallabeling.cpp
labeling/qgsrulebasedlabeling.cpp
labeling/qgstextlabelfeature.cpp
Expand Down Expand Up @@ -1021,6 +1022,7 @@ SET(QGIS_CORE_HDRS
labeling/qgslabelingenginesettings.h
labeling/qgslabelobstaclesettings.h
labeling/qgslabelsearchtree.h
labeling/qgslabelthinningsettings.h
labeling/qgspallabeling.h
labeling/qgsrulebasedlabeling.h
labeling/qgstextlabelfeature.h
Expand Down
2 changes: 1 addition & 1 deletion src/core/labeling/qgslabelobstaclesettings.h
Expand Up @@ -18,7 +18,7 @@

#include "qgis_core.h"
#include "qgis_sip.h"
#include "qgsgeos.h"
#include "qgsgeometry.h"

class QgsPropertyCollection;
class QgsExpressionContext;
Expand Down
30 changes: 30 additions & 0 deletions src/core/labeling/qgslabelthinningsettings.cpp
@@ -0,0 +1,30 @@
/***************************************************************************
qgslabelthinningsettings.cpp
----------------------------
Date : December 2019
Copyright : (C) 2019 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 "qgslabelthinningsettings.h"
#include "qgspropertycollection.h"
#include "qgsexpressioncontext.h"
#include "qgspallabeling.h"


void QgsLabelThinningSettings::updateDataDefinedProperties( const QgsPropertyCollection &properties, QgsExpressionContext &context )
{
Q_UNUSED( properties )
Q_UNUSED( context )

// temporarily avoid warnings
int unused = 1;
( void )unused;
}
93 changes: 93 additions & 0 deletions src/core/labeling/qgslabelthinningsettings.h
@@ -0,0 +1,93 @@
/***************************************************************************
qgslabelthinningsettings.h
--------------------------
Date : December 2019
Copyright : (C) 2019 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 QGSLABELTHINNINGSETTINGS_H
#define QGSLABELTHINNINGSETTINGS_H

#include "qgis_core.h"
#include "qgis_sip.h"

class QgsPropertyCollection;
class QgsExpressionContext;

/**
* \ingroup core
* \class QgsLabelThinningSettings
*
* Contains settings related to how the label engine removes candidate label positions and reduces the number
* of displayed labels.
*
* \since QGIS 3.10.2
*/
class CORE_EXPORT QgsLabelThinningSettings
{
public:

/**
* Returns TRUE if the number of labels drawn for the layer should be limited.
* \see maximumNumberLabels()
* \see setLimitNumberLabelsEnabled()
*/
bool limitNumberOfLabelsEnabled() const { return mLimitNumLabels; }

/**
* Sets wheter the the number of labels drawn for the layer should be limited.
* \see setMaximumNumberLabels()
* \see limitNumberOfLabelsEnabled()
*/
void setLimitNumberLabelsEnabled( bool enabled ) { mLimitNumLabels = enabled; }

/**
* Returns the maximum number of labels which should be drawn for this layer.
* This only has an effect if limitNumberOfLabelsEnabled() is TRUE.
* \see limitNumberOfLabelsEnabled()
* \see setMaximumNumberLabels()
*/
int maximumNumberLabels() const { return mMaxNumLabels; }

/**
* Sets the maximum \a number of labels which should be drawn for this layer.
* This only has an effect if limitNumberOfLabelsEnabled() is TRUE.
* \see setLimitNumberLabelsEnabled()
* \see maximumNumberLabels()
*/
void setMaximumNumberLabels( int number ) { mMaxNumLabels = number; }

/**
* Returns the minimum feature size (in millimeters) for a feature to be labelled.
* \see setMinimumFeatureSize()
*/
double minimumFeatureSize() const { return mMinFeatureSize; }

/**
* Sets the minimum feature \a size (in millimeters) for a feature to be labelled.
* \see minimumFeatureSize()
*/
void setMinimumFeatureSize( double size ) { mMinFeatureSize = size; }

/**
* Updates the thinning settings to respect any data defined properties
* set within the specified \a properties collection.
*/
void updateDataDefinedProperties( const QgsPropertyCollection &properties, QgsExpressionContext &context );

private:

bool mLimitNumLabels = false;
int mMaxNumLabels = 2000;
double mMinFeatureSize = 0;
};

#endif // QGSLABELTHINNINGSETTINGS_H
35 changes: 18 additions & 17 deletions src/core/labeling/qgspallabeling.cpp
Expand Up @@ -344,9 +344,6 @@ QgsPalLayerSettings &QgsPalLayerSettings::operator=( const QgsPalLayerSettings &

labelPerPart = s.labelPerPart;
mergeLines = s.mergeLines && !s.addDirectionSymbol;
minFeatureSize = s.minFeatureSize;
limitNumLabels = s.limitNumLabels;
maxNumLabels = s.maxNumLabels;
zIndex = s.zIndex;

mFormat = s.mFormat;
Expand All @@ -355,6 +352,7 @@ QgsPalLayerSettings &QgsPalLayerSettings::operator=( const QgsPalLayerSettings &
mCallout.reset( s.mCallout ? s.mCallout->clone() : nullptr );

mObstacleSettings = s.mObstacleSettings;
mThinningSettings = s.mThinningSettings;

geometryGenerator = s.geometryGenerator;
geometryGeneratorEnabled = s.geometryGeneratorEnabled;
Expand Down Expand Up @@ -808,9 +806,9 @@ void QgsPalLayerSettings::readFromLayerCustomProperties( QgsVectorLayer *layer )

labelPerPart = layer->customProperty( QStringLiteral( "labeling/labelPerPart" ) ).toBool();
mergeLines = layer->customProperty( QStringLiteral( "labeling/mergeLines" ) ).toBool();
minFeatureSize = layer->customProperty( QStringLiteral( "labeling/minFeatureSize" ) ).toDouble();
limitNumLabels = layer->customProperty( QStringLiteral( "labeling/limitNumLabels" ), QVariant( false ) ).toBool();
maxNumLabels = layer->customProperty( QStringLiteral( "labeling/maxNumLabels" ), QVariant( 2000 ) ).toInt();
mThinningSettings.setMinimumFeatureSize( layer->customProperty( QStringLiteral( "labeling/minFeatureSize" ) ).toDouble() );
mThinningSettings.setLimitNumberLabelsEnabled( layer->customProperty( QStringLiteral( "labeling/limitNumLabels" ), QVariant( false ) ).toBool() );
mThinningSettings.setMaximumNumberLabels( layer->customProperty( QStringLiteral( "labeling/maxNumLabels" ), QVariant( 2000 ) ).toInt() );
mObstacleSettings.setIsObstacle( layer->customProperty( QStringLiteral( "labeling/obstacle" ), QVariant( true ) ).toBool() );
mObstacleSettings.setFactor( layer->customProperty( QStringLiteral( "labeling/obstacleFactor" ), QVariant( 1.0 ) ).toDouble() );
mObstacleSettings.setType( static_cast< QgsLabelObstacleSettings::ObstacleType >( layer->customProperty( QStringLiteral( "labeling/obstacleType" ), QVariant( PolygonInterior ) ).toUInt() ) );
Expand Down Expand Up @@ -1034,9 +1032,9 @@ void QgsPalLayerSettings::readXml( const QDomElement &elem, const QgsReadWriteCo

labelPerPart = renderingElem.attribute( QStringLiteral( "labelPerPart" ) ).toInt();
mergeLines = renderingElem.attribute( QStringLiteral( "mergeLines" ) ).toInt();
minFeatureSize = renderingElem.attribute( QStringLiteral( "minFeatureSize" ) ).toDouble();
limitNumLabels = renderingElem.attribute( QStringLiteral( "limitNumLabels" ), QStringLiteral( "0" ) ).toInt();
maxNumLabels = renderingElem.attribute( QStringLiteral( "maxNumLabels" ), QStringLiteral( "2000" ) ).toInt();
mThinningSettings.setMinimumFeatureSize( renderingElem.attribute( QStringLiteral( "minFeatureSize" ) ).toDouble() );
mThinningSettings.setLimitNumberLabelsEnabled( renderingElem.attribute( QStringLiteral( "limitNumLabels" ), QStringLiteral( "0" ) ).toInt() );
mThinningSettings.setMaximumNumberLabels( renderingElem.attribute( QStringLiteral( "maxNumLabels" ), QStringLiteral( "2000" ) ).toInt() );
mObstacleSettings.setIsObstacle( renderingElem.attribute( QStringLiteral( "obstacle" ), QStringLiteral( "1" ) ).toInt() );
mObstacleSettings.setFactor( renderingElem.attribute( QStringLiteral( "obstacleFactor" ), QStringLiteral( "1" ) ).toDouble() );
mObstacleSettings.setType( static_cast< QgsLabelObstacleSettings::ObstacleType >( renderingElem.attribute( QStringLiteral( "obstacleType" ), QString::number( PolygonInterior ) ).toUInt() ) );
Expand Down Expand Up @@ -1181,9 +1179,9 @@ QDomElement QgsPalLayerSettings::writeXml( QDomDocument &doc, const QgsReadWrite

renderingElem.setAttribute( QStringLiteral( "labelPerPart" ), labelPerPart );
renderingElem.setAttribute( QStringLiteral( "mergeLines" ), mergeLines );
renderingElem.setAttribute( QStringLiteral( "minFeatureSize" ), minFeatureSize );
renderingElem.setAttribute( QStringLiteral( "limitNumLabels" ), limitNumLabels );
renderingElem.setAttribute( QStringLiteral( "maxNumLabels" ), maxNumLabels );
renderingElem.setAttribute( QStringLiteral( "minFeatureSize" ), mThinningSettings.minimumFeatureSize() );
renderingElem.setAttribute( QStringLiteral( "limitNumLabels" ), mThinningSettings.limitNumberOfLabelsEnabled() );
renderingElem.setAttribute( QStringLiteral( "maxNumLabels" ), mThinningSettings.maximumNumberLabels() );
renderingElem.setAttribute( QStringLiteral( "obstacle" ), mObstacleSettings.isObstacle() );
renderingElem.setAttribute( QStringLiteral( "obstacleFactor" ), mObstacleSettings.factor() );
renderingElem.setAttribute( QStringLiteral( "obstacleType" ), static_cast< unsigned int >( mObstacleSettings.type() ) );
Expand Down Expand Up @@ -1972,7 +1970,10 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
}
}

if ( minFeatureSize > 0 && !checkMinimumSizeMM( context, geom, minFeatureSize ) )
QgsLabelThinningSettings featureThinningSettings = mThinningSettings;
featureThinningSettings.updateDataDefinedProperties( mDataDefinedProperties, context.expressionContext() );

if ( featureThinningSettings.minimumFeatureSize() > 0 && !checkMinimumSizeMM( context, geom, featureThinningSettings.minimumFeatureSize() ) )
return;

if ( !geos_geom_clone )
Expand All @@ -1981,18 +1982,18 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext
// likelihood exists label will be registered with PAL and may be drawn
// check if max number of features to label (already registered with PAL) has been reached
// Debug output at end of QgsPalLabeling::drawLabeling(), when deleting temp geometries
if ( limitNumLabels )
if ( featureThinningSettings.limitNumberOfLabelsEnabled() )
{
if ( !maxNumLabels )
if ( !featureThinningSettings.maximumNumberLabels() )
{
return;
}
if ( mFeatsRegPal >= maxNumLabels )
if ( mFeatsRegPal >= featureThinningSettings.maximumNumberLabels() )
{
return;
}

int divNum = static_cast< int >( ( static_cast< double >( mFeaturesToLabel ) / maxNumLabels ) + 0.5 ); // NOLINT
int divNum = static_cast< int >( ( static_cast< double >( mFeaturesToLabel ) / featureThinningSettings.maximumNumberLabels() ) + 0.5 ); // NOLINT
if ( divNum && ( mFeatsRegPal == static_cast< int >( mFeatsSendingToPal / divNum ) ) )
{
mFeatsSendingToPal += 1;
Expand Down

0 comments on commit b1bbc85

Please sign in to comment.