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 )
161 mHasContext = o.mHasContext;
163 mContext = o.mContext;
164 mLastError = QString();
175#if PROJ_VERSION_MAJOR>8 || (PROJ_VERSION_MAJOR==8 && PROJ_VERSION_MINOR>=1)
194 if ( !d->checkValidity() )
197 d->calculateTransforms( mContext );
199 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
217 if ( !d->checkValidity() )
220 d->calculateTransforms( mContext );
222 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
244 if ( !d->checkValidity() )
247 d->calculateTransforms( mContext );
249 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
264 return d->mSourceCRS;
274 if ( !d->mIsValid || d->mShortCircuit )
278 double x = point.
x();
279 double y = point.
y();
312 if ( !d->mIsValid || d->mShortCircuit )
336#ifdef COORDINATE_TRANSFORM_VERBOSE
337 QgsDebugMsg( QStringLiteral(
"Rect projection..." ) );
348 double x = point.
x();
349 double y = point.
y();
350 double z = point.
z();
367 if ( !d->mIsValid || d->mShortCircuit )
388 double xd =
static_cast< double >( x ), yd =
static_cast< double >( y );
397 if ( !d->mIsValid || d->mShortCircuit )
423 if ( !d->mIsValid || d->mShortCircuit )
429 const int nVertices = poly.size();
431 QVector<double> x( nVertices );
432 QVector<double> y( nVertices );
433 QVector<double> z( nVertices );
434 double *destX = x.data();
435 double *destY = y.data();
436 double *destZ = z.data();
438 const QPointF *polyData = poly.constData();
439 for (
int i = 0; i < nVertices; ++i )
441 *destX++ = polyData->x();
442 *destY++ = polyData->y();
458 QPointF *destPoint = poly.data();
459 const double *srcX = x.constData();
460 const double *srcY = y.constData();
461 for (
int i = 0; i < nVertices; ++i )
463 destPoint->rx() = *srcX++;
464 destPoint->ry() = *srcY++;
469 if ( !err.isEmpty() )
477 if ( !d->mIsValid || d->mShortCircuit )
480 Q_ASSERT( x.size() == y.size() );
503 if ( !d->mIsValid || d->mShortCircuit )
506 Q_ASSERT( x.size() == y.size() );
516 const int vectorSize = x.size();
517 QVector<double> xd( x.size() );
518 QVector<double> yd( y.size() );
519 QVector<double> zd( z.size() );
521 double *destX = xd.data();
522 double *destY = yd.data();
523 double *destZ = zd.data();
525 const float *srcX = x.constData();
526 const float *srcY = y.constData();
527 const float *srcZ = z.constData();
529 for (
int i = 0; i < vectorSize; ++i )
531 *destX++ =
static_cast< double >( *srcX++ );
532 *destY++ =
static_cast< double >( *srcY++ );
533 *destZ++ =
static_cast< double >( *srcZ++ );
539 float *destFX = x.data();
540 float *destFY = y.data();
541 float *destFZ = z.data();
542 const double *srcXD = xd.constData();
543 const double *srcYD = yd.constData();
544 const double *srcZD = zd.constData();
545 for (
int i = 0; i < vectorSize; ++i )
547 *destFX++ =
static_cast< float >( *srcXD++ );
548 *destFY++ =
static_cast< float >( *srcYD++ );
549 *destFZ++ =
static_cast< float >( *srcZD++ );
567 if ( !d->mIsValid || d->mShortCircuit )
578 if ( d->mGeographicToWebMercator &&
587 constexpr double EPS = 1e-1;
588 if ( yMin < -90 + EPS )
590 if ( yMax < -90 + EPS )
591 throw QgsCsException( QObject::tr(
"Could not transform bounding box to target CRS" ) );
594 if ( yMax > 90 - EPS )
596 if ( yMin > 90 - EPS )
597 throw QgsCsException( QObject::tr(
"Could not transform bounding box to target CRS" ) );
606 const int nPoints = 1000;
607 const double d = std::sqrt( ( rect.
width() * ( yMax - yMin ) ) / std::pow( std::sqrt(
static_cast< double >( nPoints ) ) - 1, 2.0 ) );
608 const int nXPoints = std::min(
static_cast< int >( std::ceil( rect.
width() / d ) ) + 1, 1000 );
609 const int nYPoints = std::min(
static_cast< int >( std::ceil( ( yMax - yMin ) / d ) ) + 1, 1000 );
616 QVector<double> x( nXPoints * nYPoints );
617 QVector<double> y( nXPoints * nYPoints );
618 QVector<double> z( nXPoints * nYPoints );
620 QgsDebugMsgLevel( QStringLiteral(
"Entering transformBoundingBox..." ), 4 );
624 const double dx = rect.
width() /
static_cast< double >( nXPoints - 1 );
625 const double dy = ( yMax - yMin ) /
static_cast< double >( nYPoints - 1 );
627 double pointY = yMin;
629 for (
int i = 0; i < nYPoints ; i++ )
635 for (
int j = 0; j < nXPoints; j++ )
637 x[( i * nXPoints ) + j] = pointX;
638 y[( i * nXPoints ) + j] = pointY;
640 z[( i * nXPoints ) + j] = 0.0;
651 transformCoords( nXPoints * nYPoints, x.data(), y.data(), z.data(), direction );
662 for (
int i = 0; i < nXPoints * nYPoints; i++ )
664 if ( !std::isfinite( x[i] ) || !std::isfinite( y[i] ) )
669 if ( handle180Crossover )
683 throw QgsCsException( QObject::tr(
"Could not transform bounding box to target CRS" ) );
686 if ( handle180Crossover )
707 if ( !d->mIsValid || d->mShortCircuit )
710 if ( !d->mSourceCRS.isValid() )
713 "The coordinates can not be reprojected. The CRS is: %1" )
714 .arg( d->mSourceCRS.toProj() ), QObject::tr(
"CRS" ) );
717 if ( !d->mDestCRS.isValid() )
720 "The coordinates can not be reprojected. The CRS is: %1" ).arg( d->mDestCRS.toProj() ), QObject::tr(
"CRS" ) );
724 std::vector< int > zNanPositions;
725 for (
int i = 0; i < numPoints; i++ )
727 if ( std::isnan( z[i] ) )
729 zNanPositions.push_back( i );
734 std::vector< double > xprev( numPoints );
735 memcpy( xprev.data(), x,
sizeof(
double ) * numPoints );
736 std::vector< double > yprev( numPoints );
737 memcpy( yprev.data(), y,
sizeof(
double ) * numPoints );
738 std::vector< double > zprev( numPoints );
739 memcpy( zprev.data(), z,
sizeof(
double ) * numPoints );
741 const bool useTime = !std::isnan( d->mDefaultTime );
742 std::vector< double > t( useTime ? numPoints : 0, d->mDefaultTime );
744#ifdef COORDINATE_TRANSFORM_VERBOSE
747 QgsDebugMsg( QStringLiteral(
"[[[[[[ Number of points to transform: %1 ]]]]]]" ).arg( numPoints ) );
752 QgsDebugMsgLevel( QStringLiteral(
"No QgsCoordinateTransformContext context set for transform" ), 4 );
759 ProjData projData = d->threadLocalProjData();
763 proj_errno_reset( projData );
765 x,
sizeof( double ), numPoints,
766 y,
sizeof(
double ), numPoints,
767 z,
sizeof( double ), numPoints,
768 useTime ? t.data() :
nullptr,
sizeof( double ), useTime ? numPoints : 0 );
778 if ( numPoints == 1 )
780 projResult = proj_errno( projData );
781 actualRes = projResult;
785 actualRes = proj_errno( projData );
787 if ( actualRes == 0 )
791 if ( std::any_of( x, x + numPoints, [](
double v ) {
return std::isinf( v ); } )
792 || std::any_of( y, y + numPoints, [](
double v ) {
return std::isinf( v ); } )
793 || std::any_of( z, z + numPoints, [](
double v ) {
return std::isinf( v ); } ) )
799 mFallbackOperationOccurred =
false;
801 && ( d->mAvailableOpCount > 1 || d->mAvailableOpCount == -1 )
802 && ( d->mAllowFallbackTransforms || mBallparkTransformsAreAppropriate ) )
805 if (
PJ *
transform = d->threadLocalFallbackProjData() )
810 xprev.data(),
sizeof( double ), numPoints,
811 yprev.data(),
sizeof( double ), numPoints,
812 zprev.data(),
sizeof( double ), numPoints,
813 useTime ? t.data() :
nullptr,
sizeof( double ), useTime ? numPoints : 0 );
822 if ( numPoints == 1 )
827 projResult = std::isinf( xprev[0] ) || std::isinf( yprev[0] ) || std::isinf( zprev[0] ) ? 1 : 0;
830 if ( projResult == 0 )
832 memcpy( x, xprev.data(),
sizeof(
double ) * numPoints );
833 memcpy( y, yprev.data(),
sizeof(
double ) * numPoints );
834 memcpy( z, zprev.data(),
sizeof(
double ) * numPoints );
835 mFallbackOperationOccurred =
true;
838 if ( !mBallparkTransformsAreAppropriate && !mDisableFallbackHandler && sFallbackOperationOccurredHandler )
840 sFallbackOperationOccurredHandler( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation );
842 const QString warning = QStringLiteral(
"A fallback coordinate operation was used between %1 and %2" ).arg( d->mSourceCRS.authid(),
843 d->mDestCRS.authid() );
844 qWarning(
"%s", warning.toLatin1().constData() );
850 for (
const int &pos : zNanPositions )
852 z[pos] = std::numeric_limits<double>::quiet_NaN();
855 if ( projResult != 0 )
860 for (
int i = 0; i < numPoints; ++i )
862 points += QStringLiteral(
"(%1, %2)\n" ).arg( x[i], 0,
'f' ).arg( y[i], 0,
'f' );
867 const QString msg = QObject::tr(
"%1 of\n"
872 projResult < 0 ? QString::fromUtf8( proj_errno_string( projResult ) ) : QObject::tr(
"Fallback transform failed" ) );
876 if ( msg != mLastError )
878 QgsDebugMsg(
"Projection failed emitting invalid transform signal: " + msg );
886#ifdef COORDINATE_TRANSFORM_VERBOSE
887 QgsDebugMsg( QStringLiteral(
"[[[[[[ Projected %1, %2 to %3, %4 ]]]]]]" )
888 .arg( xorg, 0,
'g', 15 ).arg( yorg, 0,
'g', 15 )
889 .arg( *x, 0,
'g', 15 ).arg( *y, 0,
'g', 15 ) );
900 return !d->mIsValid || d->mShortCircuit;
905 return d->mProjCoordinateOperation;
910 ProjData projData = d->threadLocalProjData();
917 d->mProjCoordinateOperation = operation;
918 d->mShouldReverseCoordinateOperation =
false;
924 d->mAllowFallbackTransforms = allowed;
929 return d->mAllowFallbackTransforms;
934 mBallparkTransformsAreAppropriate = appropriate;
939 mDisableFallbackHandler = disabled;
944 return mFallbackOperationOccurred;
951 proj = QApplication::applicationDirPath()
952 +
"/share/proj/" + QString( name );
956 return proj.toUtf8();
964 const QString sourceKey = src.
authid().isEmpty() ?
966 const QString destKey = dest.
authid().isEmpty() ?
969 if ( sourceKey.isEmpty() || destKey.isEmpty() )
976 const QList< QgsCoordinateTransform > values = sTransforms.values( qMakePair( sourceKey, destKey ) );
977 for (
auto valIt = values.constBegin(); valIt != values.constEnd(); ++valIt )
979 if ( ( *valIt ).coordinateOperation() == coordinateOperationProj
980 && ( *valIt ).allowFallbackTransforms() == allowFallback
988 const bool hasContext = mHasContext;
995 mHasContext = hasContext;
1004void QgsCoordinateTransform::addToCache()
1006 if ( !d->mSourceCRS.isValid() || !d->mDestCRS.isValid() )
1009 const QString sourceKey = d->mSourceCRS.authid().isEmpty() ?
1011 const QString destKey = d->mDestCRS.authid().isEmpty() ?
1014 if ( sourceKey.isEmpty() || destKey.isEmpty() )
1018 if ( sDisableCache )
1021 sTransforms.insert( qMakePair( sourceKey, destKey ), *
this );
1027 return d->mSourceDatumTransform;
1035 d->mSourceDatumTransform = dt;
1042 return d->mDestinationDatumTransform;
1050 d->mDestinationDatumTransform = dt;
1057 if ( sDisableCache )
1062 sDisableCache =
true;
1065 sTransforms.clear();
1068void QgsCoordinateTransform::removeFromCacheObjectsBelongingToCurrentThread(
void *pj_context )
1074 if ( sDisableCache )
1079 if ( sDisableCache )
1082 for (
auto it = sTransforms.begin(); it != sTransforms.end(); )
1084 auto &v = it.value();
1085 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
1086 it = sTransforms.erase( it );
1096 const double distSourceUnits = std::sqrt( source1.
sqrDist( source2 ) );
1099 const double distDestUnits = std::sqrt( dest1.
sqrDist( dest2 ) );
1100 return distDestUnits / distSourceUnits;
1105 QgsCoordinateTransformPrivate::setCustomMissingRequiredGridHandler( handler );
1110 QgsCoordinateTransformPrivate::setCustomMissingPreferredGridHandler( handler );
1115 QgsCoordinateTransformPrivate::setCustomCoordinateOperationCreationErrorHandler( handler );
1120 QgsCoordinateTransformPrivate::setCustomMissingGridUsedByContextHandler( handler );
1125 sFallbackOperationOccurredHandler = handler;
1130 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 setMinimal()
Set a rectangle so that min corner is at max and max corner is at min.
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 (all coordinates zero or after call to setMinimal()).
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 is empty.
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)
const QgsCoordinateReferenceSystem & crs