32 #include <QDomElement>
33 #include <QApplication>
35 #include <QStringList>
49 QReadWriteLock QgsCoordinateTransform::sCacheLock;
51 bool QgsCoordinateTransform::sDisableCache =
false;
55 const QString &desiredOperation )> QgsCoordinateTransform::sFallbackOperationOccurredHandler =
nullptr;
59 d =
new QgsCoordinateTransformPrivate();
65 d =
new QgsCoordinateTransformPrivate( source, destination, mContext );
70 if ( !d->checkValidity() )
74 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
85 d =
new QgsCoordinateTransformPrivate( source, destination, mContext );
91 if ( !d->checkValidity() )
95 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
105 d =
new QgsCoordinateTransformPrivate( source, destination, sourceDatumTransform, destinationDatumTransform );
110 if ( !d->checkValidity() )
114 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
123 : mContext( o.mContext )
125 , mHasContext( o.mHasContext )
136 mHasContext = o.mHasContext;
138 mContext = o.mContext;
139 mLastError = QString();
149 if ( !d->checkValidity() )
152 d->calculateTransforms( mContext );
154 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
165 if ( !d->checkValidity() )
168 d->calculateTransforms( mContext );
170 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
185 if ( !d->checkValidity() )
188 d->calculateTransforms( mContext );
190 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
205 return d->mSourceCRS;
215 if ( !d->mIsValid || d->mShortCircuit )
219 double x = point.
x();
220 double y = point.
y();
253 if ( !d->mIsValid || d->mShortCircuit )
277 #ifdef COORDINATE_TRANSFORM_VERBOSE
278 QgsDebugMsg( QStringLiteral(
"Rect projection..." ) );
289 double x = point.
x();
290 double y = point.
y();
291 double z = point.
z();
308 if ( !d->mIsValid || d->mShortCircuit )
329 double xd =
static_cast< double >( x ), yd =
static_cast< double >( y );
338 if ( !d->mIsValid || d->mShortCircuit )
364 if ( !d->mIsValid || d->mShortCircuit )
370 const int nVertices = poly.size();
372 QVector<double> x( nVertices );
373 QVector<double> y( nVertices );
374 QVector<double> z( nVertices );
375 double *destX = x.data();
376 double *destY = y.data();
377 double *destZ = z.data();
379 const QPointF *polyData = poly.constData();
380 for (
int i = 0; i < nVertices; ++i )
382 *destX++ = polyData->x();
383 *destY++ = polyData->y();
399 QPointF *destPoint = poly.data();
400 const double *srcX = x.constData();
401 const double *srcY = y.constData();
402 for (
int i = 0; i < nVertices; ++i )
404 destPoint->rx() = *srcX++;
405 destPoint->ry() = *srcY++;
410 if ( !err.isEmpty() )
418 if ( !d->mIsValid || d->mShortCircuit )
421 Q_ASSERT( x.size() == y.size() );
444 if ( !d->mIsValid || d->mShortCircuit )
447 Q_ASSERT( x.size() == y.size() );
457 const int vectorSize = x.size();
458 QVector<double> xd( x.size() );
459 QVector<double> yd( y.size() );
460 QVector<double> zd( z.size() );
462 double *destX = xd.data();
463 double *destY = yd.data();
464 double *destZ = zd.data();
466 const float *srcX = x.constData();
467 const float *srcY = y.constData();
468 const float *srcZ = z.constData();
470 for (
int i = 0; i < vectorSize; ++i )
472 *destX++ =
static_cast< double >( *srcX++ );
473 *destY++ =
static_cast< double >( *srcY++ );
474 *destZ++ =
static_cast< double >( *srcZ++ );
480 float *destFX = x.data();
481 float *destFY = y.data();
482 float *destFZ = z.data();
483 const double *srcXD = xd.constData();
484 const double *srcYD = yd.constData();
485 const double *srcZD = zd.constData();
486 for (
int i = 0; i < vectorSize; ++i )
488 *destFX++ =
static_cast< float >( *srcXD++ );
489 *destFY++ =
static_cast< float >( *srcYD++ );
490 *destFZ++ =
static_cast< float >( *srcZD++ );
508 if ( !d->mIsValid || d->mShortCircuit )
519 if ( d->mGeographicToWebMercator &&
520 ( ( direction == Qgis::TransformDirection::Forward && !d->mIsReversed ) ||
521 ( direction == Qgis::TransformDirection::Reverse && d->mIsReversed ) ) )
528 constexpr
double EPS = 1e-1;
529 if ( yMin < -90 + EPS )
531 if ( yMax < -90 + EPS )
532 throw QgsCsException( QObject::tr(
"Could not transform bounding box to target CRS" ) );
535 if ( yMax > 90 - EPS )
537 if ( yMin > 90 - EPS )
538 throw QgsCsException( QObject::tr(
"Could not transform bounding box to target CRS" ) );
547 const int nPoints = 1000;
548 const double d = std::sqrt( ( rect.
width() * ( yMax - yMin ) ) / std::pow( std::sqrt(
static_cast< double >( nPoints ) ) - 1, 2.0 ) );
549 const int nXPoints = std::min(
static_cast< int >( std::ceil( rect.
width() / d ) ) + 1, 1000 );
550 const int nYPoints = std::min(
static_cast< int >( std::ceil( ( yMax - yMin ) / d ) ) + 1, 1000 );
557 QVector<double> x( nXPoints * nYPoints );
558 QVector<double> y( nXPoints * nYPoints );
559 QVector<double> z( nXPoints * nYPoints );
561 QgsDebugMsgLevel( QStringLiteral(
"Entering transformBoundingBox..." ), 4 );
565 const double dx = rect.
width() /
static_cast< double >( nXPoints - 1 );
566 const double dy = ( yMax - yMin ) /
static_cast< double >( nYPoints - 1 );
568 double pointY = yMin;
570 for (
int i = 0; i < nYPoints ; i++ )
576 for (
int j = 0; j < nXPoints; j++ )
578 x[( i * nXPoints ) + j] = pointX;
579 y[( i * nXPoints ) + j] = pointY;
581 z[( i * nXPoints ) + j] = 0.0;
592 transformCoords( nXPoints * nYPoints, x.data(), y.data(), z.data(), direction );
603 for (
int i = 0; i < nXPoints * nYPoints; i++ )
605 if ( !std::isfinite( x[i] ) || !std::isfinite( y[i] ) )
610 if ( handle180Crossover )
624 throw QgsCsException( QObject::tr(
"Could not transform bounding box to target CRS" ) );
627 if ( handle180Crossover )
648 if ( !d->mIsValid || d->mShortCircuit )
651 if ( !d->mSourceCRS.isValid() )
654 "The coordinates can not be reprojected. The CRS is: %1" )
655 .arg( d->mSourceCRS.toProj() ), QObject::tr(
"CRS" ) );
658 if ( !d->mDestCRS.isValid() )
661 "The coordinates can not be reprojected. The CRS is: %1" ).arg( d->mDestCRS.toProj() ), QObject::tr(
"CRS" ) );
665 std::vector< int > zNanPositions;
666 for (
int i = 0; i < numPoints; i++ )
668 if ( std::isnan( z[i] ) )
670 zNanPositions.push_back( i );
675 std::vector< double > xprev( numPoints );
676 memcpy( xprev.data(), x,
sizeof(
double ) * numPoints );
677 std::vector< double > yprev( numPoints );
678 memcpy( yprev.data(), y,
sizeof(
double ) * numPoints );
679 std::vector< double > zprev( numPoints );
680 memcpy( zprev.data(), z,
sizeof(
double ) * numPoints );
682 const bool useTime = !std::isnan( d->mDefaultTime );
683 std::vector< double > t( useTime ? numPoints : 0, d->mDefaultTime );
685 #ifdef COORDINATE_TRANSFORM_VERBOSE
688 QgsDebugMsg( QStringLiteral(
"[[[[[[ Number of points to transform: %1 ]]]]]]" ).arg( numPoints ) );
693 QgsDebugMsgLevel( QStringLiteral(
"No QgsCoordinateTransformContext context set for transform" ), 4 );
700 ProjData projData = d->threadLocalProjData();
704 proj_errno_reset( projData );
705 proj_trans_generic( projData, ( direction == Qgis::TransformDirection::Forward && !d->mIsReversed ) || ( direction == Qgis::TransformDirection::Reverse && d->mIsReversed ) ? PJ_FWD : PJ_INV,
706 x,
sizeof(
double ), numPoints,
707 y,
sizeof(
double ), numPoints,
708 z,
sizeof(
double ), numPoints,
709 useTime ? t.data() :
nullptr,
sizeof(
double ), useTime ? numPoints : 0 );
719 if ( numPoints == 1 )
721 projResult = proj_errno( projData );
722 actualRes = projResult;
726 actualRes = proj_errno( projData );
728 if ( actualRes == 0 )
732 if ( std::any_of( x, x + numPoints, [](
double v ) {
return std::isinf( v ); } )
733 || std::any_of( y, y + numPoints, [](
double v ) {
return std::isinf( v ); } )
734 || std::any_of( z, z + numPoints, [](
double v ) {
return std::isinf( v ); } ) )
740 mFallbackOperationOccurred =
false;
742 && ( d->mAvailableOpCount > 1 || d->mAvailableOpCount == -1 )
743 && ( d->mAllowFallbackTransforms || mBallparkTransformsAreAppropriate ) )
746 if (
PJ *
transform = d->threadLocalFallbackProjData() )
750 proj_trans_generic(
transform, direction == Qgis::TransformDirection::Forward ? PJ_FWD : PJ_INV,
751 xprev.data(),
sizeof(
double ), numPoints,
752 yprev.data(),
sizeof(
double ), numPoints,
753 zprev.data(),
sizeof(
double ), numPoints,
754 useTime ? t.data() :
nullptr,
sizeof(
double ), useTime ? numPoints : 0 );
763 if ( numPoints == 1 )
768 projResult = std::isinf( xprev[0] ) || std::isinf( yprev[0] ) || std::isinf( zprev[0] ) ? 1 : 0;
771 if ( projResult == 0 )
773 memcpy( x, xprev.data(),
sizeof(
double ) * numPoints );
774 memcpy( y, yprev.data(),
sizeof(
double ) * numPoints );
775 memcpy( z, zprev.data(),
sizeof(
double ) * numPoints );
776 mFallbackOperationOccurred =
true;
779 if ( !mBallparkTransformsAreAppropriate && !mDisableFallbackHandler && sFallbackOperationOccurredHandler )
781 sFallbackOperationOccurredHandler( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation );
783 const QString warning = QStringLiteral(
"A fallback coordinate operation was used between %1 and %2" ).arg( d->mSourceCRS.authid(),
784 d->mDestCRS.authid() );
785 qWarning(
"%s", warning.toLatin1().constData() );
791 for (
const int &pos : zNanPositions )
793 z[pos] = std::numeric_limits<double>::quiet_NaN();
796 if ( projResult != 0 )
801 for (
int i = 0; i < numPoints; ++i )
803 if ( direction == Qgis::TransformDirection::Forward )
805 points += QStringLiteral(
"(%1, %2)\n" ).arg( x[i], 0,
'f' ).arg( y[i], 0,
'f' );
809 points += QStringLiteral(
"(%1, %2)\n" ).arg( x[i], 0,
'f' ).arg( y[i], 0,
'f' );
813 const QString dir = ( direction == Qgis::TransformDirection::Forward ) ? QObject::tr(
"forward transform" ) : QObject::tr(
"inverse transform" );
815 const QString msg = QObject::tr(
"%1 of\n"
820 projResult < 0 ? QString::fromUtf8( proj_errno_string( projResult ) ) : QObject::tr(
"Fallback transform failed" ) );
824 if ( msg != mLastError )
826 QgsDebugMsg(
"Projection failed emitting invalid transform signal: " + msg );
834 #ifdef COORDINATE_TRANSFORM_VERBOSE
835 QgsDebugMsg( QStringLiteral(
"[[[[[[ Projected %1, %2 to %3, %4 ]]]]]]" )
836 .arg( xorg, 0,
'g', 15 ).arg( yorg, 0,
'g', 15 )
837 .arg( *x, 0,
'g', 15 ).arg( *y, 0,
'g', 15 ) );
848 return !d->mIsValid || d->mShortCircuit;
853 return d->mProjCoordinateOperation;
858 ProjData projData = d->threadLocalProjData();
865 d->mProjCoordinateOperation = operation;
866 d->mShouldReverseCoordinateOperation =
false;
872 d->mAllowFallbackTransforms = allowed;
877 return d->mAllowFallbackTransforms;
882 mBallparkTransformsAreAppropriate = appropriate;
887 mDisableFallbackHandler = disabled;
892 return mFallbackOperationOccurred;
899 proj = QApplication::applicationDirPath()
900 +
"/share/proj/" + QString( name );
904 return proj.toUtf8();
912 const QString sourceKey = src.
authid().isEmpty() ?
914 const QString destKey = dest.
authid().isEmpty() ?
917 if ( sourceKey.isEmpty() || destKey.isEmpty() )
924 const QList< QgsCoordinateTransform > values = sTransforms.values( qMakePair( sourceKey, destKey ) );
925 for (
auto valIt = values.constBegin(); valIt != values.constEnd(); ++valIt )
927 if ( ( *valIt ).coordinateOperation() == coordinateOperationProj
928 && ( *valIt ).allowFallbackTransforms() == allowFallback
936 const bool hasContext = mHasContext;
943 mHasContext = hasContext;
952 void QgsCoordinateTransform::addToCache()
954 if ( !d->mSourceCRS.isValid() || !d->mDestCRS.isValid() )
957 const QString sourceKey = d->mSourceCRS.authid().isEmpty() ?
959 const QString destKey = d->mDestCRS.authid().isEmpty() ?
962 if ( sourceKey.isEmpty() || destKey.isEmpty() )
969 sTransforms.insert( qMakePair( sourceKey, destKey ), *
this );
975 return d->mSourceDatumTransform;
983 d->mSourceDatumTransform = dt;
990 return d->mDestinationDatumTransform;
998 d->mDestinationDatumTransform = dt;
1005 if ( sDisableCache )
1010 sDisableCache =
true;
1013 sTransforms.clear();
1016 void QgsCoordinateTransform::removeFromCacheObjectsBelongingToCurrentThread(
void *pj_context )
1022 if ( sDisableCache )
1027 if ( sDisableCache )
1030 for (
auto it = sTransforms.begin(); it != sTransforms.end(); )
1032 auto &v = it.value();
1033 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
1034 it = sTransforms.erase( it );
1044 const double distSourceUnits = std::sqrt( source1.
sqrDist( source2 ) );
1047 const double distDestUnits = std::sqrt( dest1.
sqrDist( dest2 ) );
1048 return distDestUnits / distSourceUnits;
1053 QgsCoordinateTransformPrivate::setCustomMissingRequiredGridHandler( handler );
1058 QgsCoordinateTransformPrivate::setCustomMissingPreferredGridHandler( handler );
1063 QgsCoordinateTransformPrivate::setCustomCoordinateOperationCreationErrorHandler( handler );
1068 QgsCoordinateTransformPrivate::setCustomMissingGridUsedByContextHandler( handler );
1073 sFallbackOperationOccurredHandler = handler;
1078 QgsCoordinateTransformPrivate::setDynamicCrsToDynamicCrsWarningHandler( handler );
TransformDirection
Indicates the direction (forward or inverse) of a transform.
This class represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
@ 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 SIP_HOLDGIL
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 yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
void setXMaximum(double x) SIP_HOLDGIL
Set the maximum x value.
void setXMinimum(double x) SIP_HOLDGIL
Set the minimum x value.
void setMinimal() SIP_HOLDGIL
Set a rectangle so that min corner is at max and max corner is at min.
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
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