33#include <QApplication>
49QReadWriteLock QgsCoordinateTransform::sCacheLock;
51bool QgsCoordinateTransform::sDisableCache =
false;
55 const QString &desiredOperation )> QgsCoordinateTransform::sFallbackOperationOccurredHandler =
nullptr;
59 d =
new QgsCoordinateTransformPrivate();
65 d =
new QgsCoordinateTransformPrivate( source, destination, mContext );
68 mIgnoreImpossible =
true;
80 if ( !d->checkValidity() )
84 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
92 mBallparkTransformsAreAppropriate =
true;
98 d =
new QgsCoordinateTransformPrivate( source, destination, mContext );
105 mIgnoreImpossible =
true;
113 if ( !d->checkValidity() )
117 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
125 mBallparkTransformsAreAppropriate =
true;
130 d =
new QgsCoordinateTransformPrivate( source, destination, sourceDatumTransform, destinationDatumTransform );
135 if ( !d->checkValidity() )
139 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
148 : mContext( o.mContext )
150 , mHasContext( o.mHasContext )
156 , mIgnoreImpossible( false )
157 , mBallparkTransformsAreAppropriate( false )
158 , mDisableFallbackHandler( false )
159 , mFallbackOperationOccurred( false )
168 mHasContext = o.mHasContext;
170 mContext = o.mContext;
171 mLastError = QString();
182#if PROJ_VERSION_MAJOR>8 || (PROJ_VERSION_MAJOR==8 && PROJ_VERSION_MINOR>=1)
201 if ( !d->checkValidity() )
204 d->calculateTransforms( mContext );
206 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
224 if ( !d->checkValidity() )
227 d->calculateTransforms( mContext );
229 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
251 if ( !d->checkValidity() )
254 d->calculateTransforms( mContext );
256 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
271 return d->mSourceCRS;
281 if ( !d->mIsValid || d->mShortCircuit )
285 double x = point.
x();
286 double y = point.
y();
319 if ( !d->mIsValid || d->mShortCircuit )
343#ifdef COORDINATE_TRANSFORM_VERBOSE
355 double x = point.
x();
356 double y = point.
y();
357 double z = point.
z();
374 if ( !d->mIsValid || d->mShortCircuit )
395 double xd =
static_cast< double >( x ), yd =
static_cast< double >( y );
404 if ( !d->mIsValid || d->mShortCircuit )
430 if ( !d->mIsValid || d->mShortCircuit )
436 const int nVertices = poly.size();
438 QVector<double> x( nVertices );
439 QVector<double> y( nVertices );
440 QVector<double> z( nVertices );
441 double *destX = x.data();
442 double *destY = y.data();
443 double *destZ = z.data();
445 const QPointF *polyData = poly.constData();
446 for (
int i = 0; i < nVertices; ++i )
448 *destX++ = polyData->x();
449 *destY++ = polyData->y();
465 QPointF *destPoint = poly.data();
466 const double *srcX = x.constData();
467 const double *srcY = y.constData();
468 for (
int i = 0; i < nVertices; ++i )
470 destPoint->rx() = *srcX++;
471 destPoint->ry() = *srcY++;
476 if ( !err.isEmpty() )
484 if ( !d->mIsValid || d->mShortCircuit )
487 Q_ASSERT( x.size() == y.size() );
510 if ( !d->mIsValid || d->mShortCircuit )
513 Q_ASSERT( x.size() == y.size() );
523 const int vectorSize = x.size();
524 QVector<double> xd( x.size() );
525 QVector<double> yd( y.size() );
526 QVector<double> zd( z.size() );
528 double *destX = xd.data();
529 double *destY = yd.data();
530 double *destZ = zd.data();
532 const float *srcX = x.constData();
533 const float *srcY = y.constData();
534 const float *srcZ = z.constData();
536 for (
int i = 0; i < vectorSize; ++i )
538 *destX++ =
static_cast< double >( *srcX++ );
539 *destY++ =
static_cast< double >( *srcY++ );
540 *destZ++ =
static_cast< double >( *srcZ++ );
546 float *destFX = x.data();
547 float *destFY = y.data();
548 float *destFZ = z.data();
549 const double *srcXD = xd.constData();
550 const double *srcYD = yd.constData();
551 const double *srcZD = zd.constData();
552 for (
int i = 0; i < vectorSize; ++i )
554 *destFX++ =
static_cast< float >( *srcXD++ );
555 *destFY++ =
static_cast< float >( *srcYD++ );
556 *destFZ++ =
static_cast< float >( *srcZD++ );
574 if ( !d->mIsValid || d->mShortCircuit )
585 if ( d->mGeographicToWebMercator &&
594 constexpr double EPS = 1e-1;
595 if ( yMin < -90 + EPS )
597 if ( yMax < -90 + EPS )
598 throw QgsCsException( QObject::tr(
"Could not transform bounding box to target CRS" ) );
601 if ( yMax > 90 - EPS )
603 if ( yMin > 90 - EPS )
604 throw QgsCsException( QObject::tr(
"Could not transform bounding box to target CRS" ) );
613 const int nPoints = 1000;
614 const double dst = std::sqrt( ( rect.
width() * ( yMax - yMin ) ) / std::pow( std::sqrt(
static_cast< double >( nPoints ) ) - 1, 2.0 ) );
615 const int nXPoints = std::min(
static_cast< int >( std::ceil( rect.
width() / dst ) ) + 1, 1000 );
616 const int nYPoints = std::min(
static_cast< int >( std::ceil( ( yMax - yMin ) / dst ) ) + 1, 1000 );
623 QVector<double> x( nXPoints * nYPoints );
624 QVector<double> y( nXPoints * nYPoints );
625 QVector<double> z( nXPoints * nYPoints );
627 QgsDebugMsgLevel( QStringLiteral(
"Entering transformBoundingBox..." ), 4 );
631 const double dx = rect.
width() /
static_cast< double >( nXPoints - 1 );
632 const double dy = ( yMax - yMin ) /
static_cast< double >( nYPoints - 1 );
634 double pointY = yMin;
636 for (
int i = 0; i < nYPoints ; i++ )
642 for (
int j = 0; j < nXPoints; j++ )
644 x[( i * nXPoints ) + j] = pointX;
645 y[( i * nXPoints ) + j] = pointY;
647 z[( i * nXPoints ) + j] = 0.0;
658 transformCoords( nXPoints * nYPoints, x.data(), y.data(), z.data(), direction );
668 bool doHandle180Crossover =
false;
671 const double xMin = std::fmod( x[0], 180.0 );
672 const double xMax = std::fmod( x[nXPoints - 1], 180.0 );
673 if ( handle180Crossover
676 && xMin > 0.0 && xMin <= 180.0 && xMax < 0.0 && xMax >= -180.0 )
678 doHandle180Crossover =
true;
683 for (
int i = 0; i < nXPoints * nYPoints; i++ )
685 if ( !std::isfinite( x[i] ) || !std::isfinite( y[i] ) )
690 if ( doHandle180Crossover )
704 throw QgsCsException( QObject::tr(
"Could not transform bounding box to target CRS" ) );
707 if ( doHandle180Crossover )
728 if ( !d->mIsValid || d->mShortCircuit )
731 if ( !d->mSourceCRS.isValid() )
734 "The coordinates can not be reprojected. The CRS is: %1" )
735 .arg( d->mSourceCRS.toProj() ), QObject::tr(
"CRS" ) );
738 if ( !d->mDestCRS.isValid() )
741 "The coordinates can not be reprojected. The CRS is: %1" ).arg( d->mDestCRS.toProj() ), QObject::tr(
"CRS" ) );
745 std::vector< int > zNanPositions;
746 for (
int i = 0; i < numPoints; i++ )
748 if ( std::isnan( z[i] ) )
750 zNanPositions.push_back( i );
755 std::vector< double > xprev( numPoints );
756 memcpy( xprev.data(), x,
sizeof(
double ) * numPoints );
757 std::vector< double > yprev( numPoints );
758 memcpy( yprev.data(), y,
sizeof(
double ) * numPoints );
759 std::vector< double > zprev( numPoints );
760 memcpy( zprev.data(), z,
sizeof(
double ) * numPoints );
762 const bool useTime = !std::isnan( d->mDefaultTime );
763 std::vector< double > t( useTime ? numPoints : 0, d->mDefaultTime );
765#ifdef COORDINATE_TRANSFORM_VERBOSE
768 QgsDebugMsgLevel( QStringLiteral(
"[[[[[[ Number of points to transform: %1 ]]]]]]" ).arg( numPoints ), 2 );
773 QgsDebugMsgLevel( QStringLiteral(
"No QgsCoordinateTransformContext context set for transform" ), 4 );
780 ProjData projData = d->threadLocalProjData();
784 proj_errno_reset( projData );
786 x,
sizeof( double ), numPoints,
787 y,
sizeof(
double ), numPoints,
788 z,
sizeof( double ), numPoints,
789 useTime ? t.data() :
nullptr,
sizeof( double ), useTime ? numPoints : 0 );
799 if ( numPoints == 1 )
801 projResult = proj_errno( projData );
802 actualRes = projResult;
806 actualRes = proj_errno( projData );
808 if ( actualRes == 0 )
812 if ( std::any_of( x, x + numPoints, [](
double v ) {
return std::isinf( v ); } )
813 || std::any_of( y, y + numPoints, [](
double v ) {
return std::isinf( v ); } )
814 || std::any_of( z, z + numPoints, [](
double v ) {
return std::isinf( v ); } ) )
820 mFallbackOperationOccurred =
false;
822 && ( d->mAvailableOpCount > 1 || d->mAvailableOpCount == -1 )
823 && ( d->mAllowFallbackTransforms || mBallparkTransformsAreAppropriate ) )
826 if (
PJ *
transform = d->threadLocalFallbackProjData() )
831 xprev.data(),
sizeof( double ), numPoints,
832 yprev.data(),
sizeof( double ), numPoints,
833 zprev.data(),
sizeof( double ), numPoints,
834 useTime ? t.data() :
nullptr,
sizeof( double ), useTime ? numPoints : 0 );
843 if ( numPoints == 1 )
848 projResult = std::isinf( xprev[0] ) || std::isinf( yprev[0] ) || std::isinf( zprev[0] ) ? 1 : 0;
851 if ( projResult == 0 )
853 memcpy( x, xprev.data(),
sizeof(
double ) * numPoints );
854 memcpy( y, yprev.data(),
sizeof(
double ) * numPoints );
855 memcpy( z, zprev.data(),
sizeof(
double ) * numPoints );
856 mFallbackOperationOccurred =
true;
859 if ( !mBallparkTransformsAreAppropriate && !mDisableFallbackHandler && sFallbackOperationOccurredHandler )
861 sFallbackOperationOccurredHandler( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation );
863 const QString warning = QStringLiteral(
"A fallback coordinate operation was used between %1 and %2" ).arg( d->mSourceCRS.authid(),
864 d->mDestCRS.authid() );
865 qWarning(
"%s", warning.toLatin1().constData() );
871 for (
const int &pos : zNanPositions )
873 z[pos] = std::numeric_limits<double>::quiet_NaN();
876 if ( projResult != 0 )
881 for (
int i = 0; i < numPoints; ++i )
883 points += QStringLiteral(
"(%1, %2)\n" ).arg( x[i], 0,
'f' ).arg( y[i], 0,
'f' );
888 const QString msg = QObject::tr(
"%1 of\n"
893 projResult < 0 ? QString::fromUtf8( proj_errno_string( projResult ) ) : QObject::tr(
"Fallback transform failed" ) );
897 if ( msg != mLastError )
899 QgsDebugError(
"Projection failed emitting invalid transform signal: " + msg );
907#ifdef COORDINATE_TRANSFORM_VERBOSE
908 QgsDebugMsgLevel( QStringLiteral(
"[[[[[[ Projected %1, %2 to %3, %4 ]]]]]]" )
909 .arg( xorg, 0,
'g', 15 ).arg( yorg, 0,
'g', 15 )
910 .arg( *x, 0,
'g', 15 ).arg( *y, 0,
'g', 15 ), 2 );
921 return !d->mIsValid || d->mShortCircuit;
926 return d->mProjCoordinateOperation;
931 ProjData projData = d->threadLocalProjData();
938 d->mProjCoordinateOperation = operation;
939 d->mShouldReverseCoordinateOperation =
false;
945 d->mAllowFallbackTransforms = allowed;
950 return d->mAllowFallbackTransforms;
955 mBallparkTransformsAreAppropriate = appropriate;
960 mDisableFallbackHandler = disabled;
965 return mFallbackOperationOccurred;
972 proj = QApplication::applicationDirPath()
973 +
"/share/proj/" + QString( name );
977 return proj.toUtf8();
985 const QString sourceKey = src.
authid().isEmpty() ?
987 const QString destKey = dest.
authid().isEmpty() ?
990 if ( sourceKey.isEmpty() || destKey.isEmpty() )
997 const QList< QgsCoordinateTransform > values = sTransforms.values( qMakePair( sourceKey, destKey ) );
998 for (
auto valIt = values.constBegin(); valIt != values.constEnd(); ++valIt )
1000 if ( ( *valIt ).coordinateOperation() == coordinateOperationProj
1001 && ( *valIt ).allowFallbackTransforms() == allowFallback
1009 const bool hasContext = mHasContext;
1016 mHasContext = hasContext;
1025void QgsCoordinateTransform::addToCache()
1027 if ( !d->mSourceCRS.isValid() || !d->mDestCRS.isValid() )
1030 const QString sourceKey = d->mSourceCRS.authid().isEmpty() ?
1032 const QString destKey = d->mDestCRS.authid().isEmpty() ?
1035 if ( sourceKey.isEmpty() || destKey.isEmpty() )
1039 if ( sDisableCache )
1042 sTransforms.insert( qMakePair( sourceKey, destKey ), *
this );
1048 return d->mSourceDatumTransform;
1056 d->mSourceDatumTransform = dt;
1063 return d->mDestinationDatumTransform;
1071 d->mDestinationDatumTransform = dt;
1078 if ( sDisableCache )
1083 sDisableCache =
true;
1086 sTransforms.clear();
1089void QgsCoordinateTransform::removeFromCacheObjectsBelongingToCurrentThread(
void *pj_context )
1095 if ( sDisableCache )
1100 if ( sDisableCache )
1103 for (
auto it = sTransforms.begin(); it != sTransforms.end(); )
1105 auto &v = it.value();
1106 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
1107 it = sTransforms.erase( it );
1117 const double distSourceUnits = std::sqrt( source1.
sqrDist( source2 ) );
1120 const double distDestUnits = std::sqrt( dest1.
sqrDist( dest2 ) );
1121 return distDestUnits / distSourceUnits;
1126 QgsCoordinateTransformPrivate::setCustomMissingRequiredGridHandler( handler );
1131 QgsCoordinateTransformPrivate::setCustomMissingPreferredGridHandler( handler );
1136 QgsCoordinateTransformPrivate::setCustomCoordinateOperationCreationErrorHandler( handler );
1141 QgsCoordinateTransformPrivate::setCustomMissingGridUsedByContextHandler( handler );
1146 sFallbackOperationOccurredHandler = handler;
1151 QgsCoordinateTransformPrivate::setDynamicCrsToDynamicCrsWarningHandler( handler );
@ BallparkTransformsAreAppropriate
Indicates that approximate "ballpark" results are appropriate for this coordinate transform....
@ IgnoreImpossibleTransformations
Indicates that impossible transformations (such as those which attempt to transform between two diffe...
TransformDirection
Flags for raster layer temporal capabilities.
@ Forward
Forward transform (from source to destination)
@ Reverse
Reverse/inverse transform (from destination to source)
This class represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString celestialBodyName() const
Attempts to retrieve the name of the celestial body associated with the CRS (e.g.
@ WKT_PREFERRED
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
double coordinateEpoch() const
Returns the coordinate epoch, as a decimal year.
QString toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
Contains information about the context in which a coordinate transform is executed.
Custom exception class for Coordinate Reference System related exceptions.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
A class to represent a 2D point.
double sqrDist(double x, double y) const
Returns the squared distance between this point a specified x, y coordinate.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
QgsCoordinateTransformContext transformContext
The QgsReadWriteLocker class is a convenience class that simplifies locking and unlocking QReadWriteL...
A rectangle specified with double values.
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
double xMinimum() const
Returns the x minimum value (left side of rectangle).
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
void setXMinimum(double x)
Set the minimum x value.
double width() const
Returns the width of the rectangle.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
bool isNull() const
Test if the rectangle is null (holding no spatial information).
double yMaximum() const
Returns the y maximum value (top side of rectangle).
void setXMaximum(double x)
Set the maximum x value.
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
bool isEmpty() const
Returns true if the rectangle has no area.
void setNull()
Mark a rectangle as being null (holding no spatial information).
Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double precisi...
double y() const
Returns Y coordinate.
double z() const
Returns Z coordinate.
double x() const
Returns X coordinate.
#define Q_NOWARN_DEPRECATED_POP
#define Q_NOWARN_DEPRECATED_PUSH
bool qgsNanCompatibleEquals(double a, double b)
Compare two doubles, treating nan values as equal.
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)
const QgsCoordinateReferenceSystem & crs