Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix QgsCsException messages (LTR backport) #59543

Merged
merged 8 commits into from
Nov 21, 2024
4 changes: 4 additions & 0 deletions src/core/proj/qgscoordinatereferencesystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1627,7 +1627,11 @@ void QgsCoordinateReferenceSystem::setProjString( const QString &proj4String )
{
#ifdef QGISDEBUG
const int errNo = proj_context_errno( ctx );
#if PROJ_VERSION_MAJOR>=8
QgsDebugError( QStringLiteral( "proj string rejected: %1" ).arg( proj_context_errno_string( ctx, errNo ) ) );
#else
QgsDebugError( QStringLiteral( "proj string rejected: %1" ).arg( proj_errno_string( errNo ) ) );
#endif
#endif
d->mIsValid = false;
}
Expand Down
30 changes: 20 additions & 10 deletions src/core/proj/qgscoordinatetransform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,7 @@ void QgsCoordinateTransform::transformCoords( int numPoints, double *x, double *
}

mFallbackOperationOccurred = false;
bool errorOccurredDuringFallbackOperation = false;
if ( actualRes != 0
&& ( d->mAvailableOpCount > 1 || d->mAvailableOpCount == -1 ) // only use fallbacks if more than one operation is possible -- otherwise we've already tried it and it failed
&& ( d->mAllowFallbackTransforms || mBallparkTransformsAreAppropriate ) )
Expand All @@ -842,13 +843,14 @@ void QgsCoordinateTransform::transformCoords( int numPoints, double *x, double *
// So here just check proj_errno() for single point transform
if ( numPoints == 1 )
{
projResult = proj_errno( transform );
// hmm - something very odd here. We can't trust proj_errno( transform ), as that's giving us incorrect error numbers
// (such as "failed to load datum shift file", which is definitely incorrect for a default proj created operation!)
// so we resort to testing values ourselves...
projResult = std::isinf( xprev[0] ) || std::isinf( yprev[0] ) || std::isinf( zprev[0] ) ? 1 : 0;
errorOccurredDuringFallbackOperation = std::isinf( xprev[0] ) || std::isinf( yprev[0] ) || std::isinf( zprev[0] );
}

if ( projResult == 0 )
if ( !errorOccurredDuringFallbackOperation )
{
memcpy( x, xprev.data(), sizeof( double ) * numPoints );
memcpy( y, yprev.data(), sizeof( double ) * numPoints );
Expand All @@ -873,25 +875,33 @@ void QgsCoordinateTransform::transformCoords( int numPoints, double *x, double *
z[pos] = std::numeric_limits<double>::quiet_NaN();
}

if ( projResult != 0 )
if ( projResult != 0 || errorOccurredDuringFallbackOperation )
{
//something bad happened....
QString points;

const QChar delim = numPoints > 1 ? '\n' : ' ';
for ( int i = 0; i < numPoints; ++i )
{
points += QStringLiteral( "(%1, %2)\n" ).arg( x[i], 0, 'f' ).arg( y[i], 0, 'f' );
points += QStringLiteral( "(%1, %2)" ).arg( xprev[i], 0, 'f' ).arg( yprev[i], 0, 'f' ) + delim;
}

const QString dir = ( direction == Qgis::TransformDirection::Forward ) ? QObject::tr( "forward transform" ) : QObject::tr( "inverse transform" );
const QString dir = ( direction == Qgis::TransformDirection::Forward ) ? QObject::tr( "Forward transform" ) : QObject::tr( "Inverse transform" );

const QString msg = QObject::tr( "%1 of\n"
"%2"
"Error: %3" )
#if PROJ_VERSION_MAJOR>=8
PJ_CONTEXT *projContext = QgsProjContext::get();
const QString projError = !errorOccurredDuringFallbackOperation ? QString::fromUtf8( proj_context_errno_string( projContext, projResult ) ) : QObject::tr( "Fallback transform failed" );
#else
const QString projError = !errorOccurredDuringFallbackOperation ? QString::fromUtf8( proj_errno_string( projResult ) ) : QObject::tr( "Fallback transform failed" );
#endif

const QString msg = QObject::tr( "%1 (%2 to %3) of%4%5Error: %6" )
.arg( dir,
( direction == Qgis::TransformDirection::Forward ) ? d->mSourceCRS.authid() : d->mDestCRS.authid(),
( direction == Qgis::TransformDirection::Forward ) ? d->mDestCRS.authid() : d->mSourceCRS.authid(),
QString( delim ),
points,
projResult < 0 ? QString::fromUtf8( proj_errno_string( projResult ) ) : QObject::tr( "Fallback transform failed" ) );

projError );

// don't flood console with thousands of duplicate transform error messages
if ( msg != mLastError )
Expand Down
13 changes: 11 additions & 2 deletions src/core/proj/qgscoordinatetransform_p.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,12 +329,17 @@ ProjData QgsCoordinateTransformPrivate::threadLocalProjData()
{
// huh?
const int errNo = proj_context_errno( context );
if ( errNo && errNo != -61 )
if ( errNo )
{
#if PROJ_VERSION_MAJOR>=8
nonAvailableError = QString( proj_context_errno_string( context, errNo ) );
#else
nonAvailableError = QString( proj_errno_string( errNo ) );
#endif
}
else
{
// in theory should never be hit!
nonAvailableError = QObject::tr( "No coordinate operations are available between these two reference systems" );
}
}
Expand Down Expand Up @@ -463,9 +468,13 @@ ProjData QgsCoordinateTransformPrivate::threadLocalProjData()
{
const int errNo = proj_context_errno( context );
const QStringList projErrors = errorLogger.errors();
if ( errNo && errNo != -61 )
if ( errNo )
{
#if PROJ_VERSION_MAJOR>=8
nonAvailableError = QString( proj_context_errno_string( context, errNo ) );
#else
nonAvailableError = QString( proj_errno_string( errNo ) );
#endif
}
else if ( !projErrors.empty() )
{
Expand Down
19 changes: 19 additions & 0 deletions tests/src/python/test_qgscoordinatetransform.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@
import qgis # NOQA

from qgis.core import (
Qgis,
QgsCoordinateReferenceSystem,
QgsCoordinateTransform,
QgsCoordinateTransformContext,
QgsProject,
QgsRectangle,
QgsPointXY,
QgsCsException
)
import unittest
from qgis.testing import start_app, QgisTestCase
Expand Down Expand Up @@ -162,6 +165,22 @@ def testTransformBoundingBoxFullWorldToWebMercator(self):
self.assertAlmostEqual(transformedExtent.xMaximum(), 20037508.343, delta=1e-3)
self.assertAlmostEqual(transformedExtent.yMaximum(), 44927335.427, delta=1e-3)

def test_cs_exception(self):
ct = QgsCoordinateTransform(QgsCoordinateReferenceSystem('EPSG:4326'),
QgsCoordinateReferenceSystem('EPSG:3857'), QgsProject.instance())
point = QgsPointXY(-7603859, -7324441)
with self.assertRaises(QgsCsException) as e:
ct.transform(point)
self.assertEqual(str(e.exception), 'Forward transform (EPSG:4326 to EPSG:3857) of (-7603859.000000, -7324441.000000) Error: Invalid coordinate')

# reverse transform
ct = QgsCoordinateTransform(QgsCoordinateReferenceSystem('EPSG:3857'),
QgsCoordinateReferenceSystem('EPSG:4326'), QgsProject.instance())
point = QgsPointXY(-7603859, -7324441)
with self.assertRaises(QgsCsException) as e:
ct.transform(point, Qgis.TransformDirection.Reverse)
self.assertEqual(str(e.exception), 'Inverse transform (EPSG:4326 to EPSG:3857) of (-7603859.000000, -7324441.000000) Error: Invalid coordinate')


if __name__ == '__main__':
unittest.main()
Loading