Start on QgsCoordinateTransformContext

Stores settings related to the correct datum transforms to use
when performing a coordinate transform.
This commit is contained in:
Nyall Dawson 2017-11-05 16:56:24 +10:00
parent 905a14759d
commit 8a0bd08e07
8 changed files with 592 additions and 0 deletions

View File

@ -1742,6 +1742,112 @@ template<int, TYPE2*>
%End
};
%MappedType QMap< QPair< QString, QString>, QPair< int, int > >
{
%TypeHeaderCode
#include <QPair>
#include <QMap>
%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<QString *>(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<QString *>(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 <TYPE>
%MappedType QVector< TYPE* >

View File

@ -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

View File

@ -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<QString, int> 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<str, int>
%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 *
************************************************************************/

View File

@ -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

View File

@ -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<QString, int> 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<QString, int> 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<QString, QString>, QPair<int, int> > 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<int, int> QgsCoordinateTransformContext::calculateDatumTransforms( const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination ) const
{
}

View File

@ -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<QString, int> 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

View File

@ -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)

View File

@ -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()