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 );
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 &&
579 ( ( direction == Qgis::TransformDirection::Forward && !d->mIsReversed ) ||
580 ( direction == Qgis::TransformDirection::Reverse && d->mIsReversed ) ) )
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 );
764 proj_trans_generic( projData, ( direction == Qgis::TransformDirection::Forward && !d->mIsReversed ) || ( direction == Qgis::TransformDirection::Reverse && d->mIsReversed ) ? PJ_FWD : PJ_INV,
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() )
809 proj_trans_generic(
transform, direction == Qgis::TransformDirection::Forward ? PJ_FWD : PJ_INV,
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 if ( direction == Qgis::TransformDirection::Forward )
864 points += QStringLiteral(
"(%1, %2)\n" ).arg( x[i], 0,
'f' ).arg( y[i], 0,
'f' );
868 points += QStringLiteral(
"(%1, %2)\n" ).arg( x[i], 0,
'f' ).arg( y[i], 0,
'f' );
872 const QString dir = ( direction == Qgis::TransformDirection::Forward ) ? QObject::tr(
"forward transform" ) : QObject::tr(
"inverse transform" );
874 const QString msg = QObject::tr(
"%1 of\n"
879 projResult < 0 ? QString::fromUtf8( proj_errno_string( projResult ) ) : QObject::tr(
"Fallback transform failed" ) );
883 if ( msg != mLastError )
885 QgsDebugMsg(
"Projection failed emitting invalid transform signal: " + msg );
893 #ifdef COORDINATE_TRANSFORM_VERBOSE
894 QgsDebugMsg( QStringLiteral(
"[[[[[[ Projected %1, %2 to %3, %4 ]]]]]]" )
895 .arg( xorg, 0,
'g', 15 ).arg( yorg, 0,
'g', 15 )
896 .arg( *x, 0,
'g', 15 ).arg( *y, 0,
'g', 15 ) );
907 return !d->mIsValid || d->mShortCircuit;
912 return d->mProjCoordinateOperation;
917 ProjData projData = d->threadLocalProjData();
924 d->mProjCoordinateOperation = operation;
925 d->mShouldReverseCoordinateOperation =
false;
931 d->mAllowFallbackTransforms = allowed;
936 return d->mAllowFallbackTransforms;
941 mBallparkTransformsAreAppropriate = appropriate;
946 mDisableFallbackHandler = disabled;
951 return mFallbackOperationOccurred;
958 proj = QApplication::applicationDirPath()
959 +
"/share/proj/" + QString( name );
963 return proj.toUtf8();
971 const QString sourceKey = src.
authid().isEmpty() ?
973 const QString destKey = dest.
authid().isEmpty() ?
976 if ( sourceKey.isEmpty() || destKey.isEmpty() )
983 const QList< QgsCoordinateTransform > values = sTransforms.values( qMakePair( sourceKey, destKey ) );
984 for (
auto valIt = values.constBegin(); valIt != values.constEnd(); ++valIt )
986 if ( ( *valIt ).coordinateOperation() == coordinateOperationProj
987 && ( *valIt ).allowFallbackTransforms() == allowFallback
995 const bool hasContext = mHasContext;
1002 mHasContext = hasContext;
1011 void QgsCoordinateTransform::addToCache()
1013 if ( !d->mSourceCRS.isValid() || !d->mDestCRS.isValid() )
1016 const QString sourceKey = d->mSourceCRS.authid().isEmpty() ?
1018 const QString destKey = d->mDestCRS.authid().isEmpty() ?
1021 if ( sourceKey.isEmpty() || destKey.isEmpty() )
1025 if ( sDisableCache )
1028 sTransforms.insert( qMakePair( sourceKey, destKey ), *
this );
1034 return d->mSourceDatumTransform;
1042 d->mSourceDatumTransform = dt;
1049 return d->mDestinationDatumTransform;
1057 d->mDestinationDatumTransform = dt;
1064 if ( sDisableCache )
1069 sDisableCache =
true;
1072 sTransforms.clear();
1075 void QgsCoordinateTransform::removeFromCacheObjectsBelongingToCurrentThread(
void *pj_context )
1081 if ( sDisableCache )
1086 if ( sDisableCache )
1089 for (
auto it = sTransforms.begin(); it != sTransforms.end(); )
1091 auto &v = it.value();
1092 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
1093 it = sTransforms.erase( it );
1103 const double distSourceUnits = std::sqrt( source1.
sqrDist( source2 ) );
1106 const double distDestUnits = std::sqrt( dest1.
sqrDist( dest2 ) );
1107 return distDestUnits / distSourceUnits;
1112 QgsCoordinateTransformPrivate::setCustomMissingRequiredGridHandler( handler );
1117 QgsCoordinateTransformPrivate::setCustomMissingPreferredGridHandler( handler );
1122 QgsCoordinateTransformPrivate::setCustomCoordinateOperationCreationErrorHandler( handler );
1127 QgsCoordinateTransformPrivate::setCustomMissingGridUsedByContextHandler( handler );
1132 sFallbackOperationOccurredHandler = handler;
1137 QgsCoordinateTransformPrivate::setDynamicCrsToDynamicCrsWarningHandler( handler );