Skip to content

Commit

Permalink
Add validation to calls to QgsProject.setVerticalCrs
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Mar 11, 2024
1 parent ab164cf commit 187e9b9
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 9 deletions.
9 changes: 8 additions & 1 deletion python/PyQt6/core/auto_generated/project/qgsproject.sip.in
Expand Up @@ -360,7 +360,7 @@ The returned CRS will be invalid if the project has no vertical CRS.
.. versionadded:: 3.38
%End

void setVerticalCrs( const QgsCoordinateReferenceSystem &crs );
bool setVerticalCrs( const QgsCoordinateReferenceSystem &crs, QString *errorMessage /Out/ = 0 );
%Docstring
Sets the project's vertical coordinate reference system.

Expand All @@ -370,6 +370,13 @@ Sets the project's vertical coordinate reference system.
:py:func:`~QgsProject.verticalCrs` will be the vertical component of :py:func:`~QgsProject.crs`. Otherwise it will be the value
explicitly set by this call.

:param crs: the vertical CRS


:return: - ``True`` if vertical CRS was successfully set
- errorMessage: will be set to a descriptive message if the vertical CRS could not be set


.. seealso:: :py:func:`verticalCrs`

.. seealso:: :py:func:`setCrs`
Expand Down
9 changes: 8 additions & 1 deletion python/core/auto_generated/project/qgsproject.sip.in
Expand Up @@ -360,7 +360,7 @@ The returned CRS will be invalid if the project has no vertical CRS.
.. versionadded:: 3.38
%End

void setVerticalCrs( const QgsCoordinateReferenceSystem &crs );
bool setVerticalCrs( const QgsCoordinateReferenceSystem &crs, QString *errorMessage /Out/ = 0 );
%Docstring
Sets the project's vertical coordinate reference system.

Expand All @@ -370,6 +370,13 @@ Sets the project's vertical coordinate reference system.
:py:func:`~QgsProject.verticalCrs` will be the vertical component of :py:func:`~QgsProject.crs`. Otherwise it will be the value
explicitly set by this call.

:param crs: the vertical CRS


:return: - ``True`` if vertical CRS was successfully set
- errorMessage: will be set to a descriptive message if the vertical CRS could not be set


.. seealso:: :py:func:`verticalCrs`

.. seealso:: :py:func:`setCrs`
Expand Down
55 changes: 54 additions & 1 deletion src/core/project/qgsproject.cpp
Expand Up @@ -1021,14 +1021,66 @@ QgsCoordinateReferenceSystem QgsProject::verticalCrs() const
return mVerticalCrs;
}

void QgsProject::setVerticalCrs( const QgsCoordinateReferenceSystem &crs )
bool QgsProject::setVerticalCrs( const QgsCoordinateReferenceSystem &crs, QString *errorMessage )
{
QGIS_PROTECT_QOBJECT_THREAD_ACCESS

if ( crs.isValid() )
{
// validate that passed crs is a vertical crs
switch ( crs.type() )
{
case Qgis::CrsType::Vertical:
break;

case Qgis::CrsType::Unknown:
case Qgis::CrsType::Compound:
case Qgis::CrsType::Geodetic:
case Qgis::CrsType::Geocentric:
case Qgis::CrsType::Geographic2d:
case Qgis::CrsType::Geographic3d:
case Qgis::CrsType::Projected:
case Qgis::CrsType::Temporal:
case Qgis::CrsType::Engineering:
case Qgis::CrsType::Bound:
case Qgis::CrsType::Other:
case Qgis::CrsType::DerivedProjected:
if ( errorMessage )
*errorMessage = QObject::tr( "Specified CRS is a %1 CRS, not a Vertical CRS" ).arg( qgsEnumValueToKey( crs.type() ) );
return false;
}
}

if ( crs != mVerticalCrs )
{
const QgsCoordinateReferenceSystem oldVerticalCrs = verticalCrs();

switch ( mCrs.type() )
{
case Qgis::CrsType::Compound:
if ( crs != oldVerticalCrs )
{
if ( errorMessage )
*errorMessage = QObject::tr( "Project CRS is a Compound CRS, specified Vertical CRS will be ignored" );
return false;
}
break;

case Qgis::CrsType::Unknown:
case Qgis::CrsType::Geodetic:
case Qgis::CrsType::Geocentric:
case Qgis::CrsType::Geographic2d:
case Qgis::CrsType::Geographic3d:
case Qgis::CrsType::Projected:
case Qgis::CrsType::Temporal:
case Qgis::CrsType::Engineering:
case Qgis::CrsType::Bound:
case Qgis::CrsType::Other:
case Qgis::CrsType::Vertical:
case Qgis::CrsType::DerivedProjected:
break;
}

mVerticalCrs = crs;
mProjectScope.reset();

Expand All @@ -1038,6 +1090,7 @@ void QgsProject::setVerticalCrs( const QgsCoordinateReferenceSystem &crs )
if ( verticalCrs() != oldVerticalCrs )
emit verticalCrsChanged();
}
return true;
}

QgsCoordinateTransformContext QgsProject::transformContext() const
Expand Down
7 changes: 6 additions & 1 deletion src/core/project/qgsproject.h
Expand Up @@ -423,12 +423,17 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
* verticalCrs() will be the vertical component of crs(). Otherwise it will be the value
* explicitly set by this call.
*
* \param crs the vertical CRS
* \param errorMessage will be set to a descriptive message if the vertical CRS could not be set
*
* \returns TRUE if vertical CRS was successfully set
*
* \see verticalCrs()
* \see setCrs()
*
* \since QGIS 3.38
*/
void setVerticalCrs( const QgsCoordinateReferenceSystem &crs );
bool setVerticalCrs( const QgsCoordinateReferenceSystem &crs, QString *errorMessage SIP_OUT = nullptr );

/**
* Returns a copy of the project's coordinate transform context, which stores various
Expand Down
37 changes: 32 additions & 5 deletions tests/src/python/test_qgsproject.py
Expand Up @@ -164,11 +164,20 @@ def test_vertical_crs(self):
self.assertFalse(project.verticalCrs().isValid())

spy = QSignalSpy(project.verticalCrsChanged)
project.setVerticalCrs(QgsCoordinateReferenceSystem('EPSG:5703'))
# not a vertical crs
ok, err = project.setVerticalCrs(
QgsCoordinateReferenceSystem('EPSG:3111'))
self.assertFalse(ok)
self.assertEqual(err, 'Specified CRS is a Projected CRS, not a Vertical CRS')
self.assertFalse(project.verticalCrs().isValid())

ok, err = project.setVerticalCrs(QgsCoordinateReferenceSystem('EPSG:5703'))
self.assertTrue(ok)
self.assertEqual(project.verticalCrs().authid(), 'EPSG:5703')
self.assertEqual(len(spy), 1)
# try overwriting with same crs, should be no new signal
project.setVerticalCrs(QgsCoordinateReferenceSystem('EPSG:5703'))
ok, err = project.setVerticalCrs(QgsCoordinateReferenceSystem('EPSG:5703'))
self.assertTrue(ok)
self.assertEqual(len(spy), 1)

# check that project vertical crs variables are set in expression context
Expand Down Expand Up @@ -198,6 +207,16 @@ def test_vertical_crs(self):
self.assertEqual(len(spy), 2)
self.assertFalse(project.verticalCrs().isValid())

# test resetting vertical crs back to not set
ok, err = project.setVerticalCrs(QgsCoordinateReferenceSystem('EPSG:5703'))
self.assertTrue(ok)
self.assertEqual(len(spy), 3)

ok, err = project.setVerticalCrs(QgsCoordinateReferenceSystem())
self.assertTrue(ok)
self.assertEqual(len(spy), 4)
self.assertFalse(project.verticalCrs().isValid())

def test_vertical_crs_with_compound_horizontal_crs(self):
"""
Test vertical crs logic when project has a compound crs set
Expand All @@ -220,15 +239,23 @@ def test_vertical_crs_with_compound_horizontal_crs(self):
# if we explicitly set a vertical crs now, it should be ignored
# because the main project crs is a compound crs and that takes
# precedence
project.setVerticalCrs(other_vert_crs)
ok, err = project.setVerticalCrs(other_vert_crs)
self.assertFalse(ok)
self.assertEqual(err, 'Project CRS is a Compound CRS, specified Vertical CRS will be ignored')
self.assertEqual(project.verticalCrs().authid(), 'EPSG:5703')
self.assertEqual(len(spy), 1)
# setting the vertical crs to the vertical component of the compound crs
# IS permitted, even though it effectively has no impact...
ok, err = project.setVerticalCrs(QgsCoordinateReferenceSystem('EPSG:5703'))
self.assertTrue(ok)
self.assertEqual(project.verticalCrs().authid(), 'EPSG:5703')
self.assertEqual(len(spy), 1)

# reset horizontal crs to a non-compound crs, now the manually
# specified vertical crs should take precedence
project.setCrs(QgsCoordinateReferenceSystem('EPSG:3111'))
self.assertEqual(project.verticalCrs().authid(), 'ESRI:115700')
self.assertEqual(len(spy), 2)
self.assertEqual(project.verticalCrs().authid(), 'EPSG:5703')
self.assertEqual(len(spy), 1)

def testEllipsoid(self):
prj = QgsProject.instance()
Expand Down

0 comments on commit 187e9b9

Please sign in to comment.