Skip to content

Commit

Permalink
Avoid duplicate code for evaluating label obstacle data defined values
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Dec 7, 2019
1 parent 0443299 commit 7f38c41
Show file tree
Hide file tree
Showing 11 changed files with 138 additions and 85 deletions.
7 changes: 7 additions & 0 deletions python/core/auto_generated/qgslabelobstaclesettings.sip.in
Expand Up @@ -8,6 +8,7 @@




class QgsLabelObstacleSettings
{
%Docstring
Expand Down Expand Up @@ -95,6 +96,12 @@ Controls how features act as obstacles for labels.
.. seealso:: :py:func:`isObstacle`

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

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

};
Expand Down
1 change: 1 addition & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -266,6 +266,7 @@ SET(QGIS_CORE_SRCS
qgslabelfeature.cpp
qgslabelingengine.cpp
qgslabelingenginesettings.cpp
qgslabelobstaclesettings.cpp
qgslabelsearchtree.cpp
qgslayerdefinition.cpp
qgslegendrenderer.cpp
Expand Down
2 changes: 0 additions & 2 deletions src/core/qgslabelfeature.cpp
Expand Up @@ -31,8 +31,6 @@ QgsLabelFeature::QgsLabelFeature( QgsFeatureId id, geos::unique_ptr geometry, QS
, mOffsetType( QgsPalLayerSettings::FromPoint )
, mRepeatDistance( 0 )
, mAlwaysShow( false )
, mIsObstacle( false )
, mObstacleFactor( 1 )
{
}

Expand Down
9 changes: 0 additions & 9 deletions src/core/qgslabelfeature.h
Expand Up @@ -305,15 +305,6 @@ class CORE_EXPORT QgsLabelFeature
//! Sets whether label should be always shown (sets very high label priority)
void setAlwaysShow( bool enabled ) { mAlwaysShow = enabled; }



/**
* Returns whether the feature will act as an obstacle for labels.
* \returns TRUE if feature is an obstacle
* \see setIsObstacle
*/
bool isObstacle() const { return mIsObstacle; }

/**
* Returns the feature's arrangement flags.
* \see setArrangementFlags
Expand Down
40 changes: 40 additions & 0 deletions src/core/qgslabelobstaclesettings.cpp
@@ -0,0 +1,40 @@
/***************************************************************************
qgslabelobstaclesettings.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 "qgslabelobstaclesettings.h"
#include "qgspropertycollection.h"
#include "qgsexpressioncontext.h"
#include "qgspallabeling.h"

void QgsLabelObstacleSettings::updateDataDefinedProperties( const QgsPropertyCollection &properties, QgsExpressionContext &context )
{
if ( properties.isActive( QgsPalLayerSettings::ObstacleFactor ) )
{
context.setOriginalValueVariable( mObstacleFactor );
QVariant exprVal = properties.value( QgsPalLayerSettings::ObstacleFactor, context );
if ( exprVal.isValid() )
{
bool ok;
double factorD = exprVal.toDouble( &ok );
if ( ok )
{
factorD = qBound( 0.0, factorD, 10.0 );
factorD = factorD / 5.0 + 0.0001; // convert 0 -> 10 to 0.0001 -> 2.0
mObstacleFactor = factorD;
}
}
}

}
9 changes: 9 additions & 0 deletions src/core/qgslabelobstaclesettings.h
Expand Up @@ -19,6 +19,9 @@
#include "qgis_core.h"
#include "qgis_sip.h"

class QgsPropertyCollection;
class QgsExpressionContext;

/**
* \ingroup core
* \class QgsLabelObstacleSettings
Expand Down Expand Up @@ -117,6 +120,12 @@ class CORE_EXPORT QgsLabelObstacleSettings
mObstacleType = type;
}

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

private:

bool mIsObstacle = true;
Expand Down
41 changes: 2 additions & 39 deletions src/core/qgspallabeling.cpp
Expand Up @@ -2480,25 +2480,7 @@ void QgsPalLayerSettings::registerFeature( const QgsFeature &f, QgsRenderContext

QgsLabelObstacleSettings os = mObstacleSettings;
os.setIsObstacle( isObstacle );

double featObstacleFactor = mObstacleSettings.factor();
if ( isObstacle && mDataDefinedProperties.isActive( QgsPalLayerSettings::ObstacleFactor ) )
{
context.expressionContext().setOriginalValueVariable( featObstacleFactor );
exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::ObstacleFactor, context.expressionContext() );
if ( exprVal.isValid() )
{
bool ok;
double factorD = exprVal.toDouble( &ok );
if ( ok )
{
factorD = qBound( 0.0, factorD, 10.0 );
factorD = factorD / 5.0 + 0.0001; // convert 0 -> 10 to 0.0001 -> 2.0
featObstacleFactor = factorD;
}
}
}
os.setFactor( featObstacleFactor );
os.updateDataDefinedProperties( mDataDefinedProperties, context.expressionContext() );
lf->setObstacleSettings( os );

QVector< QgsPalLayerSettings::PredefinedPointPosition > positionOrder = predefinedPositionOrder;
Expand Down Expand Up @@ -2568,26 +2550,7 @@ void QgsPalLayerSettings::registerObstacleFeature( const QgsFeature &f, QgsRende

QgsLabelObstacleSettings os = mObstacleSettings;
os.setIsObstacle( true );

double featObstacleFactor = mObstacleSettings.factor();
if ( mDataDefinedProperties.isActive( QgsPalLayerSettings::ObstacleFactor ) )
{
context.expressionContext().setOriginalValueVariable( featObstacleFactor );
QVariant exprVal = mDataDefinedProperties.value( QgsPalLayerSettings::ObstacleFactor, context.expressionContext() );
if ( exprVal.isValid() )
{
bool ok;
double factorD = exprVal.toDouble( &ok );
if ( ok )
{
factorD = qBound( 0.0, factorD, 10.0 );
factorD = factorD / 5.0 + 0.0001; // convert 0 -> 10 to 0.0001 -> 2.0
featObstacleFactor = factorD;
}
}
}
os.setFactor( featObstacleFactor );

os.updateDataDefinedProperties( mDataDefinedProperties, context.expressionContext() );
( *obstacleFeature )->setObstacleSettings( os );

mFeatsRegPal++;
Expand Down
4 changes: 3 additions & 1 deletion src/core/qgsvectorlayerdiagramprovider.cpp
Expand Up @@ -295,7 +295,9 @@ QgsLabelFeature *QgsVectorLayerDiagramProvider::registerDiagram( QgsFeature &fea
lf->setHasFixedAngle( true );
lf->setFixedAngle( 0 );
lf->setAlwaysShow( alwaysShow );
lf->setIsObstacle( isObstacle );
QgsLabelObstacleSettings os;
os.setIsObstacle( isObstacle );
lf->setObstacleSettings( os );
if ( geosObstacleGeomClone )
{
lf->setObstacleGeometry( std::move( geosObstacleGeomClone ) );
Expand Down
1 change: 1 addition & 0 deletions tests/src/python/CMakeLists.txt
Expand Up @@ -91,6 +91,7 @@ ADD_PYTHON_TEST(PyQgsImageCache test_qgsimagecache.py)
ADD_PYTHON_TEST(PyQgsImageSourceLineEdit test_qgsimagesourcelineedit.py)
ADD_PYTHON_TEST(PyQgsInterval test_qgsinterval.py)
ADD_PYTHON_TEST(PyQgsJsonUtils test_qgsjsonutils.py)
ADD_PYTHON_TEST(PyQgsLabelObstacleSettings test_qgslabelobstaclesettings.py)
ADD_PYTHON_TEST(PyQgsLabelSettingsWidget test_qgslabelsettingswidget.py)
ADD_PYTHON_TEST(PyQgsLayerMetadata test_qgslayermetadata.py)
ADD_PYTHON_TEST(PyQgsLayerTreeMapCanvasBridge test_qgslayertreemapcanvasbridge.py)
Expand Down
75 changes: 75 additions & 0 deletions tests/src/python/test_qgslabelobstaclesettings.py
@@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
"""QGIS Unit tests for QgsLabelObstacleSettings
.. 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.
"""
__author__ = 'Nyall Dawson'
__date__ = '2019-12-07'
__copyright__ = 'Copyright 2019, The QGIS Project'

import qgis # NOQA

from qgis.core import (QgsProperty,
QgsPropertyCollection,
QgsPalLayerSettings,
QgsLabelObstacleSettings,
QgsExpressionContext,
QgsExpressionContextScope)

from qgis.testing import unittest


class TestQgsLabelObstacleSettings(unittest.TestCase):

def test_obstacle_settings(self):
"""
Test obstacle settings
"""
settings = QgsLabelObstacleSettings()
settings.setIsObstacle(True)
self.assertTrue(settings.isObstacle())
settings.setIsObstacle(False)
self.assertFalse(settings.isObstacle())

settings.setFactor(0.1)
self.assertEqual(settings.factor(), 0.1)

settings.setType(QgsLabelObstacleSettings.PolygonWhole)
self.assertEqual(settings.type(), QgsLabelObstacleSettings.PolygonWhole)

# check that compatibility code works
pal_settings = QgsPalLayerSettings()
pal_settings.obstacle = True
self.assertTrue(pal_settings.obstacle)
self.assertTrue(pal_settings.obstacleSettings().isObstacle())
pal_settings.obstacle = False
self.assertFalse(pal_settings.obstacle)
self.assertFalse(pal_settings.obstacleSettings().isObstacle())

pal_settings.obstacleFactor = 0.2
self.assertEqual(pal_settings.obstacleFactor, 0.2)
self.assertEqual(pal_settings.obstacleSettings().factor(), 0.2)

pal_settings.obstacleType = QgsPalLayerSettings.PolygonWhole
self.assertEqual(pal_settings.obstacleType, QgsPalLayerSettings.PolygonWhole)
self.assertEqual(pal_settings.obstacleSettings().type(), QgsLabelObstacleSettings.PolygonWhole)

def testUpdateDataDefinedProps(self):
settings = QgsLabelObstacleSettings()
settings.setFactor(0.1)
self.assertEqual(settings.factor(), 0.1)

props = QgsPropertyCollection()
props.setProperty(QgsPalLayerSettings.ObstacleFactor, QgsProperty.fromExpression('@factor'))
context = QgsExpressionContext()
scope = QgsExpressionContextScope()
scope.setVariable('factor', 9)
props.updateDataDefinedProperties(props, context)
self.assertEqual(settings.factor(), 0.1)


if __name__ == '__main__':
unittest.main()
34 changes: 0 additions & 34 deletions tests/src/python/test_qgspallabeling_placement.py
Expand Up @@ -109,40 +109,6 @@ def setUpClass(cls):
TestPlacementBase.setUpClass()
cls.layer = None

def test_obstacle_settings(self):
"""
Test obstacle settings
"""

settings = QgsLabelObstacleSettings()
settings.setIsObstacle(True)
self.assertTrue(settings.isObstacle())
settings.setIsObstacle(False)
self.assertFalse(settings.isObstacle())

settings.setFactor(0.1)
self.assertEqual(settings.factor(), 0.1)

settings.setType(QgsLabelObstacleSettings.PolygonWhole)
self.assertEqual(settings.type(), QgsLabelObstacleSettings.PolygonWhole)

# check that compatibility code works
pal_settings = QgsPalLayerSettings()
pal_settings.obstacle = True
self.assertTrue(pal_settings.obstacle)
self.assertTrue(pal_settings.obstacleSettings().isObstacle())
pal_settings.obstacle = False
self.assertFalse(pal_settings.obstacle)
self.assertFalse(pal_settings.obstacleSettings().isObstacle())

pal_settings.obstacleFactor = 0.2
self.assertEqual(pal_settings.obstacleFactor, 0.2)
self.assertEqual(pal_settings.obstacleSettings().factor(), 0.2)

pal_settings.obstacleType = QgsPalLayerSettings.PolygonWhole
self.assertEqual(pal_settings.obstacleType, QgsPalLayerSettings.PolygonWhole)
self.assertEqual(pal_settings.obstacleSettings().type(), QgsLabelObstacleSettings.PolygonWhole)

def test_point_placement_around(self):
# Default point label placement
self.layer = TestQgsPalLabeling.loadFeatureLayer('point')
Expand Down

0 comments on commit 7f38c41

Please sign in to comment.