From 8a0bd08e07e17a5fa9aa9b481454b2b8ade910c9 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Sun, 5 Nov 2017 16:56:24 +1000 Subject: [PATCH] Start on QgsCoordinateTransformContext Stores settings related to the correct datum transforms to use when performing a coordinate transform. --- python/core/conversions.sip | 106 ++++++++++++ python/core/core_auto.sip | 1 + python/core/qgscoordinatetransformcontext.sip | 149 ++++++++++++++++ src/core/CMakeLists.txt | 2 + src/core/qgscoordinatetransformcontext.cpp | 72 ++++++++ src/core/qgscoordinatetransformcontext.h | 159 ++++++++++++++++++ tests/src/python/CMakeLists.txt | 1 + .../test_qgscoordinatetransformcontext.py | 102 +++++++++++ 8 files changed, 592 insertions(+) create mode 100644 python/core/qgscoordinatetransformcontext.sip create mode 100644 src/core/qgscoordinatetransformcontext.cpp create mode 100644 src/core/qgscoordinatetransformcontext.h create mode 100644 tests/src/python/test_qgscoordinatetransformcontext.py diff --git a/python/core/conversions.sip b/python/core/conversions.sip index 2876f6bdee3..bc27467d2a9 100644 --- a/python/core/conversions.sip +++ b/python/core/conversions.sip @@ -1742,6 +1742,112 @@ template %End }; +%MappedType QMap< QPair< QString, QString>, QPair< int, int > > +{ +%TypeHeaderCode +#include +#include +%End + +%ConvertFromTypeCode +//convert map to a python dictionary + PyObject *d; + + if ( ( d = PyDict_New() ) == NULL ) + return NULL; + + for ( const auto it = sipCpp->constBegin(); it != sipCpp->constEnd(); ++ it ) + { + PyObject *keyobj; + if ( ( keyobj = PyTuple_New( 2 ) ) == NULL ) + { + Py_DECREF( d ); + return NULL; + } + PyObject *valueobj; + if ( ( valueobj = PyTuple_New( 2 ) ) == NULL ) + { + Py_DECREF( d ); + return NULL; + } + + // build key + PyObject *k1obj = sipConvertFromNewType( new QString( it.key().first ), sipType_QString, sipTransferObj ); + PyTuple_SetItem( keyobj, 0, k1obj ); + PyObject *k2obj = sipConvertFromNewType( new QString( it.key().second ), sipType_QString, sipTransferObj ); + PyTuple_SetItem( keyobj, 1, k2obj ); + + // build value + PyObject *v1obj = PyLong_FromLong( (long)( it.value().first ) ); + PyTuple_SetItem( valueobj, 0, v1obj ); + PyObject *v2obj = PyLong_FromLong( (long)( it.value().second ) ); + PyTuple_SetItem( valueobj, 1, v2obj ); + + if(keyobj == NULL || valueobj == NULL || PyDict_SetItem(d, keyobj, valueobj) < 0) + { + Py_DECREF(d); + + if (valueobj) + { + Py_DECREF(valueobj); + } + + if (keyobj) + { + Py_DECREF(keyobj); + } + return NULL; + } + + } + + return d; +%End + +%ConvertToTypeCode + Py_ssize_t i = 0; + + PyObject *t1obj, *t2obj; + QMap< QPair< QString, QString>, QPair< int, int > > *qm = new QMap< QPair< QString, QString>, QPair< int, int > >; + + int state; + while (PyDict_Next(sipPy, &i, &t1obj, &t2obj)) + { + PyObject *sipKeyFirst = PyTuple_GetItem( t1obj, 0 ); + PyObject *sipKeySecond = PyTuple_GetItem( t1obj, 1 ); + + PyObject *sipValueFirst = PyTuple_GetItem( t2obj, 0 ); + qint64 v1 = PyLong_AsLongLong(sipValueFirst); + PyObject *sipValueSecond = PyTuple_GetItem( t2obj, 1 ); + qint64 v2 = PyLong_AsLongLong(sipValueSecond); + + QString *k1 = reinterpret_cast(sipConvertToType(sipKeyFirst, sipType_QString, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr)); + if (*sipIsErr) + { + sipReleaseType(k1, sipType_QString, state); + delete qm; + return 0; + } + QString *k2 = reinterpret_cast(sipConvertToType(sipKeySecond, sipType_QString, sipTransferObj, SIP_NOT_NONE, &state, sipIsErr)); + if (*sipIsErr) + { + sipReleaseType(k1, sipType_QString, state); + sipReleaseType(k2, sipType_QString, state); + delete qm; + return 0; + } + + qm->insert( qMakePair( *k1,*k2 ), qMakePair( v1, v2 ) ); + sipReleaseType(k1, sipType_QString, state); + sipReleaseType(k2, sipType_QString, state); + } + + *sipCppPtr = qm; + + return sipGetState( sipTransferObj ); +%End +}; + template %MappedType QVector< TYPE* > diff --git a/python/core/core_auto.sip b/python/core/core_auto.sip index 675808a9f85..a1cc1325592 100644 --- a/python/core/core_auto.sip +++ b/python/core/core_auto.sip @@ -23,6 +23,7 @@ %Include qgsconditionalstyle.sip %Include qgscoordinateformatter.sip %Include qgscoordinatetransform.sip +%Include qgscoordinatetransformcontext.sip %Include qgscrscache.sip %Include qgsdartmeasurement.sip %Include qgsdatadefinedsizelegend.sip diff --git a/python/core/qgscoordinatetransformcontext.sip b/python/core/qgscoordinatetransformcontext.sip new file mode 100644 index 00000000000..4b5c905d115 --- /dev/null +++ b/python/core/qgscoordinatetransformcontext.sip @@ -0,0 +1,149 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/qgscoordinatetransformcontext.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + +class QgsCoordinateTransformContext +{ +%Docstring + Contains information about the context in which a coordinate transform is executed. + +.. versionadded:: 3.0 +%End + +%TypeHeaderCode +#include "qgscoordinatetransformcontext.h" +%End + public: + + QgsCoordinateTransformContext(); +%Docstring + Constructor for QgsCoordinateTransformContext. +%End + + void clear(); +%Docstring + Clears all stored transform information from the context. +%End + + QMap sourceDatumTransforms() const; +%Docstring + Returns the stored mapping for source CRS to associated datum transform to use. + The map keys will be QgsCoordinateReferenceSystems.authid()s. + + \warning This method should not be used to calculate the corresponding datum transforms + to use for a coordinate transform. Instead, always use calculateDatumTransforms() + to determine this. + +.. seealso:: addSourceDatumTransform() +.. seealso:: destinationDatumTransforms() + :rtype: QMap +%End + + bool addSourceDatumTransform( const QgsCoordinateReferenceSystem &crs, int transform ); +%Docstring + Adds a new ``transform`` to use when projecting coordinates from the specified source + ``crs``. + + Returns true if the new transform was added successfully. + + \warning Transforms set using this method may be overridden by specific source/destination + transforms set by addSourceDestinationDatumTransform(). + +.. seealso:: sourceDatumTransforms() +.. seealso:: addDestinationDatumTransform() + :rtype: bool +%End + + QMap< QString, int > destinationDatumTransforms() const; +%Docstring + Returns the stored mapping for destination CRS to associated datum transform to use. + The map keys will be QgsCoordinateReferenceSystems.authid()s. + + \warning This method should not be used to calculate the corresponding datum transforms + to use for a coordinate transform. Instead, always use calculateDatumTransforms() + to determine this. + +.. seealso:: addDestinationDatumTransform() +.. seealso:: sourceDatumTransforms() + :rtype: QMap< str, int > +%End + + bool addDestinationDatumTransform( const QgsCoordinateReferenceSystem &crs, int transform ); +%Docstring + Adds a new ``transform`` to use when projecting coordinates to the specified destination + ``crs``. + + Returns true if the new transform was added successfully. + + \warning Transforms set using this method may be overridden by specific source/destination + transforms set by addSourceDestinationDatumTransform(). + +.. seealso:: destinationDatumTransforms() +.. seealso:: addSourceDatumTransform() + :rtype: bool +%End + + QMap< QPair< QString, QString>, QPair< int, int > > sourceDestinationDatumTransforms() const; +%Docstring + Returns the stored mapping for source to destination CRS pairs to associated datum transforms to use. + The map keys will be QgsCoordinateReferenceSystems.authid()s. + + \warning This method should not be used to calculate the corresponding datum transforms + to use for a coordinate transform. Instead, always use calculateDatumTransforms() + to determine this. + +.. seealso:: addSourceDestinationDatumTransform() + :rtype: QMap< QPair< str, QString>, QPair< int, int > > +%End + + bool addSourceDestinationDatumTransform( const QgsCoordinateReferenceSystem &sourceCrs, + const QgsCoordinateReferenceSystem &destinationCrs, + int sourceTransform, + int destinationTransform ); +%Docstring + Adds a new ``sourceTransform`` and ``destinationTransform`` to use when projecting coordinates + from the the specified ``sourceCrs`` to the specified ``destinationCrs``. + + Returns true if the new transform pair was added successfully. + +.. note:: + + Transforms set using this method will override any specific source or destination + transforms set by addSourceDatumTransform() or addDestinationDatumTransform(). + +.. seealso:: sourceDestinationDatumTransforms() + :rtype: bool +%End + + QPair< int, int > calculateDatumTransforms( const QgsCoordinateReferenceSystem &source, + const QgsCoordinateReferenceSystem &destination ) const; +%Docstring + Returns the pair of source and destination datum transforms to use + for a transform from the specified ``source`` CRS to ``destination`` CRS. + + Returns -1 if a datum transform should not be used for the source or + destination. + :rtype: QPair< int, int > +%End + +}; + + + + + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/qgscoordinatetransformcontext.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 7130062923f..0efbcd43378 100755 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -150,6 +150,7 @@ SET(QGIS_CORE_SRCS qgscoordinatereferencesystem.cpp qgscoordinatetransform.cpp qgscoordinatetransform_p.cpp + qgscoordinatetransformcontext.cpp qgscoordinateutils.cpp qgscredentials.cpp qgscrscache.cpp @@ -854,6 +855,7 @@ SET(QGIS_CORE_HDRS qgsconditionalstyle.h qgscoordinateformatter.h qgscoordinatetransform.h + qgscoordinatetransformcontext.h qgscoordinateutils.h qgscrscache.h qgsdartmeasurement.h diff --git a/src/core/qgscoordinatetransformcontext.cpp b/src/core/qgscoordinatetransformcontext.cpp new file mode 100644 index 00000000000..55ab28685fa --- /dev/null +++ b/src/core/qgscoordinatetransformcontext.cpp @@ -0,0 +1,72 @@ +/*************************************************************************** + qgscoordinatetransformcontext.cpp + --------------------------------- + begin : November 2017 + copyright : (C) 2017 by Nyall Dawson + email : nyall dot dawson at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgscoordinatetransformcontext.h" + +void QgsCoordinateTransformContext::clear() +{ + mSourceDestDatumTransforms.clear(); + mSourceDatumTransforms.clear(); + mDestDatumTransforms.clear(); +} + +QMap QgsCoordinateTransformContext::sourceDatumTransforms() const +{ + return mSourceDatumTransforms; +} + +bool QgsCoordinateTransformContext::addSourceDatumTransform( const QgsCoordinateReferenceSystem &crs, int transform ) +{ + if ( !crs.isValid() ) + return false; + + mSourceDatumTransforms.insert( crs.authid(), transform ); + return true; +} + +QMap QgsCoordinateTransformContext::destinationDatumTransforms() const +{ + return mDestDatumTransforms; +} + +bool QgsCoordinateTransformContext::addDestinationDatumTransform( const QgsCoordinateReferenceSystem &crs, int transform ) +{ + if ( !crs.isValid() ) + return false; + + mDestDatumTransforms.insert( crs.authid(), transform ); + return true; +} + +QMap, QPair > QgsCoordinateTransformContext::sourceDestinationDatumTransforms() const +{ + return mSourceDestDatumTransforms; +} + +bool QgsCoordinateTransformContext::addSourceDestinationDatumTransform( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, int sourceTransform, int destinationTransform ) +{ + if ( !sourceCrs.isValid() || !destinationCrs.isValid() ) + return false; + + mSourceDestDatumTransforms.insert( qMakePair( sourceCrs.authid(), destinationCrs.authid() ), qMakePair( sourceTransform, destinationTransform ) ); + return true; +} + +QPair QgsCoordinateTransformContext::calculateDatumTransforms( const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination ) const +{ + +} diff --git a/src/core/qgscoordinatetransformcontext.h b/src/core/qgscoordinatetransformcontext.h new file mode 100644 index 00000000000..2f8b08dc9c8 --- /dev/null +++ b/src/core/qgscoordinatetransformcontext.h @@ -0,0 +1,159 @@ +/*************************************************************************** + qgscoordinatetransformcontext.h + ------------------------------- + begin : November 2017 + copyright : (C) 2017 by Nyall Dawson + email : nyall dot dawson at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSCOORDINATETRANSFORMCONTEXT_H +#define QGSCOORDINATETRANSFORMCONTEXT_H + +#include "qgis_core.h" +#include "qgis.h" +#include "qgscoordinatereferencesystem.h" + +/** + * \class QgsCoordinateTransformContext + * \ingroup core + * Contains information about the context in which a coordinate transform is executed. + * + * \since QGIS 3.0 +*/ + +class CORE_EXPORT QgsCoordinateTransformContext +{ + public: + + /** + * Constructor for QgsCoordinateTransformContext. + */ + QgsCoordinateTransformContext() = default; + + /** + * Clears all stored transform information from the context. + */ + void clear(); + + /** + * Returns the stored mapping for source CRS to associated datum transform to use. + * The map keys will be QgsCoordinateReferenceSystems::authid()s. + * + * \warning This method should not be used to calculate the corresponding datum transforms + * to use for a coordinate transform. Instead, always use calculateDatumTransforms() + * to determine this. + * + * \see addSourceDatumTransform() + * \see destinationDatumTransforms() + */ + QMap sourceDatumTransforms() const; + + /** + * Adds a new \a transform to use when projecting coordinates from the specified source + * \a crs. + * + * Returns true if the new transform was added successfully. + * + * \warning Transforms set using this method may be overridden by specific source/destination + * transforms set by addSourceDestinationDatumTransform(). + * + * \see sourceDatumTransforms() + * \see addDestinationDatumTransform() + */ + bool addSourceDatumTransform( const QgsCoordinateReferenceSystem &crs, int transform ); + + /** + * Returns the stored mapping for destination CRS to associated datum transform to use. + * The map keys will be QgsCoordinateReferenceSystems::authid()s. + * + * \warning This method should not be used to calculate the corresponding datum transforms + * to use for a coordinate transform. Instead, always use calculateDatumTransforms() + * to determine this. + * + * \see addDestinationDatumTransform() + * \see sourceDatumTransforms() + */ + QMap< QString, int > destinationDatumTransforms() const; + + /** + * Adds a new \a transform to use when projecting coordinates to the specified destination + * \a crs. + * + * Returns true if the new transform was added successfully. + * + * \warning Transforms set using this method may be overridden by specific source/destination + * transforms set by addSourceDestinationDatumTransform(). + * + * \see destinationDatumTransforms() + * \see addSourceDatumTransform() + */ + bool addDestinationDatumTransform( const QgsCoordinateReferenceSystem &crs, int transform ); + + /** + * Returns the stored mapping for source to destination CRS pairs to associated datum transforms to use. + * The map keys will be QgsCoordinateReferenceSystems::authid()s. + * + * \warning This method should not be used to calculate the corresponding datum transforms + * to use for a coordinate transform. Instead, always use calculateDatumTransforms() + * to determine this. + * + * \see addSourceDestinationDatumTransform() + */ + QMap< QPair< QString, QString>, QPair< int, int > > sourceDestinationDatumTransforms() const; + + /** + * Adds a new \a sourceTransform and \a destinationTransform to use when projecting coordinates + * from the the specified \a sourceCrs to the specified \a destinationCrs. + * + * Returns true if the new transform pair was added successfully. + * + * \note Transforms set using this method will override any specific source or destination + * transforms set by addSourceDatumTransform() or addDestinationDatumTransform(). + * + * \see sourceDestinationDatumTransforms() + */ + bool addSourceDestinationDatumTransform( const QgsCoordinateReferenceSystem &sourceCrs, + const QgsCoordinateReferenceSystem &destinationCrs, + int sourceTransform, + int destinationTransform ); + + /** + * Returns the pair of source and destination datum transforms to use + * for a transform from the specified \a source CRS to \a destination CRS. + * + * Returns -1 if a datum transform should not be used for the source or + * destination. + */ + QPair< int, int > calculateDatumTransforms( const QgsCoordinateReferenceSystem &source, + const QgsCoordinateReferenceSystem &destination ) const; + + private: + + /** + * Mapping for datum transforms to use for source/destination CRS pairs. + * Matching records from this map will take precedence over other transform maps. + */ + QMap< QPair< QString, QString >, QPair< int, int > > mSourceDestDatumTransforms; + + //! Mapping for datum transforms to use for source CRS + QMap< QString, int > mSourceDatumTransforms; + + //! Mapping for datum transforms to use for destination CRS + QMap< QString, int > mDestDatumTransforms; + +}; + +#endif // QGSCOORDINATETRANSFORMCONTEXT_H + + + + diff --git a/tests/src/python/CMakeLists.txt b/tests/src/python/CMakeLists.txt index 4bbd6e6464e..5482bae17c5 100644 --- a/tests/src/python/CMakeLists.txt +++ b/tests/src/python/CMakeLists.txt @@ -40,6 +40,7 @@ ADD_PYTHON_TEST(PyQgsComposerPolyline test_qgscomposerpolyline.py) ADD_PYTHON_TEST(PyQgsComposerView test_qgscomposerview.py) ADD_PYTHON_TEST(PyQgsComposition test_qgscomposition.py) ADD_PYTHON_TEST(PyQgsConditionalStyle test_qgsconditionalstyle.py) +ADD_PYTHON_TEST(PyQgsCoordinateTransformContext test_qgscoordinatetransformcontext.py) ADD_PYTHON_TEST(PyQgsDefaultValue test_qgsdefaultvalue.py) ADD_PYTHON_TEST(PyQgsXmlUtils test_qgsxmlutils.py) ADD_PYTHON_TEST(PyQgsCoordinateTransform test_qgscoordinatetransform.py) diff --git a/tests/src/python/test_qgscoordinatetransformcontext.py b/tests/src/python/test_qgscoordinatetransformcontext.py new file mode 100644 index 00000000000..9e2d49c2530 --- /dev/null +++ b/tests/src/python/test_qgscoordinatetransformcontext.py @@ -0,0 +1,102 @@ +# -*- coding: utf-8 -*- +"""QGIS Unit tests for QgsCoordinateTransformContext + +.. note:: This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +""" +__author__ = 'Nyall Dawson' +__date__ = '11/5/2017' +__copyright__ = 'Copyright 2017, The QGIS Project' +# This will get replaced with a git SHA1 when you do a git archive +__revision__ = '$Format:%H$' + +import qgis # NOQA + +from qgis.core import (QgsCoordinateReferenceSystem, + QgsCoordinateTransformContext) +from qgis.testing import start_app, unittest + +app = start_app() + + +class TestQgsCoordinateTransformContext(unittest.TestCase): + + def testSourceDatumTransforms(self): + context = QgsCoordinateTransformContext() + self.assertEqual(context.sourceDatumTransforms(), {}) + self.assertTrue(context.addSourceDatumTransform(QgsCoordinateReferenceSystem('EPSG:3111'), 1)) + self.assertEqual(context.sourceDatumTransforms(), {'EPSG:3111': 1}) + self.assertTrue(context.addSourceDatumTransform(QgsCoordinateReferenceSystem('EPSG:28356'), 2)) + self.assertEqual(context.sourceDatumTransforms(), {'EPSG:3111': 1, 'EPSG:28356': 2}) + self.assertTrue(context.addSourceDatumTransform(QgsCoordinateReferenceSystem('EPSG:28356'), 3)) + self.assertEqual(context.sourceDatumTransforms(), {'EPSG:3111': 1, 'EPSG:28356': 3}) + self.assertTrue(context.addSourceDatumTransform(QgsCoordinateReferenceSystem(28356), 3)) + self.assertEqual(context.sourceDatumTransforms(), {'EPSG:3111': 1, 'EPSG:28356': 3}) + + # invalid crs should fail + self.assertFalse(context.addSourceDatumTransform(QgsCoordinateReferenceSystem(), 4)) + self.assertEqual(context.sourceDatumTransforms(), {'EPSG:3111': 1, 'EPSG:28356': 3}) + + context.clear() + self.assertEqual(context.sourceDatumTransforms(), {}) + + def testDestDatumTransforms(self): + context = QgsCoordinateTransformContext() + self.assertEqual(context.destinationDatumTransforms(), {}) + self.assertTrue(context.addDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:3111'), 1)) + self.assertEqual(context.destinationDatumTransforms(), {'EPSG:3111': 1}) + self.assertTrue(context.addDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:28356'), 2)) + self.assertEqual(context.destinationDatumTransforms(), {'EPSG:3111': 1, 'EPSG:28356': 2}) + self.assertTrue(context.addDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:28356'), 3)) + self.assertEqual(context.destinationDatumTransforms(), {'EPSG:3111': 1, 'EPSG:28356': 3}) + self.assertTrue(context.addDestinationDatumTransform(QgsCoordinateReferenceSystem(28356), 3)) + self.assertEqual(context.destinationDatumTransforms(), {'EPSG:3111': 1, 'EPSG:28356': 3}) + + # invalid crs should fail + self.assertFalse(context.addDestinationDatumTransform(QgsCoordinateReferenceSystem(), 4)) + self.assertEqual(context.destinationDatumTransforms(), {'EPSG:3111': 1, 'EPSG:28356': 3}) + + context.clear() + self.assertEqual(context.destinationDatumTransforms(), {}) + + def testSourceDestinationDatumTransforms(self): + context = QgsCoordinateTransformContext() + self.assertEqual(context.sourceDestinationDatumTransforms(), {}) + self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:3111'), + QgsCoordinateReferenceSystem('EPSG:4283'), 1, 2)) + self.assertEqual(context.sourceDestinationDatumTransforms(), {('EPSG:3111', 'EPSG:4283'): (1, 2)}) + self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:28356'), + QgsCoordinateReferenceSystem(4283), 3, 4)) + self.assertEqual(context.sourceDestinationDatumTransforms(), {('EPSG:3111', 'EPSG:4283'): (1, 2), + ('EPSG:28356', 'EPSG:4283'): (3, 4)}) + self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:28356'), + QgsCoordinateReferenceSystem(28357), 7, 8)) + self.assertEqual(context.sourceDestinationDatumTransforms(), {('EPSG:3111', 'EPSG:4283'): (1, 2), + ('EPSG:28356', 'EPSG:4283'): (3, 4), + ('EPSG:28356', 'EPSG:28357'): (7, 8)}) + self.assertTrue(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:28356'), + QgsCoordinateReferenceSystem('EPSG:28357'), 9, 11)) + self.assertEqual(context.sourceDestinationDatumTransforms(), {('EPSG:3111', 'EPSG:4283'): (1, 2), + ('EPSG:28356', 'EPSG:4283'): (3, 4), + ('EPSG:28356', 'EPSG:28357'): (9, 11)}) + + # invalid additions + self.assertFalse(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem(), + QgsCoordinateReferenceSystem('EPSG:28357'), 9, 11)) + self.assertEqual(context.sourceDestinationDatumTransforms(), {('EPSG:3111', 'EPSG:4283'): (1, 2), + ('EPSG:28356', 'EPSG:4283'): (3, 4), + ('EPSG:28356', 'EPSG:28357'): (9, 11)}) + self.assertFalse(context.addSourceDestinationDatumTransform(QgsCoordinateReferenceSystem('EPSG:3111'), + QgsCoordinateReferenceSystem(), 9, 11)) + self.assertEqual(context.sourceDestinationDatumTransforms(), {('EPSG:3111', 'EPSG:4283'): (1, 2), + ('EPSG:28356', 'EPSG:4283'): (3, 4), + ('EPSG:28356', 'EPSG:28357'): (9, 11)}) + + context.clear() + self.assertEqual(context.sourceDestinationDatumTransforms(), {}) + + +if __name__ == '__main__': + unittest.main()