Skip to content

Commit

Permalink
Add API to QgsCoordinateTransformContext to prevent use of fallback
Browse files Browse the repository at this point in the history
ballpark transforms for a source/dest CRS pair
  • Loading branch information
nyalldawson committed Feb 17, 2020
1 parent 62e6e9e commit 61e6ab0
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 25 deletions.
21 changes: 20 additions & 1 deletion python/core/auto_generated/qgscoordinatetransformcontext.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ Returns ``True`` if the new transform pair was added successfully.
Has no effect on builds based on Proj 6.0 or later, use addCoordinateOperation() instead.
%End

bool addCoordinateOperation( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QString &coordinateOperationProjString );
bool addCoordinateOperation( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QString &coordinateOperationProjString, bool allowFallback = true );
%Docstring
Adds a new ``coordinateOperationProjString`` to use when projecting coordinates
from the specified ``sourceCrs`` to the specified ``destinationCrs``.
Expand All @@ -134,6 +134,14 @@ from the specified ``sourceCrs`` to the specified ``destinationCrs``.
string. If ``coordinateOperationProjString`` is empty, then the default Proj operation
will be used when transforming between the coordinate reference systems.

If ``allowFallback`` is ``True`` (since QGIS 3.12), then "ballpark" fallback transformations
will be used in the case that the specified coordinate operation fails (such as when
coordinates from outside a required grid shift file are transformed). See
QgsCoordinateTransform.fallbackOperationOccurred() for further details. Note that if an
existing ``sourceCrs`` and ``destinationCrs`` pair are added with a different ``allowFallback``
value, that value will replace the existing one (i.e. each combination of ``sourceCrs`` and
``destinationCrs`` must be unique).

.. warning::

coordinateOperationProjString MUST be a proj string which has been normalized for
Expand Down Expand Up @@ -229,6 +237,17 @@ be used.


.. versionadded:: 3.8
%End

bool allowFallbackTransform( const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination ) const;
%Docstring
Returns ``True`` if approximate "ballpark" transforms may be used when transforming
between a \source and ``destination`` CRS pair, in the case that the preferred
coordinate operation fails (such as when
coordinates from outside a required grid shift file are transformed). See
QgsCoordinateTransform.fallbackOperationOccurred() for further details.

.. versionadded:: 3.12
%End

bool mustReverseCoordinateOperation( const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination ) const;
Expand Down
68 changes: 52 additions & 16 deletions src/core/qgscoordinatetransformcontext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,11 @@ QMap<QPair<QString, QString>, QString> QgsCoordinateTransformContext::coordinate
auto res = d->mSourceDestDatumTransforms;
res.detach();
d->mLock.unlock();
return res;
QMap<QPair<QString, QString>, QString> results;
for ( auto it = res.constBegin(); it != res.constEnd(); ++it )
results.insert( it.key(), it.value().operation );

return results;
#else
return QMap<QPair<QString, QString>, QString>();
#endif
Expand All @@ -103,18 +107,22 @@ bool QgsCoordinateTransformContext::addSourceDestinationDatumTransform( const Qg
#endif
}

bool QgsCoordinateTransformContext::addCoordinateOperation( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QString &coordinateOperationProjString )
bool QgsCoordinateTransformContext::addCoordinateOperation( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QString &coordinateOperationProjString, bool allowFallback )
{
if ( !sourceCrs.isValid() || !destinationCrs.isValid() )
return false;
#if PROJ_VERSION_MAJOR>=6
d.detach();
d->mLock.lockForWrite();
d->mSourceDestDatumTransforms.insert( qMakePair( sourceCrs.authid(), destinationCrs.authid() ), coordinateOperationProjString );
QgsCoordinateTransformContextPrivate::OperationDetails details;
details.operation = coordinateOperationProjString;
details.allowFallback = allowFallback;
d->mSourceDestDatumTransforms.insert( qMakePair( sourceCrs.authid(), destinationCrs.authid() ), details );
d->mLock.unlock();
return true;
#else
Q_UNUSED( coordinateOperationProjString )
Q_UNUSED( allowFallback )
return false;
#endif
}
Expand Down Expand Up @@ -174,37 +182,54 @@ QString QgsCoordinateTransformContext::calculateCoordinateOperation( const QgsCo
const QString destKey = destination.authid();

d->mLock.lockForRead();
QString res = d->mSourceDestDatumTransforms.value( qMakePair( srcKey, destKey ), QString() );
if ( res.isEmpty() )
QgsCoordinateTransformContextPrivate::OperationDetails res = d->mSourceDestDatumTransforms.value( qMakePair( srcKey, destKey ), QgsCoordinateTransformContextPrivate::OperationDetails() );
if ( res.operation.isEmpty() )
{
// try to reverse
res = d->mSourceDestDatumTransforms.value( qMakePair( destKey, srcKey ), QString() );
res = d->mSourceDestDatumTransforms.value( qMakePair( destKey, srcKey ), QgsCoordinateTransformContextPrivate::OperationDetails() );
}
d->mLock.unlock();
return res;
return res.operation;
#else
Q_UNUSED( source )
Q_UNUSED( destination )
return QString();
#endif
}

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() );
d->mLock.unlock();
return res.allowFallback;
#else
Q_UNUSED( source )
Q_UNUSED( destination )
return false;
#endif
}

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

d->mLock.lockForRead();
QString res = d->mSourceDestDatumTransforms.value( qMakePair( srcKey, destKey ), QString() );
if ( !res.isEmpty() )
QgsCoordinateTransformContextPrivate::OperationDetails res = d->mSourceDestDatumTransforms.value( qMakePair( srcKey, destKey ), 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 ), QString() );
if ( !res.isEmpty() )
res = d->mSourceDestDatumTransforms.value( qMakePair( destKey, srcKey ), QgsCoordinateTransformContextPrivate::OperationDetails() );
if ( !res.operation.isEmpty() )
{
d->mLock.unlock();
return true;
Expand Down Expand Up @@ -248,6 +273,7 @@ bool QgsCoordinateTransformContext::readXml( const QDomElement &element, const Q

#if PROJ_VERSION_MAJOR>=6
const QString coordinateOp = transformElem.attribute( QStringLiteral( "coordinateOp" ) );
const bool allowFallback = transformElem.attribute( QStringLiteral( "allowFallback" ), QStringLiteral( "1" ) ).toInt();

// try to instantiate operation, and check for missing grids
if ( !QgsProjUtils::coordinateOperationIsAvailable( coordinateOp ) )
Expand All @@ -257,7 +283,10 @@ bool QgsCoordinateTransformContext::readXml( const QDomElement &element, const Q
result = false;
}

d->mSourceDestDatumTransforms.insert( qMakePair( key1, key2 ), coordinateOp );
QgsCoordinateTransformContextPrivate::OperationDetails deets;
deets.operation = coordinateOp;
deets.allowFallback = allowFallback;
d->mSourceDestDatumTransforms.insert( qMakePair( key1, key2 ), deets );
#else
QString value1 = transformElem.attribute( QStringLiteral( "sourceTransform" ) );
QString value2 = transformElem.attribute( QStringLiteral( "destTransform" ) );
Expand Down Expand Up @@ -307,7 +336,8 @@ void QgsCoordinateTransformContext::writeXml( QDomElement &element, const QgsRea
transformElem.setAttribute( QStringLiteral( "source" ), it.key().first );
transformElem.setAttribute( QStringLiteral( "dest" ), it.key().second );
#if PROJ_VERSION_MAJOR>=6
transformElem.setAttribute( QStringLiteral( "coordinateOp" ), it.value() );
transformElem.setAttribute( QStringLiteral( "coordinateOp" ), it.value().operation );
transformElem.setAttribute( QStringLiteral( "allowFallback" ), it.value().allowFallback ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
#else
Q_NOWARN_DEPRECATED_PUSH
transformElem.setAttribute( QStringLiteral( "sourceTransform" ), it.value().sourceTransformId < 0 ? QString() : QgsDatumTransform::datumTransformToProj( it.value().sourceTransformId ) );
Expand All @@ -334,7 +364,7 @@ void QgsCoordinateTransformContext::readSettings()

//collect src and dest entries that belong together
#if PROJ_VERSION_MAJOR>=6
QMap< QPair< QString, QString >, QString > transforms;
QMap< QPair< QString, QString >, QgsCoordinateTransformContextPrivate::OperationDetails > transforms;
#else
QMap< QPair< QString, QString >, QPair< int, int > > transforms;
#endif
Expand All @@ -356,7 +386,11 @@ void QgsCoordinateTransformContext::readSettings()
}

const QString proj = settings.value( *pkeyIt ).toString();
transforms[ qMakePair( srcAuthId, destAuthId )] = proj;
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;
}
#else
if ( pkeyIt->contains( QLatin1String( "srcTransform" ) ) || pkeyIt->contains( QLatin1String( "destTransform" ) ) )
Expand Down Expand Up @@ -423,8 +457,10 @@ void QgsCoordinateTransformContext::writeSettings()
const QString destAuthId = transformIt.key().second;

#if PROJ_VERSION_MAJOR>=6
const QString proj = transformIt.value();
const QString proj = transformIt.value().operation;
const bool allowFallback = transformIt.value().allowFallback;
settings.setValue( srcAuthId + "//" + destAuthId + "_coordinateOp", proj );
settings.setValue( srcAuthId + "//" + destAuthId + "_allowFallback", allowFallback );
#else
int sourceDatumTransform = transformIt.value().sourceTransformId;
QString sourceDatumProj;
Expand Down
21 changes: 20 additions & 1 deletion src/core/qgscoordinatetransformcontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,14 @@ class CORE_EXPORT QgsCoordinateTransformContext
* string. If \a coordinateOperationProjString is empty, then the default Proj operation
* will be used when transforming between the coordinate reference systems.
*
* If \a allowFallback is TRUE (since QGIS 3.12), then "ballpark" fallback transformations
* will be used in the case that the specified coordinate operation fails (such as when
* coordinates from outside a required grid shift file are transformed). See
* QgsCoordinateTransform::fallbackOperationOccurred() for further details. Note that if an
* existing \a sourceCrs and \a destinationCrs pair are added with a different \a allowFallback
* value, that value will replace the existing one (i.e. each combination of \a sourceCrs and
* \a destinationCrs must be unique).
*
* \warning coordinateOperationProjString MUST be a proj string which has been normalized for
* visualization, and must be constructed so that coordinates are always input and output
* with x/y coordinate ordering. (Proj strings output by utilities such as projinfo will NOT
Expand All @@ -155,7 +163,7 @@ class CORE_EXPORT QgsCoordinateTransformContext
*
* \since QGIS 3.8
*/
bool addCoordinateOperation( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QString &coordinateOperationProjString );
bool addCoordinateOperation( const QgsCoordinateReferenceSystem &sourceCrs, const QgsCoordinateReferenceSystem &destinationCrs, const QString &coordinateOperationProjString, bool allowFallback = true );

/**
* Removes the source to destination datum transform pair for the specified \a sourceCrs and
Expand Down Expand Up @@ -214,6 +222,17 @@ class CORE_EXPORT QgsCoordinateTransformContext
*/
QString calculateCoordinateOperation( const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination ) const;

/**
* Returns TRUE if approximate "ballpark" transforms may be used when transforming
* between a \source and \a destination CRS pair, in the case that the preferred
* coordinate operation fails (such as when
* coordinates from outside a required grid shift file are transformed). See
* QgsCoordinateTransform::fallbackOperationOccurred() for further details.
*
* \since QGIS 3.12
*/
bool allowFallbackTransform( const QgsCoordinateReferenceSystem &source, const QgsCoordinateReferenceSystem &destination ) const;

/**
* Returns TRUE if the coordinate operation returned by calculateCoordinateOperation() for the \a source to \a destination pair
* must be inverted.
Expand Down
13 changes: 12 additions & 1 deletion src/core/qgscoordinatetransformcontext_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,18 @@ class QgsCoordinateTransformContextPrivate : public QSharedData
* Mapping for coordinate operation Proj string to use for source/destination CRS pairs.
*/
#if PROJ_VERSION_MAJOR>=6
QMap< QPair< QString, QString >, QString > mSourceDestDatumTransforms;
class OperationDetails
{
public:
QString operation;
bool allowFallback = true;

bool operator==( const OperationDetails &other ) const
{
return operation == other.operation && allowFallback == other.allowFallback;
}
};
QMap< QPair< QString, QString >, OperationDetails > mSourceDestDatumTransforms;
#else
QMap< QPair< QString, QString >, QgsDatumTransform::TransformPair > mSourceDestDatumTransforms;
#endif
Expand Down
Loading

0 comments on commit 61e6ab0

Please sign in to comment.