qgsvectorlayerelevationproperties: Add support for custom tolerance

This allows to define a custom tolerance. If this tolerance is
enabled, then this tolerance is used instead of the one defined in the
elevation profile widget.

This custom tolerance is enabled and set to 0 by default for Lines and
Polygons. Indeed, most of the time, only want to use the tolerance for
points.
This commit is contained in:
Jean Felder 2024-10-25 13:25:12 +02:00 committed by Loïc Bartoletti
parent fe5f8e63e1
commit 2db1f20af3
7 changed files with 243 additions and 9 deletions

View File

@ -147,6 +147,52 @@ Sets the feature extrusion height.
the :py:func:`~QgsVectorLayerElevationProperties.zScale` factor is NOT applied to extrusion heights.
.. seealso:: :py:func:`extrusionHeight`
%End
bool customToleranceEnabled() const;
%Docstring
Returns ``True`` if custom tolerance is enabled.
.. seealso:: :py:func:`setCustomToleranceEnabled`
.. seealso:: :py:func:`customTolerance`
%End
void setCustomToleranceEnabled( bool enabled );
%Docstring
Sets whether custom tolerance is ``enabled``.
.. seealso:: :py:func:`customToleranceEnabled`
.. seealso:: :py:func:`setCustomTolerance`
%End
double customTolerance() const;
%Docstring
Returns the feature custom tolerance.
.. warning::
custom tolerance is only applied if :py:func:`~QgsVectorLayerElevationProperties.customToleranceEnabled` is ``True``.
If enabled, the profile generator will use this tolerance instead of the one
defined in the elevation profile widget.
.. seealso:: :py:func:`setCustomTolerance`
%End
void setCustomTolerance( double tolerance );
%Docstring
Sets the feature custom tolerance.
.. warning::
custom tolerance is only applied if :py:func:`~QgsVectorLayerElevationProperties.customToleranceEnabled` is ``True``.
If enabled, the profile generator will use this tolerance instead of the one
defined in the elevation profile widget.
.. seealso:: :py:func:`customTolerance`
%End
bool respectLayerSymbology() const;

View File

@ -147,6 +147,52 @@ Sets the feature extrusion height.
the :py:func:`~QgsVectorLayerElevationProperties.zScale` factor is NOT applied to extrusion heights.
.. seealso:: :py:func:`extrusionHeight`
%End
bool customToleranceEnabled() const;
%Docstring
Returns ``True`` if custom tolerance is enabled.
.. seealso:: :py:func:`setCustomToleranceEnabled`
.. seealso:: :py:func:`customTolerance`
%End
void setCustomToleranceEnabled( bool enabled );
%Docstring
Sets whether custom tolerance is ``enabled``.
.. seealso:: :py:func:`customToleranceEnabled`
.. seealso:: :py:func:`setCustomTolerance`
%End
double customTolerance() const;
%Docstring
Returns the feature custom tolerance.
.. warning::
custom tolerance is only applied if :py:func:`~QgsVectorLayerElevationProperties.customToleranceEnabled` is ``True``.
If enabled, the profile generator will use this tolerance instead of the one
defined in the elevation profile widget.
.. seealso:: :py:func:`setCustomTolerance`
%End
void setCustomTolerance( double tolerance );
%Docstring
Sets the feature custom tolerance.
.. warning::
custom tolerance is only applied if :py:func:`~QgsVectorLayerElevationProperties.customToleranceEnabled` is ``True``.
If enabled, the profile generator will use this tolerance instead of the one
defined in the elevation profile widget.
.. seealso:: :py:func:`customTolerance`
%End
bool respectLayerSymbology() const;

View File

@ -54,6 +54,8 @@ QDomElement QgsVectorLayerElevationProperties::writeXml( QDomElement &parentElem
element.setAttribute( QStringLiteral( "extrusionEnabled" ), mEnableExtrusion ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
element.setAttribute( QStringLiteral( "extrusion" ), qgsDoubleToString( mExtrusionHeight ) );
element.setAttribute( QStringLiteral( "customToleranceEnabled" ), mEnableCustomTolerance ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
element.setAttribute( QStringLiteral( "customTolerance" ), qgsDoubleToString( mCustomTolerance ) );
element.setAttribute( QStringLiteral( "clamping" ), qgsEnumValueToKey( mClamping ) );
element.setAttribute( QStringLiteral( "binding" ), qgsEnumValueToKey( mBinding ) );
element.setAttribute( QStringLiteral( "type" ), qgsEnumValueToKey( mType ) );
@ -93,6 +95,8 @@ bool QgsVectorLayerElevationProperties::readXml( const QDomElement &element, con
mType = qgsEnumKeyToValue( elevationElement.attribute( QStringLiteral( "type" ) ), Qgis::VectorProfileType::IndividualFeatures );
mEnableExtrusion = elevationElement.attribute( QStringLiteral( "extrusionEnabled" ), QStringLiteral( "0" ) ).toInt();
mExtrusionHeight = elevationElement.attribute( QStringLiteral( "extrusion" ), QStringLiteral( "0" ) ).toDouble();
mEnableCustomTolerance = elevationElement.attribute( QStringLiteral( "customToleranceEnabled" ), QStringLiteral( "0" ) ).toInt();
mCustomTolerance = elevationElement.attribute( QStringLiteral( "customTolerance" ), QStringLiteral( "0" ) ).toDouble();
mSymbology = qgsEnumKeyToValue( elevationElement.attribute( QStringLiteral( "symbology" ) ), Qgis::ProfileSurfaceSymbology::Line );
if ( elevationElement.hasAttribute( QStringLiteral( "elevationLimit" ) ) )
mElevationLimit = elevationElement.attribute( QStringLiteral( "elevationLimit" ) ).toDouble();
@ -135,6 +139,12 @@ void QgsVectorLayerElevationProperties::setDefaultsFromLayer( QgsMapLayer *layer
mEnableExtrusion = false;
mExtrusionHeight = 0;
// By default override default tolerance for Polygon and Line
// to avoid unexpected behaviors.
// For example, see: https://github.com/qgis/QGIS/issues/58016
mEnableCustomTolerance = vlayer->geometryType() != Qgis::GeometryType::Point;
mCustomTolerance = 0;
mDataDefinedProperties.clear();
mBinding = Qgis::AltitudeBinding::Centroid;
@ -157,6 +167,8 @@ QgsVectorLayerElevationProperties *QgsVectorLayerElevationProperties::clone() co
res->setType( mType );
res->setExtrusionEnabled( mEnableExtrusion );
res->setExtrusionHeight( mExtrusionHeight );
res->setCustomToleranceEnabled( mEnableCustomTolerance );
res->setCustomTolerance( mCustomTolerance );
res->setProfileLineSymbol( mProfileLineSymbol->clone() );
res->setProfileFillSymbol( mProfileFillSymbol->clone() );
res->setProfileMarkerSymbol( mProfileMarkerSymbol->clone() );
@ -228,6 +240,11 @@ QString QgsVectorLayerElevationProperties::htmlSummary() const
}
}
if ( mEnableCustomTolerance )
{
properties << tr( "CustomTolerance: %1" ).arg( mCustomTolerance );
}
properties << tr( "Scale: %1" ).arg( mZScale );
return QStringLiteral( "<li>%1</li>" ).arg( properties.join( QLatin1String( "</li><li>" ) ) );
@ -305,6 +322,26 @@ void QgsVectorLayerElevationProperties::setExtrusionHeight( double height )
emit profileGenerationPropertyChanged();
}
void QgsVectorLayerElevationProperties::setCustomTolerance( double tolerance )
{
if ( mCustomTolerance == tolerance )
return;
mCustomTolerance = tolerance;
emit changed();
emit profileGenerationPropertyChanged();
}
void QgsVectorLayerElevationProperties::setCustomToleranceEnabled( bool enabled )
{
if ( mEnableCustomTolerance == enabled )
return;
mEnableCustomTolerance = enabled;
emit changed();
emit profileGenerationPropertyChanged();
}
void QgsVectorLayerElevationProperties::setRespectLayerSymbology( bool enabled )
{
if ( mRespectLayerSymbology == enabled )

View File

@ -142,6 +142,46 @@ class CORE_EXPORT QgsVectorLayerElevationProperties : public QgsMapLayerElevatio
*/
void setExtrusionHeight( double height );
/**
* Returns TRUE if custom tolerance is enabled.
*
* \see setCustomToleranceEnabled()
* \see customTolerance()
*/
bool customToleranceEnabled() const { return mEnableCustomTolerance; }
/**
* Sets whether custom tolerance is \a enabled.
*
* \see customToleranceEnabled()
* \see setCustomTolerance()
*/
void setCustomToleranceEnabled( bool enabled );
/**
* Returns the feature custom tolerance.
*
* \warning custom tolerance is only applied if customToleranceEnabled() is TRUE.
*
* If enabled, the profile generator will use this tolerance instead of the one
* defined in the elevation profile widget.
*
* \see setCustomTolerance()
*/
double customTolerance() const { return mCustomTolerance; }
/**
* Sets the feature custom tolerance.
*
* \warning custom tolerance is only applied if customToleranceEnabled() is TRUE.
*
* If enabled, the profile generator will use this tolerance instead of the one
* defined in the elevation profile widget.
*
* \see customTolerance()
*/
void setCustomTolerance( double tolerance );
/**
* Returns TRUE if layer symbology should be respected when rendering elevation profile plots.
*
@ -313,6 +353,8 @@ class CORE_EXPORT QgsVectorLayerElevationProperties : public QgsMapLayerElevatio
bool mEnableExtrusion = false;
double mExtrusionHeight = 0;
bool mEnableCustomTolerance = false;
double mCustomTolerance = 0;
std::unique_ptr< QgsLineSymbol > mProfileLineSymbol;
std::unique_ptr< QgsFillSymbol > mProfileFillSymbol;

View File

@ -916,6 +916,10 @@ class TestQgsLayoutItemElevationProfile(QgisTestCase, LayoutItemTestCase):
f.setGeometry(QgsGeometry.fromWkt(line))
self.assertTrue(vl.dataProvider().addFeature(f))
tolerance = 1
vl.elevationProperties().setCustomToleranceEnabled(True)
vl.elevationProperties().setCustomTolerance(tolerance)
vl.elevationProperties().setClamping(Qgis.AltitudeClamping.Absolute)
line_symbol = QgsLineSymbol.createSimple({"color": "#ff00ff", "width": "0.8"})
line_symbol.setWidthUnit(Qgis.RenderUnit.MapUnits)
@ -977,7 +981,7 @@ class TestQgsLayoutItemElevationProfile(QgisTestCase, LayoutItemTestCase):
)
)
profile_item.setTolerance(1)
profile_item.setTolerance(tolerance)
profile_item.setLayers([vl])
self.assertTrue(

View File

@ -46,6 +46,8 @@ class TestQgsVectorLayerElevationProperties(QgisTestCase):
self.assertEqual(props.type(), Qgis.VectorProfileType.IndividualFeatures)
self.assertEqual(props.profileSymbology(), Qgis.ProfileSurfaceSymbology.Line)
self.assertFalse(props.showMarkerSymbolInSurfacePlots())
self.assertFalse(props.customToleranceEnabled())
self.assertEqual(props.customTolerance(), 0)
props.setZOffset(0.5)
props.setZScale(2)
@ -135,6 +137,8 @@ class TestQgsVectorLayerElevationProperties(QgisTestCase):
)
self.assertTrue(props2.showMarkerSymbolInSurfacePlots())
self.assertEqual(props2.elevationLimit(), 909)
self.assertFalse(props2.customToleranceEnabled())
self.assertEqual(props2.customTolerance(), 0)
self.assertEqual(props2.profileLineSymbol().color().name(), "#ff4433")
self.assertEqual(props2.profileFillSymbol().color().name(), "#ff4455")
@ -161,6 +165,8 @@ class TestQgsVectorLayerElevationProperties(QgisTestCase):
)
self.assertTrue(props_clone.showMarkerSymbolInSurfacePlots())
self.assertEqual(props2.elevationLimit(), 909)
self.assertFalse(props_clone.customToleranceEnabled())
self.assertEqual(props_clone.customTolerance(), 0)
self.assertEqual(props_clone.profileLineSymbol().color().name(), "#ff4433")
self.assertEqual(props_clone.profileFillSymbol().color().name(), "#ff4455")
@ -210,6 +216,38 @@ class TestQgsVectorLayerElevationProperties(QgisTestCase):
props.setClamping(Qgis.AltitudeClamping.Absolute)
self.assertTrue(props.showByDefaultInElevationProfilePlots())
def test_defaults_from_layer(self):
props = QgsVectorLayerElevationProperties(None)
self.assertFalse(props.customToleranceEnabled())
self.assertEqual(props.customTolerance(), 0)
point_layer = QgsVectorLayer("Point", "my layer point", "memory")
self.assertTrue(point_layer.isValid())
props = QgsVectorLayerElevationProperties(None)
self.assertFalse(props.customToleranceEnabled())
self.assertEqual(props.customTolerance(), 0)
props.setDefaultsFromLayer(point_layer)
self.assertFalse(props.customToleranceEnabled())
self.assertEqual(props.customTolerance(), 0)
line_layer = QgsVectorLayer("LineStringZ", "my layer point", "memory")
self.assertTrue(line_layer.isValid())
props = QgsVectorLayerElevationProperties(None)
self.assertFalse(props.customToleranceEnabled())
self.assertEqual(props.customTolerance(), 0)
props.setDefaultsFromLayer(line_layer)
self.assertTrue(props.customToleranceEnabled())
self.assertEqual(props.customTolerance(), 0)
polygon_layer = QgsVectorLayer("Polygon", "Polys", "memory")
self.assertTrue(polygon_layer.isValid())
props = QgsVectorLayerElevationProperties(None)
self.assertFalse(props.customToleranceEnabled())
self.assertEqual(props.customTolerance(), 0)
props.setDefaultsFromLayer(polygon_layer)
self.assertTrue(props.customToleranceEnabled())
self.assertEqual(props.customTolerance(), 0)
if __name__ == "__main__":
unittest.main()

View File

@ -691,7 +691,9 @@ class TestQgsVectorLayerProfileGenerator(QgisTestCase):
req.setCrs(QgsCoordinateReferenceSystem("EPSG:3857"))
# very small tolerance
req.setTolerance(0.1)
tolerance = 0.1
vl.elevationProperties().setCustomTolerance(tolerance)
req.setTolerance(tolerance)
generator = vl.createProfileGenerator(req)
self.assertTrue(generator.generateProfile())
results = generator.takeResults()
@ -739,7 +741,9 @@ class TestQgsVectorLayerProfileGenerator(QgisTestCase):
self.assertAlmostEqual(results.zRange().upper(), 62.7499, 2)
# 1 meter tolerance
req.setTolerance(1)
tolerance = 1
vl.elevationProperties().setCustomTolerance(tolerance)
req.setTolerance(tolerance)
generator = vl.createProfileGenerator(req)
self.assertTrue(generator.generateProfile())
results = generator.takeResults()
@ -787,7 +791,9 @@ class TestQgsVectorLayerProfileGenerator(QgisTestCase):
self.assertAlmostEqual(results.zRange().upper(), 62.7499, 2)
# 15 meters tolerance
req.setTolerance(15)
tolerance = 15
vl.elevationProperties().setCustomTolerance(tolerance)
req.setTolerance(tolerance)
generator = vl.createProfileGenerator(req)
self.assertTrue(generator.generateProfile())
results = generator.takeResults()
@ -1391,11 +1397,14 @@ class TestQgsVectorLayerProfileGenerator(QgisTestCase):
f.setGeometry(QgsGeometry.fromWkt(line))
self.assertTrue(vl.dataProvider().addFeature(f))
tolerance = 2.0
vl.elevationProperties().setClamping(Qgis.AltitudeClamping.Relative)
vl.elevationProperties().setZScale(2.5)
vl.elevationProperties().setZOffset(10)
vl.elevationProperties().setExtrusionEnabled(True)
vl.elevationProperties().setExtrusionHeight(7)
vl.elevationProperties().setCustomTolerance(tolerance)
curve = QgsLineString()
curve.fromWkt(
@ -1413,7 +1422,7 @@ class TestQgsVectorLayerProfileGenerator(QgisTestCase):
req.setTerrainProvider(terrain_provider)
req.setCrs(QgsCoordinateReferenceSystem("EPSG:3857"))
req.setTolerance(2.0)
req.setTolerance(tolerance)
generator = vl.createProfileGenerator(req)
self.assertTrue(generator.generateProfile())
@ -2784,6 +2793,7 @@ class TestQgsVectorLayerProfileGenerator(QgisTestCase):
f.setGeometry(QgsGeometry.fromWkt(line))
self.assertTrue(vl.dataProvider().addFeature(f))
tolerance = 20
vl.elevationProperties().setClamping(Qgis.AltitudeClamping.Absolute)
vl.elevationProperties().setType(Qgis.VectorProfileType.ContinuousSurface)
vl.elevationProperties().setProfileSymbology(
@ -2797,6 +2807,7 @@ class TestQgsVectorLayerProfileGenerator(QgisTestCase):
vl.elevationProperties().setProfileFillSymbol(fill_symbol)
line_symbol = QgsLineSymbol.createSimple({"color": "#ff00ff", "width": "0.8"})
vl.elevationProperties().setProfileLineSymbol(line_symbol)
vl.elevationProperties().setCustomTolerance(tolerance)
curve = QgsLineString()
curve.fromWkt(
@ -2806,7 +2817,7 @@ class TestQgsVectorLayerProfileGenerator(QgisTestCase):
req.setTransformContext(self.create_transform_context())
req.setCrs(QgsCoordinateReferenceSystem())
req.setTolerance(20)
req.setTolerance(tolerance)
plot_renderer = QgsProfilePlotRenderer([vl], req)
plot_renderer.startGeneration()
@ -2881,11 +2892,13 @@ class TestQgsVectorLayerProfileGenerator(QgisTestCase):
f.setGeometry(QgsGeometry.fromWkt(line))
self.assertTrue(vl.dataProvider().addFeature(f))
tolerance = 10
vl.elevationProperties().setClamping(Qgis.AltitudeClamping.Absolute)
vl.elevationProperties().setRespectLayerSymbology(False)
line_symbol = QgsLineSymbol.createSimple({"color": "#ff00ff", "width": "0.8"})
line_symbol.setWidthUnit(Qgis.RenderUnit.MapUnits)
vl.elevationProperties().setProfileLineSymbol(line_symbol)
vl.elevationProperties().setCustomTolerance(tolerance)
curve = QgsLineString()
curve.fromWkt(
@ -2895,7 +2908,7 @@ class TestQgsVectorLayerProfileGenerator(QgisTestCase):
req.setTransformContext(self.create_transform_context())
req.setCrs(QgsCoordinateReferenceSystem())
req.setTolerance(10)
req.setTolerance(tolerance)
plot_renderer = QgsProfilePlotRenderer([vl], req)
plot_renderer.startGeneration()
@ -2998,6 +3011,8 @@ class TestQgsVectorLayerProfileGenerator(QgisTestCase):
expectedFeatures: list[list[int]],
):
request.setTolerance(tolerance)
if tolerance > 0 and layer.geometryType() != Qgis.GeometryType.Point:
layer.elevationProperties().setCustomTolerance(tolerance)
profGen = layer.createProfileGenerator(request)
self.assertIsNotNone(profGen)
@ -3284,6 +3299,9 @@ class TestQgsVectorLayerProfileGenerator(QgisTestCase):
vl.elevationProperties().setClamping(Qgis.AltitudeClamping.Absolute)
vl.elevationProperties().setExtrusionEnabled(False)
tolerance = 10
vl.elevationProperties().setCustomTolerance(tolerance)
wkt_str = "POLYHEDRALSURFACE Z(((321474.91 129812.38 -20.00,322277.09 130348.29 -20.00,322631.00 129738.23 -20.00,321434.46 129266.36 -20.00,321474.91 129812.38 -20.00)),((321474.91 129812.38 30.00,321434.46 129266.36 30.00,322631.00 129738.23 30.00,322277.09 130348.29 30.00,321474.91 129812.38 30.00)),((321474.91 129812.38 -20.00,321474.91 129812.38 30.00,322277.09 130348.29 30.00,322277.09 130348.29 -20.00,321474.91 129812.38 -20.00)),((322277.09 130348.29 -20.00,322277.09 130348.29 30.00,322631.00 129738.23 30.00,322631.00 129738.23 -20.00,322277.09 130348.29 -20.00)),((322631.00 129738.23 -20.00,322631.00 129738.23 30.00,321434.46 129266.36 30.00,321434.46 129266.36 -20.00,322631.00 129738.23 -20.00)),((321434.46 129266.36 -20.00,321434.46 129266.36 30.00,321474.91 129812.38 30.00,321474.91 129812.38 -20.00,321434.46 129266.36 -20.00)))"
vl_feature = QgsFeature()
vl_feature.setGeometry(QgsGeometry.fromWkt(wkt_str))
@ -3297,7 +3315,7 @@ class TestQgsVectorLayerProfileGenerator(QgisTestCase):
req = QgsProfileRequest(curve)
req.setCrs(QgsCoordinateReferenceSystem("EPSG:3857"))
req.setTolerance(10)
req.setTolerance(tolerance)
generator = vl.createProfileGenerator(req)
self.assertTrue(generator.generateProfile())
@ -3340,6 +3358,9 @@ class TestQgsVectorLayerProfileGenerator(QgisTestCase):
vl.elevationProperties().setClamping(Qgis.AltitudeClamping.Absolute)
vl.elevationProperties().setExtrusionEnabled(False)
tolerance = 10
vl.elevationProperties().setCustomTolerance(tolerance)
vl_feature = QgsFeature()
vl_feature.setGeometry(
QgsGeometry.fromWkt(
@ -3356,7 +3377,7 @@ class TestQgsVectorLayerProfileGenerator(QgisTestCase):
req = QgsProfileRequest(curve)
req.setCrs(QgsCoordinateReferenceSystem("EPSG:3857"))
req.setTolerance(10)
req.setTolerance(tolerance)
generator = vl.createProfileGenerator(req)
self.assertTrue(generator.generateProfile())