Skip to content

Commit

Permalink
Add API to QgsCoordinateTransform to control fallback availability
Browse files Browse the repository at this point in the history
on a transform-by-transform basis, populate correctly from context
  • Loading branch information
nyalldawson committed Feb 17, 2020
1 parent 61e6ab0 commit e3b1649
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 11 deletions.
50 changes: 50 additions & 0 deletions python/core/auto_generated/qgscoordinatetransform.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,40 @@ coordinates.
.. seealso:: :py:func:`coordinateOperation`

.. versionadded:: 3.8
%End

void setAllowFallbackTransforms( bool allowed );
%Docstring
Sets whether "ballpark" fallback transformations can be used in the case that the specified
coordinate operation fails (such as when coordinates from outside a required grid shift file
are transformed). See fallbackOperationOccurred() for further details.

.. note::

Requires Proj 6.0 or later. Builds based on earlier Proj versions will ignore this setting.

.. seealso:: :py:func:`allowFallbackTransforms`

.. seealso:: :py:func:`setBallparkTransformsAreAppropriate`

.. versionadded:: 3.12
%End

bool allowFallbackTransforms() const;
%Docstring
Returns whether "ballpark" fallback transformations will be used in the case that the specified
coordinate operation fails (such as when coordinates from outside a required grid shift file
are transformed). See fallbackOperationOccurred() for further details.

.. note::

Requires Proj 6.0 or later. Builds based on earlier Proj versions will ignore this setting.

.. seealso:: :py:func:`setAllowFallbackTransforms`

.. seealso:: :py:func:`setBallparkTransformsAreAppropriate`

.. versionadded:: 3.12
%End

void setBallparkTransformsAreAppropriate( bool appropriate );
Expand All @@ -374,11 +408,18 @@ If ``appropriate`` is ``False`` (the default behavior), then transforms MAY STIL
when the preferred or user-specified operation fails, however whenever this occurs
a user-visible warning will be generated.

If allowFallbackTransforms() is ``False`` then this setting has no effect.

.. warning::

This setting applies to a single instance of a coordinate transform only,
and is not copied when a coordinate transform object is copied or assigned.

.. note::

Requires Proj 6.0 or later. Builds based on earlier Proj versions will ignore this setting.


.. versionadded:: 3.12
%End

Expand All @@ -394,6 +435,11 @@ operation occurred by testing fallbackOperationOccurred() immediately after a tr
This setting applies to a single instance of a coordinate transform only,
and is not copied when a coordinate transform object is copied or assigned.

.. note::

Requires Proj 6.0 or later. Builds based on earlier Proj versions will never perform fallback operations.


.. seealso:: :py:func:`fallbackOperationOccurred`

.. versionadded:: 3.12
Expand All @@ -403,6 +449,10 @@ operation occurred by testing fallbackOperationOccurred() immediately after a tr
%Docstring
Returns ``True`` if a fallback operation occurred for the most recent transform.

.. note::

Requires Proj 6.0 or later. Builds based on earlier Proj versions will never perform fallback operations.

.. seealso:: :py:func:`disableFallbackOperationHandler`

.. versionadded:: 3.12
Expand Down
29 changes: 20 additions & 9 deletions src/core/qgscoordinatetransform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ QgsCoordinateTransform::QgsCoordinateTransform( const QgsCoordinateReferenceSyst

Q_NOWARN_DEPRECATED_PUSH
#if PROJ_VERSION_MAJOR>=6
if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation ) )
if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
#else
if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
#endif
Expand All @@ -99,7 +99,7 @@ QgsCoordinateTransform::QgsCoordinateTransform( const QgsCoordinateReferenceSyst

Q_NOWARN_DEPRECATED_PUSH
#if PROJ_VERSION_MAJOR>=6
if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation ) )
if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
#else
if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
#endif
Expand All @@ -122,7 +122,7 @@ QgsCoordinateTransform::QgsCoordinateTransform( const QgsCoordinateReferenceSyst

Q_NOWARN_DEPRECATED_PUSH
#if PROJ_VERSION_MAJOR>=6
if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation ) )
if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
#else
if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
#endif
Expand Down Expand Up @@ -164,7 +164,7 @@ void QgsCoordinateTransform::setSourceCrs( const QgsCoordinateReferenceSystem &c
d->calculateTransforms( mContext );
Q_NOWARN_DEPRECATED_PUSH
#if PROJ_VERSION_MAJOR>=6
if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation ) )
if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
#else
if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
#endif
Expand All @@ -184,7 +184,7 @@ void QgsCoordinateTransform::setDestinationCrs( const QgsCoordinateReferenceSyst
d->calculateTransforms( mContext );
Q_NOWARN_DEPRECATED_PUSH
#if PROJ_VERSION_MAJOR>=6
if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation ) )
if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
#else
if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
#endif
Expand All @@ -208,7 +208,7 @@ void QgsCoordinateTransform::setContext( const QgsCoordinateTransformContext &co
d->calculateTransforms( mContext );
Q_NOWARN_DEPRECATED_PUSH
#if PROJ_VERSION_MAJOR>=6
if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation ) )
if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
#else
if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
#endif
Expand Down Expand Up @@ -743,7 +743,7 @@ void QgsCoordinateTransform::transformCoords( int numPoints, double *x, double *
#if PROJ_VERSION_MAJOR>=6

mFallbackOperationOccurred = false;
if ( actualRes != 0 )
if ( actualRes != 0 && d->mAllowFallbackTransforms )
{
// fail #1 -- try with getting proj to auto-pick an appropriate coordinate operation for the points
if ( PJ *transform = d->threadLocalFallbackProjData() )
Expand Down Expand Up @@ -901,6 +901,17 @@ void QgsCoordinateTransform::setCoordinateOperation( const QString &operation )
d->mShouldReverseCoordinateOperation = false;
}

void QgsCoordinateTransform::setAllowFallbackTransforms( bool allowed )
{
d.detach();
d->mAllowFallbackTransforms = allowed;
}

bool QgsCoordinateTransform::allowFallbackTransforms() const
{
return d->mAllowFallbackTransforms;
}

void QgsCoordinateTransform::setBallparkTransformsAreAppropriate( bool appropriate )
{
mBallparkTransformsAreAppropriate = appropriate;
Expand Down Expand Up @@ -929,7 +940,7 @@ const char *finder( const char *name )
}

#if PROJ_VERSION_MAJOR>=6
bool QgsCoordinateTransform::setFromCache( const QgsCoordinateReferenceSystem &src, const QgsCoordinateReferenceSystem &dest, const QString &coordinateOperationProj )
bool QgsCoordinateTransform::setFromCache( const QgsCoordinateReferenceSystem &src, const QgsCoordinateReferenceSystem &dest, const QString &coordinateOperationProj, bool allowFallback )
{
if ( !src.isValid() || !dest.isValid() )
return false;
Expand All @@ -949,7 +960,7 @@ bool QgsCoordinateTransform::setFromCache( const QgsCoordinateReferenceSystem &s
const QList< QgsCoordinateTransform > values = sTransforms.values( qMakePair( sourceKey, destKey ) );
for ( auto valIt = values.constBegin(); valIt != values.constEnd(); ++valIt )
{
if ( ( *valIt ).coordinateOperation() == coordinateOperationProj )
if ( ( *valIt ).coordinateOperation() == coordinateOperationProj && ( *valIt ).allowFallbackTransforms() == allowFallback )
{
// need to save, and then restore the context... we don't want this to be cached or to use the values from the cache
QgsCoordinateTransformContext context = mContext;
Expand Down
36 changes: 35 additions & 1 deletion src/core/qgscoordinatetransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,32 @@ class CORE_EXPORT QgsCoordinateTransform
*/
void setCoordinateOperation( const QString &operation ) const;

/**
* Sets whether "ballpark" fallback transformations can be used in the case that the specified
* coordinate operation fails (such as when coordinates from outside a required grid shift file
* are transformed). See fallbackOperationOccurred() for further details.
*
* \note Requires Proj 6.0 or later. Builds based on earlier Proj versions will ignore this setting.
*
* \see allowFallbackTransforms()
* \see setBallparkTransformsAreAppropriate()
* \since QGIS 3.12
*/
void setAllowFallbackTransforms( bool allowed );

/**
* Returns whether "ballpark" fallback transformations will be used in the case that the specified
* coordinate operation fails (such as when coordinates from outside a required grid shift file
* are transformed). See fallbackOperationOccurred() for further details.
*
* \note Requires Proj 6.0 or later. Builds based on earlier Proj versions will ignore this setting.
*
* \see setAllowFallbackTransforms()
* \see setBallparkTransformsAreAppropriate()
* \since QGIS 3.12
*/
bool allowFallbackTransforms() const;

/**
* Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
*
Expand All @@ -401,9 +427,13 @@ class CORE_EXPORT QgsCoordinateTransform
* when the preferred or user-specified operation fails, however whenever this occurs
* a user-visible warning will be generated.
*
* If allowFallbackTransforms() is FALSE then this setting has no effect.
*
* \warning This setting applies to a single instance of a coordinate transform only,
* and is not copied when a coordinate transform object is copied or assigned.
*
* \note Requires Proj 6.0 or later. Builds based on earlier Proj versions will ignore this setting.
*
* \since QGIS 3.12
*/
void setBallparkTransformsAreAppropriate( bool appropriate );
Expand All @@ -417,6 +447,8 @@ class CORE_EXPORT QgsCoordinateTransform
* \warning This setting applies to a single instance of a coordinate transform only,
* and is not copied when a coordinate transform object is copied or assigned.
*
* \note Requires Proj 6.0 or later. Builds based on earlier Proj versions will never perform fallback operations.
*
* \see fallbackOperationOccurred()
* \since QGIS 3.12
*/
Expand All @@ -425,6 +457,8 @@ class CORE_EXPORT QgsCoordinateTransform
/**
* Returns TRUE if a fallback operation occurred for the most recent transform.
*
* \note Requires Proj 6.0 or later. Builds based on earlier Proj versions will never perform fallback operations.
*
* \see disableFallbackOperationHandler()
* \since QGIS 3.12
*/
Expand Down Expand Up @@ -647,7 +681,7 @@ class CORE_EXPORT QgsCoordinateTransform
#if PROJ_VERSION_MAJOR>=6
bool setFromCache( const QgsCoordinateReferenceSystem &src,
const QgsCoordinateReferenceSystem &dest,
const QString &coordinateOperationProj );
const QString &coordinateOperationProj, bool allowFallback );
#else
bool setFromCache( const QgsCoordinateReferenceSystem &src,
const QgsCoordinateReferenceSystem &dest,
Expand Down
2 changes: 2 additions & 0 deletions src/core/qgscoordinatetransform_p.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ QgsCoordinateTransformPrivate::QgsCoordinateTransformPrivate( const QgsCoordinat
, mDestinationDatumTransform( other.mDestinationDatumTransform )
, mProjCoordinateOperation( other.mProjCoordinateOperation )
, mShouldReverseCoordinateOperation( other.mShouldReverseCoordinateOperation )
, mAllowFallbackTransforms( other.mAllowFallbackTransforms )
, mIsReversed( other.mIsReversed )
{
#if PROJ_VERSION_MAJOR < 6
Expand Down Expand Up @@ -263,6 +264,7 @@ void QgsCoordinateTransformPrivate::calculateTransforms( const QgsCoordinateTran
#if PROJ_VERSION_MAJOR >= 6
mProjCoordinateOperation = context.calculateCoordinateOperation( mSourceCRS, mDestCRS );
mShouldReverseCoordinateOperation = context.mustReverseCoordinateOperation( mSourceCRS, mDestCRS );
mAllowFallbackTransforms = context.allowFallbackTransform( mSourceCRS, mDestCRS );
#else
Q_NOWARN_DEPRECATED_PUSH
QgsDatumTransform::TransformPair transforms = context.calculateDatumTransforms( mSourceCRS, mDestCRS );
Expand Down
1 change: 1 addition & 0 deletions src/core/qgscoordinatetransform_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ class QgsCoordinateTransformPrivate : public QSharedData
Q_DECL_DEPRECATED int mDestinationDatumTransform = -1;
QString mProjCoordinateOperation;
bool mShouldReverseCoordinateOperation = false;
bool mAllowFallbackTransforms = true;

//! True if the proj transform corresponds to the reverse direction, and must be flipped when transforming...
bool mIsReversed = false;
Expand Down
17 changes: 16 additions & 1 deletion tests/src/python/test_qgscoordinatetransform.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,20 @@ def testContextProj6(self):

transform = QgsCoordinateTransform(QgsCoordinateReferenceSystem('EPSG:28354'), QgsCoordinateReferenceSystem('EPSG:28353'), context)
self.assertEqual(list(transform.context().coordinateOperations().keys()), [('EPSG:28356', 'EPSG:4283')])

# should be no coordinate operation
self.assertEqual(transform.coordinateOperation(), '')
# should default to allowing fallback transforms
self.assertTrue(transform.allowFallbackTransforms())

transform = QgsCoordinateTransform(QgsCoordinateReferenceSystem('EPSG:28356'),
QgsCoordinateReferenceSystem('EPSG:4283'), context)
self.assertTrue(transform.allowFallbackTransforms())
context.addCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:28356'),
QgsCoordinateReferenceSystem('EPSG:4283'),
'proj', False)
transform = QgsCoordinateTransform(QgsCoordinateReferenceSystem('EPSG:28356'),
QgsCoordinateReferenceSystem('EPSG:4283'), context)
self.assertFalse(transform.allowFallbackTransforms())

# matching source
transform = QgsCoordinateTransform(QgsCoordinateReferenceSystem('EPSG:28356'), QgsCoordinateReferenceSystem('EPSG:28353'), context)
Expand All @@ -168,6 +179,10 @@ def testContextProj6(self):
# test manual overwriting
transform.setCoordinateOperation('proj2')
self.assertEqual(transform.coordinateOperation(), 'proj2')
transform.setAllowFallbackTransforms(False)
self.assertFalse(transform.allowFallbackTransforms())
transform.setAllowFallbackTransforms(True)
self.assertTrue(transform.allowFallbackTransforms())

# test that auto operation setting occurs when updating src/dest crs
transform.setSourceCrs(QgsCoordinateReferenceSystem('EPSG:28356'))
Expand Down

0 comments on commit e3b1649

Please sign in to comment.