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 );
@ Preferred
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
@ 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 toWkt(Qgis::CrsWktVariant variant=Qgis::CrsWktVariant::Wkt1Gdal, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
QString celestialBodyName() const
Attempts to retrieve the name of the celestial body associated with the CRS (e.g.
double coordinateEpoch() const
Returns the coordinate epoch, as a decimal year.
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