[api] Set specific distance:elevation ratio for elevation profile canvas

This commit is contained in:
Nyall Dawson 2025-08-04 11:17:18 +10:00
parent 19d87693db
commit d919d8f776
No known key found for this signature in database
GPG Key ID: AEA7D60D3DF12896
5 changed files with 164 additions and 3 deletions

View File

@ -235,6 +235,35 @@ Sets whether the distance and elevation scales are locked to each other.
.. seealso:: :py:func:`lockAxisScales`
.. versionadded:: 3.32
%End
double axisScaleRatio() const;
%Docstring
Returns the current ratio of horizontal (distance) to vertical
(elevation) scale for the plot.
.. seealso:: :py:func:`setAxisScaleRatio`
.. versionadded:: 4.0
%End
void setAxisScaleRatio( double scale );
%Docstring
Sets the ratio of horizontal (distance) to vertical (elevation) scale
for the plot.
E.g. a ``scale`` of 3 indicates a ratio of 3:1 for distance vs
elevation, whereas a scale of 0.3333 indicates a ratio of 1:3 for
distance vs elevation.
This will immediately update the visible plot area to match the
specified scale.
.. seealso:: :py:func:`axisScaleRatio`
.. seealso:: :py:func:`setLockAxisScales`
.. versionadded:: 4.0
%End
Qgis::DistanceUnit distanceUnit() const;

View File

@ -235,6 +235,35 @@ Sets whether the distance and elevation scales are locked to each other.
.. seealso:: :py:func:`lockAxisScales`
.. versionadded:: 3.32
%End
double axisScaleRatio() const;
%Docstring
Returns the current ratio of horizontal (distance) to vertical
(elevation) scale for the plot.
.. seealso:: :py:func:`setAxisScaleRatio`
.. versionadded:: 4.0
%End
void setAxisScaleRatio( double scale );
%Docstring
Sets the ratio of horizontal (distance) to vertical (elevation) scale
for the plot.
E.g. a ``scale`` of 3 indicates a ratio of 3:1 for distance vs
elevation, whereas a scale of 0.3333 indicates a ratio of 1:3 for
distance vs elevation.
This will immediately update the visible plot area to match the
specified scale.
.. seealso:: :py:func:`axisScaleRatio`
.. seealso:: :py:func:`setLockAxisScales`
.. versionadded:: 4.0
%End
Qgis::DistanceUnit distanceUnit() const;

View File

@ -605,16 +605,19 @@ void QgsElevationProfileCanvas::adjustRangeForAxisScaleLock( double &xMinimum, d
// ensures that we always "zoom out" to match horizontal/vertical scales
const double horizontalScale = ( xMaximum - xMinimum ) / mPlotItem->plotArea().width();
const double verticalScale = ( yMaximum - yMinimum ) / mPlotItem->plotArea().height();
if ( horizontalScale > verticalScale )
const double currentRatio = horizontalScale / verticalScale;
if ( currentRatio <= mLockedAxisScale )
{
const double height = horizontalScale * mPlotItem->plotArea().height();
const double height = horizontalScale * mPlotItem->plotArea().height() / mLockedAxisScale;
const double deltaHeight = ( yMaximum - yMinimum ) - height;
yMinimum += deltaHeight / 2;
yMaximum -= deltaHeight / 2;
}
else
{
const double width = verticalScale * mPlotItem->plotArea().width();
const double width = verticalScale * mPlotItem->plotArea().width() * mLockedAxisScale;
const double deltaWidth = ( ( xMaximum - xMinimum ) - width );
xMinimum += deltaWidth / 2;
xMaximum -= deltaWidth / 2;
@ -700,6 +703,32 @@ void QgsElevationProfileCanvas::setLockAxisScales( bool lock )
}
}
double QgsElevationProfileCanvas::axisScaleRatio() const
{
const double horizontalScale = ( mPlotItem->xMaximum() - mPlotItem->xMinimum() ) * mPlotItem->mXScaleFactor / mPlotItem->plotArea().width();
const double verticalScale = ( mPlotItem->yMaximum() - mPlotItem->yMinimum() ) / mPlotItem->plotArea().height();
return horizontalScale / verticalScale;
}
void QgsElevationProfileCanvas::setAxisScaleRatio( double scale )
{
mLockedAxisScale = scale;
double xMinimum = mPlotItem->xMinimum() * mPlotItem->mXScaleFactor;
double xMaximum = mPlotItem->xMaximum() * mPlotItem->mXScaleFactor;
double yMinimum = mPlotItem->yMinimum();
double yMaximum = mPlotItem->yMaximum();
adjustRangeForAxisScaleLock( xMinimum, xMaximum, yMinimum, yMaximum );
mPlotItem->setXMinimum( xMinimum / mPlotItem->mXScaleFactor );
mPlotItem->setXMaximum( xMaximum / mPlotItem->mXScaleFactor );
mPlotItem->setYMinimum( yMinimum );
mPlotItem->setYMaximum( yMaximum );
refineResults();
mPlotItem->updatePlot();
emit plotAreaChanged();
}
QgsPointXY QgsElevationProfileCanvas::snapToPlot( QPoint point )
{
if ( !mCurrentJob || !mSnappingEnabled )

View File

@ -239,6 +239,29 @@ class GUI_EXPORT QgsElevationProfileCanvas : public QgsPlotCanvas
*/
void setLockAxisScales( bool lock );
/**
* Returns the current ratio of horizontal (distance) to vertical (elevation) scale
* for the plot.
*
* \see setAxisScaleRatio()
* \since QGIS 4.0
*/
double axisScaleRatio() const;
/**
* Sets the ratio of horizontal (distance) to vertical (elevation) scale for the plot.
*
* E.g. a \a scale of 3 indicates a ratio of 3:1 for distance vs elevation, whereas a scale
* of 0.3333 indicates a ratio of 1:3 for distance vs elevation.
*
* This will immediately update the visible plot area to match the specified scale.
*
* \see axisScaleRatio()
* \see setLockAxisScales()
* \since QGIS 4.0
*/
void setAxisScaleRatio( double scale );
/**
* Returns the distance unit used by the canvas.
*
@ -340,6 +363,7 @@ class GUI_EXPORT QgsElevationProfileCanvas : public QgsPlotCanvas
QgsScreenHelper *mScreenHelper = nullptr;
bool mLockAxisScales = false;
double mLockedAxisScale = 1;
QgsCoordinateReferenceSystem mCrs;
QgsProject *mProject = nullptr;

View File

@ -225,6 +225,56 @@ class TestQgsElevationProfileCanvas(QgisTestCase):
canvas.wheelEvent(wheel_event)
self.assertEqual(tool.events[-1].type(), QEvent.Type.Wheel)
def test_ratio(self):
"""
Test axis scale ratio logic
"""
canvas = QgsElevationProfileCanvas()
canvas.setCrs(QgsCoordinateReferenceSystem("EPSG:4326"))
canvas.setFrameStyle(0)
# make a 2:1 canvas, to make the maths easier!
canvas.resize(800, 400)
canvas.setProject(QgsProject.instance())
canvas.show()
self.assertEqual(canvas.width(), 800)
self.assertEqual(canvas.height(), 400)
canvas.setVisiblePlotRange(100, 200, 50, 150)
# showing 100m distance, 100m elevation
# distance:elevation ratio is 1:2, as canvas is twice as wide as high
self.assertAlmostEqual(canvas.axisScaleRatio(), 0.5, 1)
canvas.setVisiblePlotRange(100, 300, 50, 150)
# showing 200m distance, 100m elevation
# distance:elevation ratio is 1:1, as canvas is twice as wide as high
self.assertAlmostEqual(canvas.axisScaleRatio(), 1.0, 1)
canvas.setVisiblePlotRange(100, 500, 50, 150)
# showing 400m distance, 100m elevation
# distance:elevation ratio is 2:1, as canvas is twice as wide as high
self.assertAlmostEqual(canvas.axisScaleRatio(), 2.0, delta=0.1)
canvas.setAxisScaleRatio(0.5)
self.assertAlmostEqual(canvas.axisScaleRatio(), 0.5, 1)
self.assertAlmostEqual(canvas.visibleDistanceRange().lower(), 248.024, 1)
self.assertAlmostEqual(canvas.visibleDistanceRange().upper(), 351.976, 1)
self.assertAlmostEqual(canvas.visibleElevationRange().lower(), 50.0, 1)
self.assertAlmostEqual(canvas.visibleElevationRange().upper(), 150.0, 1)
canvas.setAxisScaleRatio(1.0)
self.assertAlmostEqual(canvas.axisScaleRatio(), 1.0, 1)
self.assertAlmostEqual(canvas.visibleDistanceRange().lower(), 248.024, 1)
self.assertAlmostEqual(canvas.visibleDistanceRange().upper(), 351.976, 1)
self.assertAlmostEqual(canvas.visibleElevationRange().lower(), 75.0, 1)
self.assertAlmostEqual(canvas.visibleElevationRange().upper(), 125.0, 1)
canvas.setAxisScaleRatio(2.0)
self.assertAlmostEqual(canvas.axisScaleRatio(), 2.0, 1)
self.assertAlmostEqual(canvas.visibleDistanceRange().lower(), 248.024, 1)
self.assertAlmostEqual(canvas.visibleDistanceRange().upper(), 351.976, 1)
self.assertAlmostEqual(canvas.visibleElevationRange().lower(), 87.5, 1)
self.assertAlmostEqual(canvas.visibleElevationRange().upper(), 112.5, 1)
if __name__ == "__main__":
unittest.main()