Fix invalid transforms occur if project file contains corrupt/incomplete

coordinate operation details

Also make storage of transform operations more resilent by correctly
handling crses without authids.

Fixes #34926
This commit is contained in:
Nyall Dawson 2020-03-13 12:23:33 +10:00
parent 02a94f030c
commit b91bccc64c
3 changed files with 157 additions and 98 deletions

View File

@ -21,6 +21,19 @@
#include "qgssettings.h"
#include "qgsprojutils.h"
QString crsToKey( const QgsCoordinateReferenceSystem &crs )
{
return crs.authid().isEmpty() ? crs.toWkt( QgsCoordinateReferenceSystem::WKT2_2018 ) : crs.authid();
}
template<>
bool qMapLessThanKey<QPair<QgsCoordinateReferenceSystem, QgsCoordinateReferenceSystem>>( const QPair<QgsCoordinateReferenceSystem, QgsCoordinateReferenceSystem> &key1,
const QPair<QgsCoordinateReferenceSystem, QgsCoordinateReferenceSystem> &key2 )
{
const QPair< QString, QString > key1String = qMakePair( crsToKey( key1.first ), crsToKey( key1.second ) );
const QPair< QString, QString > key2String = qMakePair( crsToKey( key2.first ), crsToKey( key2.second ) );
return key1String < key2String;
}
QgsCoordinateTransformContext::QgsCoordinateTransformContext()
: d( new QgsCoordinateTransformContextPrivate() )
@ -28,11 +41,11 @@ QgsCoordinateTransformContext::QgsCoordinateTransformContext()
QgsCoordinateTransformContext::~QgsCoordinateTransformContext() = default;
QgsCoordinateTransformContext::QgsCoordinateTransformContext( const QgsCoordinateTransformContext &rhs ) //NOLINT
QgsCoordinateTransformContext::QgsCoordinateTransformContext( const QgsCoordinateTransformContext &rhs ) //NOLINT
: d( rhs.d )
{}
QgsCoordinateTransformContext &QgsCoordinateTransformContext::operator=( const QgsCoordinateTransformContext &rhs ) //NOLINT
QgsCoordinateTransformContext &QgsCoordinateTransformContext::operator=( const QgsCoordinateTransformContext &rhs ) //NOLINT
{
d = rhs.d;
return *this;
@ -82,7 +95,7 @@ QMap<QPair<QString, QString>, QString> QgsCoordinateTransformContext::coordinate
d->mLock.unlock();
QMap<QPair<QString, QString>, QString> results;
for ( auto it = res.constBegin(); it != res.constEnd(); ++it )
results.insert( it.key(), it.value().operation );
results.insert( qMakePair( it.key().first.authid(), it.key().second.authid() ), it.value().operation );
return results;
#else
@ -117,7 +130,7 @@ bool QgsCoordinateTransformContext::addCoordinateOperation( const QgsCoordinateR
QgsCoordinateTransformContextPrivate::OperationDetails details;
details.operation = coordinateOperationProjString;
details.allowFallback = allowFallback;
d->mSourceDestDatumTransforms.insert( qMakePair( sourceCrs.authid(), destinationCrs.authid() ), details );
d->mSourceDestDatumTransforms.insert( qMakePair( sourceCrs, destinationCrs ), details );
d->mLock.unlock();
return true;
#else
@ -134,7 +147,7 @@ void QgsCoordinateTransformContext::removeSourceDestinationDatumTransform( const
void QgsCoordinateTransformContext::removeCoordinateOperation( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs )
{
d->mSourceDestDatumTransforms.remove( qMakePair( sourceCrs.authid(), destinationCrs.authid() ) );
d->mSourceDestDatumTransforms.remove( qMakePair( sourceCrs, destinationCrs ) );
}
bool QgsCoordinateTransformContext::hasTransform( const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination ) const
@ -178,15 +191,12 @@ QgsDatumTransform::TransformPair QgsCoordinateTransformContext::calculateDatumTr
QString QgsCoordinateTransformContext::calculateCoordinateOperation( 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();
QgsCoordinateTransformContextPrivate::OperationDetails res = d->mSourceDestDatumTransforms.value( qMakePair( srcKey, destKey ), QgsCoordinateTransformContextPrivate::OperationDetails() );
QgsCoordinateTransformContextPrivate::OperationDetails res = d->mSourceDestDatumTransforms.value( qMakePair( source, destination ), QgsCoordinateTransformContextPrivate::OperationDetails() );
if ( res.operation.isEmpty() )
{
// try to reverse
res = d->mSourceDestDatumTransforms.value( qMakePair( destKey, srcKey ), QgsCoordinateTransformContextPrivate::OperationDetails() );
res = d->mSourceDestDatumTransforms.value( qMakePair( destination, source ), QgsCoordinateTransformContextPrivate::OperationDetails() );
}
d->mLock.unlock();
return res.operation;
@ -200,15 +210,12 @@ QString QgsCoordinateTransformContext::calculateCoordinateOperation( const QgsCo
bool QgsCoordinateTransformContext::allowFallbackTransform( 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();
QgsCoordinateTransformContextPrivate::OperationDetails res = d->mSourceDestDatumTransforms.value( qMakePair( srcKey, destKey ), QgsCoordinateTransformContextPrivate::OperationDetails() );
QgsCoordinateTransformContextPrivate::OperationDetails res = d->mSourceDestDatumTransforms.value( qMakePair( source, destination ), QgsCoordinateTransformContextPrivate::OperationDetails() );
if ( res.operation.isEmpty() )
{
// try to reverse
res = d->mSourceDestDatumTransforms.value( qMakePair( destKey, srcKey ), QgsCoordinateTransformContextPrivate::OperationDetails() );
res = d->mSourceDestDatumTransforms.value( qMakePair( destination, source ), QgsCoordinateTransformContextPrivate::OperationDetails() );
}
d->mLock.unlock();
return res.allowFallback;
@ -222,18 +229,15 @@ bool QgsCoordinateTransformContext::allowFallbackTransform( const QgsCoordinateR
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();
QgsCoordinateTransformContextPrivate::OperationDetails res = d->mSourceDestDatumTransforms.value( qMakePair( srcKey, destKey ), QgsCoordinateTransformContextPrivate::OperationDetails() );
QgsCoordinateTransformContextPrivate::OperationDetails res = d->mSourceDestDatumTransforms.value( qMakePair( source, destination ), QgsCoordinateTransformContextPrivate::OperationDetails() );
if ( !res.operation.isEmpty() )
{
d->mLock.unlock();
return false;
}
// see if the reverse operation is present
res = d->mSourceDestDatumTransforms.value( qMakePair( destKey, srcKey ), QgsCoordinateTransformContextPrivate::OperationDetails() );
res = d->mSourceDestDatumTransforms.value( qMakePair( destination, source ), QgsCoordinateTransformContextPrivate::OperationDetails() );
if ( !res.operation.isEmpty() )
{
d->mLock.unlock();
@ -273,10 +277,30 @@ bool QgsCoordinateTransformContext::readXml( const QDomElement &element, const Q
for ( int i = 0; i < srcDestNodes.size(); ++i )
{
const QDomElement transformElem = srcDestNodes.at( i ).toElement();
const QString key1 = transformElem.attribute( QStringLiteral( "source" ) );
const QString key2 = transformElem.attribute( QStringLiteral( "dest" ) );
#if PROJ_VERSION_MAJOR>=6
const QDomElement srcElem = transformElem.firstChildElement( QStringLiteral( "src" ) );
const QDomElement destElem = transformElem.firstChildElement( QStringLiteral( "dest" ) );
QgsCoordinateReferenceSystem srcCrs;
QgsCoordinateReferenceSystem destCrs;
if ( !srcElem.isNull() && !destElem.isNull() )
{
srcCrs.readXml( srcElem );
destCrs.readXml( destElem );
}
else
{
// for older project compatibility
const QString key1 = transformElem.attribute( QStringLiteral( "source" ) );
const QString key2 = transformElem.attribute( QStringLiteral( "dest" ) );
srcCrs = QgsCoordinateReferenceSystem( key1 );
destCrs = QgsCoordinateReferenceSystem( key2 );
}
if ( !srcCrs.isValid() || !destCrs.isValid() )
continue;
const QString coordinateOp = transformElem.attribute( QStringLiteral( "coordinateOp" ) );
const bool allowFallback = transformElem.attribute( QStringLiteral( "allowFallback" ), QStringLiteral( "1" ) ).toInt();
@ -291,8 +315,11 @@ bool QgsCoordinateTransformContext::readXml( const QDomElement &element, const Q
QgsCoordinateTransformContextPrivate::OperationDetails deets;
deets.operation = coordinateOp;
deets.allowFallback = allowFallback;
d->mSourceDestDatumTransforms.insert( qMakePair( key1, key2 ), deets );
d->mSourceDestDatumTransforms.insert( qMakePair( srcCrs, destCrs ), deets );
#else
const QString key1 = transformElem.attribute( QStringLiteral( "source" ) );
const QString key2 = transformElem.attribute( QStringLiteral( "dest" ) );
QString value1 = transformElem.attribute( QStringLiteral( "sourceTransform" ) );
QString value2 = transformElem.attribute( QStringLiteral( "destTransform" ) );
@ -332,14 +359,24 @@ void QgsCoordinateTransformContext::writeXml( QDomElement &element, const QgsRea
{
d->mLock.lockForRead();
QDomElement contextElem = element.ownerDocument().createElement( QStringLiteral( "transformContext" ) );
QDomDocument doc = element.ownerDocument();
QDomElement contextElem = doc.createElement( QStringLiteral( "transformContext" ) );
//src/dest transforms
for ( auto it = d->mSourceDestDatumTransforms.constBegin(); it != d->mSourceDestDatumTransforms.constEnd(); ++ it )
{
QDomElement transformElem = element.ownerDocument().createElement( QStringLiteral( "srcDest" ) );
transformElem.setAttribute( QStringLiteral( "source" ), it.key().first );
transformElem.setAttribute( QStringLiteral( "dest" ), it.key().second );
QDomElement transformElem = doc.createElement( QStringLiteral( "srcDest" ) );
QDomElement srcElem = doc.createElement( QStringLiteral( "src" ) );
QDomElement destElem = doc.createElement( QStringLiteral( "dest" ) );
it.key().first.writeXml( srcElem, doc );
it.key().second.writeXml( destElem, doc );
transformElem.appendChild( srcElem );
transformElem.appendChild( destElem );
#if PROJ_VERSION_MAJOR>=6
transformElem.setAttribute( QStringLiteral( "coordinateOp" ), it.value().operation );
transformElem.setAttribute( QStringLiteral( "allowFallback" ), it.value().allowFallback ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
@ -369,7 +406,7 @@ void QgsCoordinateTransformContext::readSettings()
//collect src and dest entries that belong together
#if PROJ_VERSION_MAJOR>=6
QMap< QPair< QString, QString >, QgsCoordinateTransformContextPrivate::OperationDetails > transforms;
QMap< QPair< QgsCoordinateReferenceSystem, QgsCoordinateReferenceSystem >, QgsCoordinateTransformContextPrivate::OperationDetails > transforms;
#else
QMap< QPair< QString, QString >, QPair< int, int > > transforms;
#endif
@ -390,12 +427,15 @@ void QgsCoordinateTransformContext::readSettings()
destAuthId = split.at( 1 ).split( '_' ).at( 0 );
}
if ( srcAuthId.isEmpty() || destAuthId.isEmpty() )
continue;
const QString proj = settings.value( *pkeyIt ).toString();
const bool allowFallback = settings.value( QStringLiteral( "%1//%2_allowFallback" ).arg( srcAuthId, destAuthId ) ).toBool();
QgsCoordinateTransformContextPrivate::OperationDetails deets;
deets.operation = proj;
deets.allowFallback = allowFallback;
transforms[ qMakePair( srcAuthId, destAuthId )] = deets;
transforms[ qMakePair( QgsCoordinateReferenceSystem( srcAuthId ), QgsCoordinateReferenceSystem( destAuthId ) )] = deets;
}
#else
if ( pkeyIt->contains( QLatin1String( "srcTransform" ) ) || pkeyIt->contains( QLatin1String( "destTransform" ) ) )
@ -458,8 +498,11 @@ void QgsCoordinateTransformContext::writeSettings()
for ( auto transformIt = d->mSourceDestDatumTransforms.constBegin(); transformIt != d->mSourceDestDatumTransforms.constEnd(); ++transformIt )
{
const QString srcAuthId = transformIt.key().first;
const QString destAuthId = transformIt.key().second;
const QString srcAuthId = transformIt.key().first.authid();
const QString destAuthId = transformIt.key().second.authid();
if ( srcAuthId.isEmpty() || destAuthId.isEmpty() )
continue; // not so nice, but alternative would be to shove whole CRS wkt into the settings values...
#if PROJ_VERSION_MAJOR>=6
const QString proj = transformIt.value().operation;

View File

@ -70,7 +70,7 @@ class QgsCoordinateTransformContextPrivate : public QSharedData
return operation == other.operation && allowFallback == other.allowFallback;
}
};
QMap< QPair< QString, QString >, OperationDetails > mSourceDestDatumTransforms;
QMap< QPair< QgsCoordinateReferenceSystem, QgsCoordinateReferenceSystem >, OperationDetails > mSourceDestDatumTransforms;
#else
QMap< QPair< QString, QString >, QgsDatumTransform::TransformPair > mSourceDestDatumTransforms;
#endif

View File

@ -54,12 +54,12 @@ class TestQgsCoordinateTransformContext(unittest.TestCase):
self.assertEqual(context.sourceDestinationDatumTransforms(),
{('EPSG:3111', 'EPSG:4283'): QgsDatumTransform.TransformPair(1, 2)})
self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:28356'),
QgsCoordinateReferenceSystem(4283), 3, 4))
QgsCoordinateReferenceSystem('EPSG:4283'), 3, 4))
self.assertEqual(context.sourceDestinationDatumTransforms(),
{('EPSG:3111', 'EPSG:4283'): QgsDatumTransform.TransformPair(1, 2),
('EPSG:28356', 'EPSG:4283'): QgsDatumTransform.TransformPair(3, 4)})
self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:28356'),
QgsCoordinateReferenceSystem(28357), 7, 8))
QgsCoordinateReferenceSystem('EPSG:28357'), 7, 8))
self.assertEqual(context.sourceDestinationDatumTransforms(),
{('EPSG:3111', 'EPSG:4283'): QgsDatumTransform.TransformPair(1, 2),
('EPSG:28356', 'EPSG:4283'): QgsDatumTransform.TransformPair(3, 4),
@ -86,23 +86,23 @@ class TestQgsCoordinateTransformContext(unittest.TestCase):
('EPSG:28356', 'EPSG:28357'): QgsDatumTransform.TransformPair(9, 11)})
# indicate no transform required
self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem(28357),
QgsCoordinateReferenceSystem(28356), -1, -1))
self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:28357'),
QgsCoordinateReferenceSystem('EPSG:28356'), -1, -1))
self.assertEqual(context.sourceDestinationDatumTransforms(),
{('EPSG:3111', 'EPSG:4283'): QgsDatumTransform.TransformPair(1, 2),
('EPSG:28356', 'EPSG:4283'): QgsDatumTransform.TransformPair(3, 4),
('EPSG:28356', 'EPSG:28357'): QgsDatumTransform.TransformPair(9, 11),
('EPSG:28357', 'EPSG:28356'): QgsDatumTransform.TransformPair(-1, -1)})
self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem(3111),
QgsCoordinateReferenceSystem(28356), 17, -1))
self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:3111'),
QgsCoordinateReferenceSystem('EPSG:28356'), 17, -1))
self.assertEqual(context.sourceDestinationDatumTransforms(),
{('EPSG:3111', 'EPSG:4283'): QgsDatumTransform.TransformPair(1, 2),
('EPSG:28356', 'EPSG:4283'): QgsDatumTransform.TransformPair(3, 4),
('EPSG:28356', 'EPSG:28357'): QgsDatumTransform.TransformPair(9, 11),
('EPSG:28357', 'EPSG:28356'): QgsDatumTransform.TransformPair(-1, -1),
('EPSG:3111', 'EPSG:28356'): QgsDatumTransform.TransformPair(17, -1)})
self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem(3113),
QgsCoordinateReferenceSystem(28356), -1, 18))
self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:3113'),
QgsCoordinateReferenceSystem('EPSG:28356'), -1, 18))
self.assertEqual(context.sourceDestinationDatumTransforms(),
{('EPSG:3111', 'EPSG:4283'): QgsDatumTransform.TransformPair(1, 2),
('EPSG:28356', 'EPSG:4283'): QgsDatumTransform.TransformPair(3, 4),
@ -111,7 +111,7 @@ class TestQgsCoordinateTransformContext(unittest.TestCase):
('EPSG:3111', 'EPSG:28356'): QgsDatumTransform.TransformPair(17, -1),
('EPSG:3113', 'EPSG:28356'): QgsDatumTransform.TransformPair(-1, 18)})
# remove non-existing
context.removeCoordinateOperation(QgsCoordinateReferenceSystem(3113), QgsCoordinateReferenceSystem(3111))
context.removeCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:3113'), QgsCoordinateReferenceSystem('EPSG:3111'))
self.assertEqual(context.sourceDestinationDatumTransforms(),
{('EPSG:3111', 'EPSG:4283'): QgsDatumTransform.TransformPair(1, 2),
('EPSG:28356', 'EPSG:4283'): QgsDatumTransform.TransformPair(3, 4),
@ -121,16 +121,16 @@ class TestQgsCoordinateTransformContext(unittest.TestCase):
('EPSG:3113', 'EPSG:28356'): QgsDatumTransform.TransformPair(-1, 18)})
# remove existing
context.removeCoordinateOperation(QgsCoordinateReferenceSystem(3111),
QgsCoordinateReferenceSystem(4283))
context.removeCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:3111'),
QgsCoordinateReferenceSystem('EPSG:4283'))
self.assertEqual(context.sourceDestinationDatumTransforms(),
{('EPSG:28356', 'EPSG:4283'): QgsDatumTransform.TransformPair(3, 4),
('EPSG:28356', 'EPSG:28357'): QgsDatumTransform.TransformPair(9, 11),
('EPSG:28357', 'EPSG:28356'): QgsDatumTransform.TransformPair(-1, -1),
('EPSG:3111', 'EPSG:28356'): QgsDatumTransform.TransformPair(17, -1),
('EPSG:3113', 'EPSG:28356'): QgsDatumTransform.TransformPair(-1, 18)})
context.removeCoordinateOperation(QgsCoordinateReferenceSystem(3111),
QgsCoordinateReferenceSystem(28356))
context.removeCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:3111'),
QgsCoordinateReferenceSystem('EPSG:28356'))
self.assertEqual(context.sourceDestinationDatumTransforms(),
{('EPSG:28356', 'EPSG:4283'): QgsDatumTransform.TransformPair(3, 4),
('EPSG:28356', 'EPSG:28357'): QgsDatumTransform.TransformPair(9, 11),
@ -143,7 +143,7 @@ class TestQgsCoordinateTransformContext(unittest.TestCase):
@unittest.skipIf(QgsProjUtils.projVersionMajor() < 6, 'Skipped on non proj6 builds')
def testSourceDestinationDatumTransformsProj6(self):
context = QgsCoordinateTransformContext()
self.assertEqual(context.sourceDestinationDatumTransforms(), {})
self.assertEqual(context.coordinateOperations(), {})
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')))
@ -203,12 +203,12 @@ class TestQgsCoordinateTransformContext(unittest.TestCase):
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))
QgsCoordinateReferenceSystem('EPSG:4283'), proj_string_2))
self.assertEqual(context.coordinateOperations(), {('EPSG:3111', 'EPSG:4283'): proj_string,
('EPSG:28356', 'EPSG:4283'): proj_string_2})
proj_string_3 = '+proj=pipeline +step +inv +proj=utm +zone=56 +south +ellps=GRS80 +step +proj=utm +zone=57 +south +ellps=GRS80'
self.assertTrue(context.addCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:28356'),
QgsCoordinateReferenceSystem(28357), proj_string_3))
QgsCoordinateReferenceSystem('EPSG:28357'), proj_string_3))
self.assertEqual(context.coordinateOperations(), {('EPSG:3111', 'EPSG:4283'): proj_string,
('EPSG:28356', 'EPSG:4283'): proj_string_2,
('EPSG:28356', 'EPSG:28357'): proj_string_3})
@ -232,28 +232,28 @@ class TestQgsCoordinateTransformContext(unittest.TestCase):
('EPSG:28356', 'EPSG:28357'): 'some other proj string'})
# indicate no transform required
self.assertTrue(context.addCoordinateOperation(QgsCoordinateReferenceSystem(28357),
QgsCoordinateReferenceSystem(28356), ''))
self.assertTrue(context.addCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:28357'),
QgsCoordinateReferenceSystem('EPSG:28356'), ''))
self.assertEqual(context.coordinateOperations(), {('EPSG:3111', 'EPSG:4283'): proj_string,
('EPSG:28356', 'EPSG:4283'): proj_string_2,
('EPSG:28356', 'EPSG:28357'): 'some other proj string',
('EPSG:28357', 'EPSG:28356'): ''})
# remove non-existing
context.removeCoordinateOperation(QgsCoordinateReferenceSystem(3113), QgsCoordinateReferenceSystem(3111))
context.removeCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:3113'), QgsCoordinateReferenceSystem('EPSG:3111'))
self.assertEqual(context.coordinateOperations(), {('EPSG:3111', 'EPSG:4283'): proj_string,
('EPSG:28356', 'EPSG:4283'): proj_string_2,
('EPSG:28356', 'EPSG:28357'): 'some other proj string',
('EPSG:28357', 'EPSG:28356'): ''})
# remove existing
context.removeCoordinateOperation(QgsCoordinateReferenceSystem(3111),
QgsCoordinateReferenceSystem(4283))
context.removeCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:3111'),
QgsCoordinateReferenceSystem('EPSG:4283'))
self.assertEqual(context.coordinateOperations(), {('EPSG:28356', 'EPSG:4283'): proj_string_2,
('EPSG:28356', 'EPSG:28357'): 'some other proj string',
('EPSG:28357', 'EPSG:28356'): ''})
context.removeCoordinateOperation(QgsCoordinateReferenceSystem(28356),
QgsCoordinateReferenceSystem(28357))
context.removeCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:28356'),
QgsCoordinateReferenceSystem('EPSG:28357'))
self.assertEqual(context.coordinateOperations(), {('EPSG:28356', 'EPSG:4283'): proj_string_2,
('EPSG:28357', 'EPSG:28356'): ''})
@ -319,21 +319,21 @@ class TestQgsCoordinateTransformContext(unittest.TestCase):
# setup a context
context = QgsCoordinateTransformContext()
source_id_1 = QgsDatumTransform.datumTransformations(QgsCoordinateReferenceSystem(4204),
QgsCoordinateReferenceSystem(4326))[0].sourceTransformId
dest_id_1 = QgsDatumTransform.datumTransformations(QgsCoordinateReferenceSystem(4204),
QgsCoordinateReferenceSystem(4326))[0].destinationTransformId
source_id_1 = QgsDatumTransform.datumTransformations(QgsCoordinateReferenceSystem('EPSG:4204'),
QgsCoordinateReferenceSystem('EPSG:4326'))[0].sourceTransformId
dest_id_1 = QgsDatumTransform.datumTransformations(QgsCoordinateReferenceSystem('EPSG:4204'),
QgsCoordinateReferenceSystem('EPSG:4326'))[0].destinationTransformId
source_id_2 = QgsDatumTransform.datumTransformations(QgsCoordinateReferenceSystem(4205),
QgsCoordinateReferenceSystem(4326))[0].sourceTransformId
dest_id_2 = QgsDatumTransform.datumTransformations(QgsCoordinateReferenceSystem(4205),
QgsCoordinateReferenceSystem(4326))[0].destinationTransformId
source_id_2 = QgsDatumTransform.datumTransformations(QgsCoordinateReferenceSystem('EPSG:4205'),
QgsCoordinateReferenceSystem('EPSG:4326'))[0].sourceTransformId
dest_id_2 = QgsDatumTransform.datumTransformations(QgsCoordinateReferenceSystem('EPSG:4205'),
QgsCoordinateReferenceSystem('EPSG:4326'))[0].destinationTransformId
self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem(4204),
QgsCoordinateReferenceSystem(4326), source_id_1,
self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:4204'),
QgsCoordinateReferenceSystem('EPSG:4326'), source_id_1,
dest_id_1))
self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem(4205),
QgsCoordinateReferenceSystem(4326), source_id_2,
self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:4205'),
QgsCoordinateReferenceSystem('EPSG:4326'), source_id_2,
dest_id_2))
self.assertEqual(context.sourceDestinationDatumTransforms(),
@ -361,15 +361,21 @@ class TestQgsCoordinateTransformContext(unittest.TestCase):
proj_1 = '+proj=pipeline +step +proj=axisswap +order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=push +v_3 +step +proj=cart +ellps=intl +step +proj=helmert +x=-18.944 +y=-379.364 +z=-24.063 +rx=-0.04 +ry=0.764 +rz=-6.431 +s=3.657 +convention=coordinate_frame +step +inv +proj=cart +ellps=WGS84 +step +proj=pop +v_3 +step +proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1'
proj_2 = '+proj=pipeline +step +proj=axisswap +order=2,1 +step +proj=unitconvert +xy_in=deg +xy_out=rad +step +proj=push +v_3 +step +proj=cart +ellps=intl +step +proj=helmert +x=-150 +y=-250 +z=-1 +step +inv +proj=cart +ellps=WGS84 +step +proj=pop +v_3 +step +proj=unitconvert +xy_in=rad +xy_out=deg +step +proj=axisswap +order=2,1'
proj_3 = '+proj=pipeline +step +proj=axisswap +order=2,1'
self.assertTrue(context.addCoordinateOperation(QgsCoordinateReferenceSystem(4204),
QgsCoordinateReferenceSystem(4326), proj_1, True))
self.assertTrue(context.addCoordinateOperation(QgsCoordinateReferenceSystem(4205),
QgsCoordinateReferenceSystem(4326), proj_2, False))
self.assertTrue(context.addCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:4204'),
QgsCoordinateReferenceSystem('EPSG:4326'), proj_1, True))
self.assertTrue(context.addCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:4205'),
QgsCoordinateReferenceSystem('EPSG:4326'), proj_2, False))
# also insert a crs with no authid available
self.assertTrue(context.addCoordinateOperation(QgsCoordinateReferenceSystem.fromProj("+proj=longlat +a=6378137 +rf=298.25722356300003 +no_defs"),
QgsCoordinateReferenceSystem('EPSG:4326'), proj_3, False))
self.assertEqual(context.coordinateOperations(),
{('EPSG:4204', 'EPSG:4326'): proj_1,
('EPSG:4205', 'EPSG:4326'): proj_2})
('EPSG:4205', 'EPSG:4326'): proj_2,
('', 'EPSG:4326'): proj_3})
# save to xml
doc = QDomDocument("testdoc")
@ -383,11 +389,21 @@ class TestQgsCoordinateTransformContext(unittest.TestCase):
# check result
self.assertEqual(context2.coordinateOperations(),
{('EPSG:4204', 'EPSG:4326'): proj_1,
('EPSG:4205', 'EPSG:4326'): proj_2})
self.assertTrue(context2.allowFallbackTransform(QgsCoordinateReferenceSystem(4204),
QgsCoordinateReferenceSystem(4326)))
self.assertFalse(context2.allowFallbackTransform(QgsCoordinateReferenceSystem(4205),
QgsCoordinateReferenceSystem(4326)))
('EPSG:4205', 'EPSG:4326'): proj_2,
('', 'EPSG:4326'): proj_3})
self.assertEqual(context2.calculateCoordinateOperation(QgsCoordinateReferenceSystem.fromProj("+proj=longlat +a=6378137 +rf=298.25722356300003 +no_defs"),
QgsCoordinateReferenceSystem('EPSG:4326')), '+proj=pipeline +step +proj=axisswap +order=2,1')
self.assertFalse(context2.mustReverseCoordinateOperation(QgsCoordinateReferenceSystem.fromProj("+proj=longlat +a=6378137 +rf=298.25722356300003 +no_defs"),
QgsCoordinateReferenceSystem('EPSG:4326')))
self.assertEqual(context2.calculateCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:4326'),
QgsCoordinateReferenceSystem.fromProj("+proj=longlat +a=6378137 +rf=298.25722356300003 +no_defs")),
'+proj=pipeline +step +proj=axisswap +order=2,1')
self.assertTrue(context2.mustReverseCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:4326'),
QgsCoordinateReferenceSystem.fromProj("+proj=longlat +a=6378137 +rf=298.25722356300003 +no_defs")))
self.assertTrue(context2.allowFallbackTransform(QgsCoordinateReferenceSystem('EPSG:4204'),
QgsCoordinateReferenceSystem('EPSG:4326')))
self.assertFalse(context2.allowFallbackTransform(QgsCoordinateReferenceSystem('EPSG:4205'),
QgsCoordinateReferenceSystem('EPSG:4326')))
@unittest.skipIf(QgsProjUtils.projVersionMajor() >= 6, 'Skipped on proj6 builds')
def testMissingTransforms(self):
@ -482,15 +498,15 @@ class TestQgsCoordinateTransformContext(unittest.TestCase):
context = QgsCoordinateTransformContext()
context.readSettings()
source_id_1 = QgsDatumTransform.datumTransformations(QgsCoordinateReferenceSystem(4204),
QgsCoordinateReferenceSystem(4326))[0].sourceTransformId
dest_id_1 = QgsDatumTransform.datumTransformations(QgsCoordinateReferenceSystem(4204),
QgsCoordinateReferenceSystem(4326))[0].destinationTransformId
source_id_1 = QgsDatumTransform.datumTransformations(QgsCoordinateReferenceSystem('EPSG:4204'),
QgsCoordinateReferenceSystem('EPSG:4326'))[0].sourceTransformId
dest_id_1 = QgsDatumTransform.datumTransformations(QgsCoordinateReferenceSystem('EPSG:4204'),
QgsCoordinateReferenceSystem('EPSG:4326'))[0].destinationTransformId
source_id_2 = QgsDatumTransform.datumTransformations(QgsCoordinateReferenceSystem(4205),
QgsCoordinateReferenceSystem(4326))[0].sourceTransformId
dest_id_2 = QgsDatumTransform.datumTransformations(QgsCoordinateReferenceSystem(4205),
QgsCoordinateReferenceSystem(4326))[0].destinationTransformId
source_id_2 = QgsDatumTransform.datumTransformations(QgsCoordinateReferenceSystem('EPSG:4205'),
QgsCoordinateReferenceSystem('EPSG:4326'))[0].sourceTransformId
dest_id_2 = QgsDatumTransform.datumTransformations(QgsCoordinateReferenceSystem('EPSG:4205'),
QgsCoordinateReferenceSystem('EPSG:4326'))[0].destinationTransformId
# should be empty
self.assertEqual(context.sourceDestinationDatumTransforms(), {})
@ -499,7 +515,7 @@ class TestQgsCoordinateTransformContext(unittest.TestCase):
QgsCoordinateReferenceSystem('EPSG:4326'),
source_id_1, dest_id_1))
self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:4205'),
QgsCoordinateReferenceSystem(4326), source_id_2,
QgsCoordinateReferenceSystem('EPSG:4326'), source_id_2,
dest_id_2))
self.assertEqual(context.sourceDestinationDatumTransforms(),
@ -530,18 +546,18 @@ class TestQgsCoordinateTransformContext(unittest.TestCase):
# should be empty
self.assertEqual(context.coordinateOperations(), {})
self.assertTrue(context.addCoordinateOperation(QgsCoordinateReferenceSystem(4204),
QgsCoordinateReferenceSystem(4326), proj_1, True))
self.assertTrue(context.addCoordinateOperation(QgsCoordinateReferenceSystem(4205),
QgsCoordinateReferenceSystem(4326), proj_2, False))
self.assertTrue(context.addCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:4204'),
QgsCoordinateReferenceSystem('EPSG:4326'), proj_1, True))
self.assertTrue(context.addCoordinateOperation(QgsCoordinateReferenceSystem('EPSG:4205'),
QgsCoordinateReferenceSystem('EPSG:4326'), proj_2, False))
self.assertEqual(context.coordinateOperations(),
{('EPSG:4204', 'EPSG:4326'): proj_1,
('EPSG:4205', 'EPSG:4326'): proj_2})
self.assertTrue(context.allowFallbackTransform(QgsCoordinateReferenceSystem(4204),
QgsCoordinateReferenceSystem(4326)))
self.assertFalse(context.allowFallbackTransform(QgsCoordinateReferenceSystem(4205),
QgsCoordinateReferenceSystem(4326)))
self.assertTrue(context.allowFallbackTransform(QgsCoordinateReferenceSystem('EPSG:4204'),
QgsCoordinateReferenceSystem('EPSG:4326')))
self.assertFalse(context.allowFallbackTransform(QgsCoordinateReferenceSystem('EPSG:4205'),
QgsCoordinateReferenceSystem('EPSG:4326')))
# save to settings
context.writeSettings()
@ -556,10 +572,10 @@ class TestQgsCoordinateTransformContext(unittest.TestCase):
{('EPSG:4204', 'EPSG:4326'): proj_1,
('EPSG:4205', 'EPSG:4326'): proj_2})
self.assertTrue(context2.allowFallbackTransform(QgsCoordinateReferenceSystem(4204),
QgsCoordinateReferenceSystem(4326)))
self.assertFalse(context2.allowFallbackTransform(QgsCoordinateReferenceSystem(4205),
QgsCoordinateReferenceSystem(4326)))
self.assertTrue(context2.allowFallbackTransform(QgsCoordinateReferenceSystem('EPSG:4204'),
QgsCoordinateReferenceSystem('EPSG:4326')))
self.assertFalse(context2.allowFallbackTransform(QgsCoordinateReferenceSystem('EPSG:4205'),
QgsCoordinateReferenceSystem('EPSG:4326')))
@unittest.skipIf(QgsProjUtils.projVersionMajor() >= 6, 'Skipped on proj6 builds')
def testEqualOperator(self):