Skip to content

Commit

Permalink
Add QgsCoordinateTransformContext arguments to QgsCoordinateTransform…
Browse files Browse the repository at this point in the history
… constructor

And automatically retrieve correct datum transforms for the
transform, based on the information in the context.

Also add a convenience constructor which takes a QgsProject
instance instead of a QgsCoordinateTransformContext and which
automatically retrieves the project's transform context and uses
that. This is designed to make it easy to upgrade existing
Python code (by adding just QgsProject.instance() to the transform
constructors).
  • Loading branch information
nyalldawson committed Dec 15, 2017
1 parent 2edb2d8 commit 91e0afb
Show file tree
Hide file tree
Showing 6 changed files with 296 additions and 25 deletions.
82 changes: 78 additions & 4 deletions python/core/qgscoordinatetransform.sip
Expand Up @@ -45,14 +45,42 @@ class QgsCoordinateTransform
Default constructor, creates an invalid QgsCoordinateTransform.
%End

QgsCoordinateTransform( const QgsCoordinateReferenceSystem &source,
const QgsCoordinateReferenceSystem &destination );
explicit QgsCoordinateTransform( const QgsCoordinateReferenceSystem &source,
const QgsCoordinateReferenceSystem &destination );
%Docstring
Constructs a QgsCoordinateTransform using QgsCoordinateReferenceSystem objects.
\param source source CRS, typically of the layer's coordinate system
\param destination CRS, typically of the map canvas coordinate system
%End

explicit QgsCoordinateTransform( const QgsCoordinateReferenceSystem &source,
const QgsCoordinateReferenceSystem &destination,
const QgsCoordinateTransformContext &context );
%Docstring
Constructs a QgsCoordinateTransform to transform from the ``source``
to ``destination`` coordinate reference system.

The ``context`` argument specifies the context under which the transform
will be applied, and is used for calculating necessary datum transforms
to utilise.

.. versionadded:: 3.0
%End

explicit QgsCoordinateTransform( const QgsCoordinateReferenceSystem &source,
const QgsCoordinateReferenceSystem &destination,
const QgsProject *project );
%Docstring
Constructs a QgsCoordinateTransform to transform from the ``source``
to ``destination`` coordinate reference system, when used with the
given ``project``.

No reference to ``project`` is stored or utilised outside of the constructor,
and it is used to retrieve the project's transform context only.

.. versionadded:: 3.0
%End

QgsCoordinateTransform( const QgsCoordinateTransform &o );
%Docstring
Copy constructor
Expand Down Expand Up @@ -86,6 +114,13 @@ Default constructor, creates an invalid QgsCoordinateTransform.
\param crs CRS to transform coordinates to
.. seealso:: :py:func:`destinationCrs()`
.. seealso:: :py:func:`setSourceCrs()`
%End

void setContext( const QgsCoordinateTransformContext &context );
%Docstring
Sets the ``context`` in which the coordinate transform should be
calculated.
.. versionadded:: 3.0
%End

QgsCoordinateReferenceSystem sourceCrs() const;
Expand Down Expand Up @@ -213,14 +248,53 @@ Default constructor, creates an invalid QgsCoordinateTransform.

int sourceDatumTransform() const;
%Docstring
Returns the index of the datum transform to use when projecting from the source
CRS.

This is usually calculated automatically from the transform's QgsCoordinateTransformContext,
but can be manually overwritten by a call to setSourceDatumTransform().

.. seealso:: setSourceDatumTransform()
.. seealso:: destinationDatumTransform()
:rtype: int
%End
void setSourceDatumTransform( int dt );

void setSourceDatumTransform( int datum );
%Docstring
Sets the index of the ``datum`` transform to use when projecting from the source
CRS.

This is usually calculated automatically from the transform's QgsCoordinateTransformContext.
Calling this method will overwrite any automatically calculated datum transform.

.. seealso:: sourceDatumTransform()
.. seealso:: setDestinationDatumTransform()
%End

int destinationDatumTransform() const;
%Docstring
Returns the index of the datum transform to use when projecting to the destination
CRS.

This is usually calculated automatically from the transform's QgsCoordinateTransformContext,
but can be manually overwritten by a call to setDestinationDatumTransform().

.. seealso:: setDestinationDatumTransform()
.. seealso:: sourceDatumTransform()
:rtype: int
%End
void setDestinationDatumTransform( int dt );

void setDestinationDatumTransform( int datum );
%Docstring
Sets the index of the ``datum`` transform to use when projecting to the destination
CRS.

This is usually calculated automatically from the transform's QgsCoordinateTransformContext.
Calling this method will overwrite any automatically calculated datum transform.

.. seealso:: destinationDatumTransform()
.. seealso:: setSourceDatumTransform()
%End

void initialize();
%Docstring
Expand Down
23 changes: 22 additions & 1 deletion src/core/qgscoordinatetransform.cpp
Expand Up @@ -22,6 +22,7 @@
#include "qgspointxy.h"
#include "qgsrectangle.h"
#include "qgsexception.h"
#include "qgsproject.h"

//qt includes
#include <QDomNode>
Expand All @@ -47,7 +48,17 @@ QgsCoordinateTransform::QgsCoordinateTransform()

QgsCoordinateTransform::QgsCoordinateTransform( const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination )
{
d = new QgsCoordinateTransformPrivate( source, destination );
d = new QgsCoordinateTransformPrivate( source, destination, QgsCoordinateTransformContext() );
}

QgsCoordinateTransform::QgsCoordinateTransform( const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination, const QgsCoordinateTransformContext &context )
{
d = new QgsCoordinateTransformPrivate( source, destination, context );
}

QgsCoordinateTransform::QgsCoordinateTransform( const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination, const QgsProject *project )
{
d = new QgsCoordinateTransformPrivate( source, destination, project ? project->transformContext() : QgsCoordinateTransformContext() );
}

QgsCoordinateTransform::QgsCoordinateTransform( const QgsCoordinateTransform &o )
Expand All @@ -67,12 +78,22 @@ void QgsCoordinateTransform::setSourceCrs( const QgsCoordinateReferenceSystem &c
{
d.detach();
d->mSourceCRS = crs;
d->calculateTransforms();
d->initialize();
}
void QgsCoordinateTransform::setDestinationCrs( const QgsCoordinateReferenceSystem &crs )
{
d.detach();
d->mDestCRS = crs;
d->calculateTransforms();
d->initialize();
}

void QgsCoordinateTransform::setContext( const QgsCoordinateTransformContext &context )
{
d.detach();
d->mContext = context;
d->calculateTransforms();
d->initialize();
}

Expand Down
88 changes: 84 additions & 4 deletions src/core/qgscoordinatetransform.h
Expand Up @@ -27,6 +27,8 @@ class QgsCoordinateTransformPrivate;
class QgsPointXY;
class QgsRectangle;
class QPolygonF;
class QgsCoordinateTransformContext;
class QgsProject;

/**
* \ingroup core
Expand Down Expand Up @@ -63,8 +65,36 @@ class CORE_EXPORT QgsCoordinateTransform
* \param source source CRS, typically of the layer's coordinate system
* \param destination CRS, typically of the map canvas coordinate system
*/
QgsCoordinateTransform( const QgsCoordinateReferenceSystem &source,
const QgsCoordinateReferenceSystem &destination );
explicit QgsCoordinateTransform( const QgsCoordinateReferenceSystem &source,
const QgsCoordinateReferenceSystem &destination );

/**
* Constructs a QgsCoordinateTransform to transform from the \a source
* to \a destination coordinate reference system.
*
* The \a context argument specifies the context under which the transform
* will be applied, and is used for calculating necessary datum transforms
* to utilise.
*
* \since QGIS 3.0
*/
explicit QgsCoordinateTransform( const QgsCoordinateReferenceSystem &source,
const QgsCoordinateReferenceSystem &destination,
const QgsCoordinateTransformContext &context );

/**
* Constructs a QgsCoordinateTransform to transform from the \a source
* to \a destination coordinate reference system, when used with the
* given \a project.
*
* No reference to \a project is stored or utilised outside of the constructor,
* and it is used to retrieve the project's transform context only.
*
* \since QGIS 3.0
*/
explicit QgsCoordinateTransform( const QgsCoordinateReferenceSystem &source,
const QgsCoordinateReferenceSystem &destination,
const QgsProject *project );

/**
* Copy constructor
Expand Down Expand Up @@ -101,6 +131,13 @@ class CORE_EXPORT QgsCoordinateTransform
*/
void setDestinationCrs( const QgsCoordinateReferenceSystem &crs );

/**
* Sets the \a context in which the coordinate transform should be
* calculated.
* \since QGIS 3.0
*/
void setContext( const QgsCoordinateTransformContext &context );

/**
* Returns the source coordinate reference system, which the transform will
* transform coordinates from.
Expand Down Expand Up @@ -271,10 +308,53 @@ class CORE_EXPORT QgsCoordinateTransform
\returns epsgNr epsg code of the transformation (or 0 if not in epsg db)*/
static bool datumTransformCrsInfo( int datumTransform, int &epsgNr, QString &srcProjection, QString &dstProjection, QString &remarks, QString &scope, bool &preferred, bool &deprecated );

/**
* Returns the index of the datum transform to use when projecting from the source
* CRS.
*
* This is usually calculated automatically from the transform's QgsCoordinateTransformContext,
* but can be manually overwritten by a call to setSourceDatumTransform().
*
* \see setSourceDatumTransform()
* \see destinationDatumTransform()
*/
int sourceDatumTransform() const;
void setSourceDatumTransform( int dt );

/**
* Sets the index of the \a datum transform to use when projecting from the source
* CRS.
*
* This is usually calculated automatically from the transform's QgsCoordinateTransformContext.
* Calling this method will overwrite any automatically calculated datum transform.
*
* \see sourceDatumTransform()
* \see setDestinationDatumTransform()
*/
void setSourceDatumTransform( int datum );

/**
* Returns the index of the datum transform to use when projecting to the destination
* CRS.
*
* This is usually calculated automatically from the transform's QgsCoordinateTransformContext,
* but can be manually overwritten by a call to setDestinationDatumTransform().
*
* \see setDestinationDatumTransform()
* \see sourceDatumTransform()
*/
int destinationDatumTransform() const;
void setDestinationDatumTransform( int dt );

/**
* Sets the index of the \a datum transform to use when projecting to the destination
* CRS.
*
* This is usually calculated automatically from the transform's QgsCoordinateTransformContext.
* Calling this method will overwrite any automatically calculated datum transform.
*
* \see destinationDatumTransform()
* \see setSourceDatumTransform()
*/
void setDestinationDatumTransform( int datum );

//!initialize is used to actually create the Transformer instance
void initialize();
Expand Down
40 changes: 28 additions & 12 deletions src/core/qgscoordinatetransform_p.cpp
Expand Up @@ -50,11 +50,15 @@ QgsCoordinateTransformPrivate::QgsCoordinateTransformPrivate()
setFinder();
}

QgsCoordinateTransformPrivate::QgsCoordinateTransformPrivate( const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination )
QgsCoordinateTransformPrivate::QgsCoordinateTransformPrivate( const QgsCoordinateReferenceSystem &source,
const QgsCoordinateReferenceSystem &destination,
const QgsCoordinateTransformContext &context )
: mSourceCRS( source )
, mDestCRS( destination )
, mContext( context )
{
setFinder();
calculateTransforms();
initialize();
}

Expand All @@ -64,6 +68,7 @@ QgsCoordinateTransformPrivate::QgsCoordinateTransformPrivate( const QgsCoordinat
, mShortCircuit( other.mShortCircuit )
, mSourceCRS( other.mSourceCRS )
, mDestCRS( other.mDestCRS )
, mContext( other.mContext )
, mSourceDatumTransform( other.mSourceDatumTransform )
, mDestinationDatumTransform( other.mDestinationDatumTransform )
{
Expand Down Expand Up @@ -101,7 +106,9 @@ bool QgsCoordinateTransformPrivate::initialize()

mIsValid = true;

bool useDefaultDatumTransform = ( mSourceDatumTransform == - 1 && mDestinationDatumTransform == -1 );
int sourceDatumTransform = mSourceDatumTransform;
int destDatumTransform = mDestinationDatumTransform;
bool useDefaultDatumTransform = ( sourceDatumTransform == - 1 && destDatumTransform == -1 );

// init the projections (destination and source)
freeProj();
Expand All @@ -111,24 +118,24 @@ bool QgsCoordinateTransformPrivate::initialize()
{
mSourceProjString = stripDatumTransform( mSourceProjString );
}
if ( mSourceDatumTransform != -1 )
if ( sourceDatumTransform != -1 )
{
mSourceProjString += ( ' ' + datumTransformString( mSourceDatumTransform ) );
mSourceProjString += ( ' ' + datumTransformString( sourceDatumTransform ) );
}

mDestProjString = mDestCRS.toProj4();
if ( !useDefaultDatumTransform )
{
mDestProjString = stripDatumTransform( mDestProjString );
}
if ( mDestinationDatumTransform != -1 )
if ( destDatumTransform != -1 )
{
mDestProjString += ( ' ' + datumTransformString( mDestinationDatumTransform ) );
mDestProjString += ( ' ' + datumTransformString( destDatumTransform ) );
}

if ( !useDefaultDatumTransform )
{
addNullGridShifts( mSourceProjString, mDestProjString );
addNullGridShifts( mSourceProjString, mDestProjString, sourceDatumTransform, destDatumTransform );
}

// create proj projections for current thread
Expand Down Expand Up @@ -185,6 +192,14 @@ bool QgsCoordinateTransformPrivate::initialize()
return mIsValid;
}

void QgsCoordinateTransformPrivate::calculateTransforms()
{
// recalculate datum transforms from context
QPair< int, int > transforms = mContext.calculateDatumTransforms( mSourceCRS, mDestCRS );
mSourceDatumTransform = transforms.first;
mDestinationDatumTransform = transforms.second;
}

QPair<projPJ, projPJ> QgsCoordinateTransformPrivate::threadLocalProjData()
{
mProjLock.lockForRead();
Expand Down Expand Up @@ -301,27 +316,28 @@ QString QgsCoordinateTransformPrivate::datumTransformString( int datumTransform
return transformString;
}

void QgsCoordinateTransformPrivate::addNullGridShifts( QString &srcProjString, QString &destProjString ) const
void QgsCoordinateTransformPrivate::addNullGridShifts( QString &srcProjString, QString &destProjString,
int sourceDatumTransform, int destinationDatumTransform ) const
{
//if one transformation uses ntv2, the other one needs to be null grid shift
if ( mDestinationDatumTransform == -1 && srcProjString.contains( QLatin1String( "+nadgrids" ) ) ) //add null grid if source transformation is ntv2
if ( destinationDatumTransform == -1 && srcProjString.contains( QLatin1String( "+nadgrids" ) ) ) //add null grid if source transformation is ntv2
{
destProjString += QLatin1String( " +nadgrids=@null" );
return;
}
if ( mSourceDatumTransform == -1 && destProjString.contains( QLatin1String( "+nadgrids" ) ) )
if ( sourceDatumTransform == -1 && destProjString.contains( QLatin1String( "+nadgrids" ) ) )
{
srcProjString += QLatin1String( " +nadgrids=@null" );
return;
}

//add null shift grid for google mercator
//(see e.g. http://trac.osgeo.org/proj/wiki/FAQ#ChangingEllipsoidWhycantIconvertfromWGS84toGoogleEarthVirtualGlobeMercator)
if ( mSourceCRS.authid().compare( QLatin1String( "EPSG:3857" ), Qt::CaseInsensitive ) == 0 && mSourceDatumTransform == -1 )
if ( mSourceCRS.authid().compare( QLatin1String( "EPSG:3857" ), Qt::CaseInsensitive ) == 0 && sourceDatumTransform == -1 )
{
srcProjString += QLatin1String( " +nadgrids=@null" );
}
if ( mDestCRS.authid().compare( QLatin1String( "EPSG:3857" ), Qt::CaseInsensitive ) == 0 && mDestinationDatumTransform == -1 )
if ( mDestCRS.authid().compare( QLatin1String( "EPSG:3857" ), Qt::CaseInsensitive ) == 0 && destinationDatumTransform == -1 )
{
destProjString += QLatin1String( " +nadgrids=@null" );
}
Expand Down

0 comments on commit 91e0afb

Please sign in to comment.