Skip to content
Permalink
Browse files

Add a method to determine if the coordinate operation string returned

by QgsCoordinateTransformContext.calculateCoordinateOperation corresponds
to the reverse of what's actually required.

Gross API, but it's the best we can do until proj has a method to
invert a coordinate operation so that we can return the proper
inverse operation proj string from calculateCoordinateOperation
(without resorting to fragile proj string parsing/mangling)

(cherry picked from commit a1ac778)
  • Loading branch information
nyalldawson committed Dec 13, 2019
1 parent eb8c7d7 commit 8a7d6d2ccdb5a85de851aee84c4450c06f167407
@@ -221,9 +221,24 @@ be used.
an empty string, and the deprecated calculateDatumTransforms() method should be used instead.


.. warning::

Always check the result of mustReverseCoordinateOperation() in order to determine if the
proj coordinate operation string returned by this method corresponds to the reverse operation, and
must be manually flipped when calculating coordinate transforms.


.. versionadded:: 3.8
%End

bool mustReverseCoordinateOperation( const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination ) const;
%Docstring
Returns ``True`` if the coordinate operation returned by calculateCoordinateOperation() for the ``source`` to ``destination`` pair
must be inverted.

.. versionadded:: 3.10.2
%End


bool readXml( const QDomElement &element, const QgsReadWriteContext &context, QStringList &missingTransforms /Out/ );
%Docstring
@@ -189,6 +189,36 @@ QString QgsCoordinateTransformContext::calculateCoordinateOperation( const QgsCo
#endif
}

bool QgsCoordinateTransformContext::mustReverseCoordinateOperation( const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination ) const
{
#if PROJ_VERSION_MAJOR>=6
const QString srcKey = source.authid();
const QString destKey = destination.authid();

d->mLock.lockForRead();
QString res = d->mSourceDestDatumTransforms.value( qMakePair( srcKey, destKey ), QString() );
if ( !res.isEmpty() )
{
d->mLock.unlock();
return false;
}
// see if the reverse operation is present
res = d->mSourceDestDatumTransforms.value( qMakePair( destKey, srcKey ), QString() );
if ( !res.isEmpty() )
{
d->mLock.unlock();
return true;
}

d->mLock.unlock();
return false;
#else
Q_UNUSED( source )
Q_UNUSED( destination )
return false;
#endif
}

bool QgsCoordinateTransformContext::readXml( const QDomElement &element, const QgsReadWriteContext &, QStringList &missingTransforms )
{
d.detach();
@@ -206,10 +206,22 @@ class CORE_EXPORT QgsCoordinateTransformContext
* \note Requires Proj 6.0 or later. Builds based on earlier Proj versions will always return
* an empty string, and the deprecated calculateDatumTransforms() method should be used instead.
*
* \warning Always check the result of mustReverseCoordinateOperation() in order to determine if the
* proj coordinate operation string returned by this method corresponds to the reverse operation, and
* must be manually flipped when calculating coordinate transforms.
*
* \since QGIS 3.8
*/
QString calculateCoordinateOperation( const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination ) const;

/**
* Returns TRUE if the coordinate operation returned by calculateCoordinateOperation() for the \a source to \a destination pair
* must be inverted.
*
* \since QGIS 3.10.2
*/
bool mustReverseCoordinateOperation( const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination ) const;

// TODO QGIS 4.0 - remove missingTransforms, not used for Proj >= 6.0 builds

/**
@@ -147,6 +147,9 @@ def testSourceDestinationDatumTransformsProj6(self):
proj_string = '+proj=pipeline +step +inv +proj=lcc +lat_0=-37 +lon_0=145 +lat_1=-36 +lat_2=-38 +x_0=2500000 +y_0=2500000 +ellps=GRS80 +step +proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1'
self.assertFalse(
context.hasTransform(QgsCoordinateReferenceSystem('EPSG:3111'), QgsCoordinateReferenceSystem('EPSG:4283')))
self.assertFalse(context.mustReverseCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:3111'), QgsCoordinateReferenceSystem('EPSG:4283')))
self.assertFalse(context.mustReverseCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:4283'),
QgsCoordinateReferenceSystem('EPSG:3111')))
self.assertTrue(context.addCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:3111'),
QgsCoordinateReferenceSystem('EPSG:4283'), proj_string))
self.assertTrue(
@@ -156,6 +159,31 @@ def testSourceDestinationDatumTransformsProj6(self):
self.assertFalse(
context.hasTransform(QgsCoordinateReferenceSystem('EPSG:3113'), QgsCoordinateReferenceSystem('EPSG:4283')))
self.assertEqual(context.coordinateOperations(), {('EPSG:3111', 'EPSG:4283'): proj_string})
self.assertTrue(context.mustReverseCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:4283'),
QgsCoordinateReferenceSystem('EPSG:3111')))

self.assertTrue(
context.hasTransform(QgsCoordinateReferenceSystem('EPSG:4283'), QgsCoordinateReferenceSystem('EPSG:3111')))

self.assertEqual(context.calculateCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:3111'),
QgsCoordinateReferenceSystem('EPSG:4283')), proj_string)
self.assertFalse(context.mustReverseCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:3111'),
QgsCoordinateReferenceSystem('EPSG:4283')))
# ideally not equal, but for now it's all we can do, and return True for mustReverseCoordinateOperation here
self.assertEqual(context.calculateCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:4283'),
QgsCoordinateReferenceSystem('EPSG:3111')), proj_string)
self.assertTrue(context.mustReverseCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:4283'),
QgsCoordinateReferenceSystem('EPSG:3111')))
proj_string_2 = 'dummy'
self.assertTrue(context.addCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:4283'),
QgsCoordinateReferenceSystem('EPSG:3111'), proj_string_2))
self.assertEqual(context.calculateCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:4283'),
QgsCoordinateReferenceSystem('EPSG:3111')), proj_string_2)
self.assertFalse(context.mustReverseCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:4283'),
QgsCoordinateReferenceSystem('EPSG:3111')))
context.removeCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:4283'),
QgsCoordinateReferenceSystem('EPSG:3111'))

proj_string_2 = '+proj=pipeline +step +inv +proj=utm +zone=56 +south +ellps=GRS80 +step +proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1'
self.assertTrue(context.addCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:28356'),
QgsCoordinateReferenceSystem(4283), proj_string_2))

0 comments on commit 8a7d6d2

Please sign in to comment.
You can’t perform that action at this time.