30 #include <QDomElement>
31 #include <QApplication>
33 #include <QStringList>
36 #if PROJ_VERSION_MAJOR>=6
51 QReadWriteLock QgsCoordinateTransform::sCacheLock;
53 bool QgsCoordinateTransform::sDisableCache =
false;
57 const QString &desiredOperation )> QgsCoordinateTransform::sFallbackOperationOccurredHandler =
nullptr;
61 d =
new QgsCoordinateTransformPrivate();
67 d =
new QgsCoordinateTransformPrivate( source, destination, mContext );
72 if ( !d->checkValidity() )
76 #if PROJ_VERSION_MAJOR>=6
77 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
79 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
91 d =
new QgsCoordinateTransformPrivate( source, destination, mContext );
97 if ( !d->checkValidity() )
101 #if PROJ_VERSION_MAJOR>=6
102 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
104 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
115 d =
new QgsCoordinateTransformPrivate( source, destination, sourceDatumTransform, destinationDatumTransform );
120 if ( !d->checkValidity() )
124 #if PROJ_VERSION_MAJOR>=6
125 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
127 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
137 : mContext( o.mContext )
139 , mHasContext( o.mHasContext )
150 mHasContext = o.mHasContext;
152 mContext = o.mContext;
153 mLastError = QString();
163 if ( !d->checkValidity() )
166 d->calculateTransforms( mContext );
168 #if PROJ_VERSION_MAJOR>=6
169 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
171 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
183 if ( !d->checkValidity() )
186 d->calculateTransforms( mContext );
188 #if PROJ_VERSION_MAJOR>=6
189 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
191 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
207 if ( !d->checkValidity() )
210 d->calculateTransforms( mContext );
212 #if PROJ_VERSION_MAJOR>=6
213 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
215 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
231 return d->mSourceCRS;
241 if ( !d->mIsValid || d->mShortCircuit )
245 double x = point.
x();
246 double y = point.
y();
279 if ( !d->mIsValid || d->mShortCircuit )
303 #ifdef COORDINATE_TRANSFORM_VERBOSE
304 QgsDebugMsg( QStringLiteral(
"Rect projection..." ) );
316 if ( !d->mIsValid || d->mShortCircuit )
337 double xd =
static_cast< double >( x ), yd =
static_cast< double >( y );
346 if ( !d->mIsValid || d->mShortCircuit )
372 if ( !d->mIsValid || d->mShortCircuit )
378 int nVertices = poly.size();
380 QVector<double> x( nVertices );
381 QVector<double> y( nVertices );
382 QVector<double> z( nVertices );
383 double *destX = x.data();
384 double *destY = y.data();
385 double *destZ = z.data();
387 const QPointF *polyData = poly.constData();
388 for (
int i = 0; i < nVertices; ++i )
390 *destX++ = polyData->x();
391 *destY++ = polyData->y();
407 QPointF *destPoint = poly.data();
408 const double *srcX = x.constData();
409 const double *srcY = y.constData();
410 for (
int i = 0; i < nVertices; ++i )
412 destPoint->rx() = *srcX++;
413 destPoint->ry() = *srcY++;
418 if ( !err.isEmpty() )
423 QVector<double> &x, QVector<double> &y, QVector<double> &z,
427 if ( !d->mIsValid || d->mShortCircuit )
430 Q_ASSERT( x.size() == y.size() );
451 QVector<float> &x, QVector<float> &y, QVector<float> &z,
454 if ( !d->mIsValid || d->mShortCircuit )
457 Q_ASSERT( x.size() == y.size() );
467 int vectorSize = x.size();
468 QVector<double> xd( x.size() );
469 QVector<double> yd( y.size() );
470 QVector<double> zd( z.size() );
472 double *destX = xd.data();
473 double *destY = yd.data();
474 double *destZ = zd.data();
476 const float *srcX = x.constData();
477 const float *srcY = y.constData();
478 const float *srcZ = z.constData();
480 for (
int i = 0; i < vectorSize; ++i )
482 *destX++ =
static_cast< double >( *srcX++ );
483 *destY++ =
static_cast< double >( *srcY++ );
484 *destZ++ =
static_cast< double >( *srcZ++ );
490 float *destFX = x.data();
491 float *destFY = y.data();
492 float *destFZ = z.data();
493 const double *srcXD = xd.constData();
494 const double *srcYD = yd.constData();
495 const double *srcZD = zd.constData();
496 for (
int i = 0; i < vectorSize; ++i )
498 *destFX++ =
static_cast< float >( *srcXD++ );
499 *destFY++ =
static_cast< float >( *srcYD++ );
500 *destFZ++ =
static_cast< float >( *srcZD++ );
518 if ( !d->mIsValid || d->mShortCircuit )
531 const int nPoints = 1000;
532 double d = std::sqrt( ( rect.
width() * rect.
height() ) / std::pow( std::sqrt(
static_cast< double >( nPoints ) ) - 1, 2.0 ) );
533 int nXPoints = std::min(
static_cast< int >( std::ceil( rect.
width() / d ) ) + 1, 1000 );
534 int nYPoints = std::min(
static_cast< int >( std::ceil( rect.
height() / d ) ) + 1, 1000 );
541 QVector<double> x( nXPoints * nYPoints );
542 QVector<double> y( nXPoints * nYPoints );
543 QVector<double> z( nXPoints * nYPoints );
545 QgsDebugMsgLevel( QStringLiteral(
"Entering transformBoundingBox..." ), 4 );
549 double dx = rect.
width() /
static_cast< double >( nXPoints - 1 );
550 double dy = rect.
height() /
static_cast< double >( nYPoints - 1 );
554 for (
int i = 0; i < nYPoints ; i++ )
560 for (
int j = 0; j < nXPoints; j++ )
562 x[( i * nXPoints ) + j] = pointX;
563 y[( i * nXPoints ) + j] = pointY;
565 z[( i * nXPoints ) + j] = 0.0;
576 transformCoords( nXPoints * nYPoints, x.data(), y.data(), z.data(), direction );
587 for (
int i = 0; i < nXPoints * nYPoints; i++ )
589 if ( !std::isfinite( x[i] ) || !std::isfinite( y[i] ) )
594 if ( handle180Crossover )
608 throw QgsCsException( QObject::tr(
"Could not transform bounding box to target CRS" ) );
611 if ( handle180Crossover )
632 if ( !d->mIsValid || d->mShortCircuit )
635 if ( !d->mSourceCRS.isValid() )
638 "The coordinates can not be reprojected. The CRS is: %1" )
639 .arg( d->mSourceCRS.toProj() ), QObject::tr(
"CRS" ) );
642 if ( !d->mDestCRS.isValid() )
645 "The coordinates can not be reprojected. The CRS is: %1" ).arg( d->mDestCRS.toProj() ), QObject::tr(
"CRS" ) );
649 #if PROJ_VERSION_MAJOR>=6
650 std::vector< double > xprev( numPoints );
651 memcpy( xprev.data(), x,
sizeof(
double ) * numPoints );
652 std::vector< double > yprev( numPoints );
653 memcpy( yprev.data(), y,
sizeof(
double ) * numPoints );
654 std::vector< double > zprev( numPoints );
655 memcpy( zprev.data(), z,
sizeof(
double ) * numPoints );
658 #ifdef COORDINATE_TRANSFORM_VERBOSE
661 QgsDebugMsg( QStringLiteral(
"[[[[[[ Number of points to transform: %1 ]]]]]]" ).arg( numPoints ) );
666 QgsDebugMsgLevel( QStringLiteral(
"No QgsCoordinateTransformContext context set for transform" ), 4 );
673 ProjData projData = d->threadLocalProjData();
676 #if PROJ_VERSION_MAJOR>=6
677 proj_errno_reset( projData );
678 proj_trans_generic( projData, ( direction ==
ForwardTransform && !d->mIsReversed ) || ( direction ==
ReverseTransform && d->mIsReversed ) ? PJ_FWD : PJ_INV,
679 x,
sizeof(
double ), numPoints,
680 y,
sizeof(
double ), numPoints,
681 z,
sizeof(
double ), numPoints,
682 nullptr,
sizeof(
double ), 0 );
692 if ( numPoints == 1 )
694 projResult = proj_errno( projData );
695 actualRes = projResult;
699 actualRes = proj_errno( projData );
701 if ( actualRes == 0 )
705 if ( std::any_of( x, x + numPoints, [](
double v ) {
return std::isinf( v ); } )
706 || std::any_of( y, y + numPoints, [](
double v ) {
return std::isinf( v ); } )
707 || std::any_of( z, z + numPoints, [](
double v ) {
return std::isinf( v ); } ) )
713 bool sourceIsLatLong =
false;
714 bool destIsLatLong =
false;
716 projPJ sourceProj = projData.first;
717 projPJ destProj = projData.second;
718 sourceIsLatLong = pj_is_latlong( sourceProj );
719 destIsLatLong = pj_is_latlong( destProj );
724 for (
int i = 0; i < numPoints; ++i )
732 #if PROJ_VERSION_MAJOR<6
735 projResult = pj_transform( destProj, sourceProj, numPoints, 0, x, y, z );
739 Q_ASSERT( sourceProj );
740 Q_ASSERT( destProj );
741 projResult = pj_transform( sourceProj, destProj, numPoints, 0, x, y, z );
745 #if PROJ_VERSION_MAJOR>=6
747 mFallbackOperationOccurred =
false;
749 && ( d->mAvailableOpCount > 1 || d->mAvailableOpCount == -1 )
750 && ( d->mAllowFallbackTransforms || mBallparkTransformsAreAppropriate ) )
753 if ( PJ *
transform = d->threadLocalFallbackProjData() )
758 xprev.data(),
sizeof(
double ), numPoints,
759 yprev.data(),
sizeof(
double ), numPoints,
760 zprev.data(),
sizeof(
double ), numPoints,
761 nullptr,
sizeof(
double ), 0 );
770 if ( numPoints == 1 )
775 projResult = std::isinf( xprev[0] ) || std::isinf( yprev[0] ) || std::isinf( zprev[0] ) ? 1 : 0;
778 if ( projResult == 0 )
780 memcpy( x, xprev.data(),
sizeof(
double ) * numPoints );
781 memcpy( y, yprev.data(),
sizeof(
double ) * numPoints );
782 memcpy( z, zprev.data(),
sizeof(
double ) * numPoints );
783 mFallbackOperationOccurred =
true;
786 if ( !mBallparkTransformsAreAppropriate && !mDisableFallbackHandler && sFallbackOperationOccurredHandler )
788 sFallbackOperationOccurredHandler( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation );
790 const QString warning = QStringLiteral(
"A fallback coordinate operation was used between %1 and %2" ).arg( d->mSourceCRS.authid(),
791 d->mDestCRS.authid() );
792 qWarning(
"%s", warning.toLatin1().constData() );
798 if ( projResult != 0 )
803 for (
int i = 0; i < numPoints; ++i )
807 points += QStringLiteral(
"(%1, %2)\n" ).arg( x[i], 0,
'f' ).arg( y[i], 0,
'f' );
811 #if PROJ_VERSION_MAJOR>=6
812 points += QStringLiteral(
"(%1, %2)\n" ).arg( x[i], 0,
'f' ).arg( y[i], 0,
'f' );
814 points += QStringLiteral(
"(%1, %2)\n" ).arg( x[i] * RAD_TO_DEG, 0,
'f' ).arg( y[i] * RAD_TO_DEG, 0,
'f' );
819 QString dir = ( direction ==
ForwardTransform ) ? QObject::tr(
"forward transform" ) : QObject::tr(
"inverse transform" );
821 #if PROJ_VERSION_MAJOR>=6
822 QString msg = QObject::tr(
"%1 of\n"
827 projResult < 0 ? QString::fromUtf8( proj_errno_string( projResult ) ) : QObject::tr(
"Fallback transform failed" ) );
829 char *srcdef = pj_get_def( sourceProj, 0 );
830 char *dstdef = pj_get_def( destProj, 0 );
832 QString msg = QObject::tr(
"%1 of\n"
839 QString::fromUtf8( pj_strerrno( projResult ) ) );
846 if ( msg != mLastError )
848 QgsDebugMsg(
"Projection failed emitting invalid transform signal: " + msg );
856 #if PROJ_VERSION_MAJOR<6
862 for (
int i = 0; i < numPoints; ++i )
869 #ifdef COORDINATE_TRANSFORM_VERBOSE
870 QgsDebugMsg( QStringLiteral(
"[[[[[[ Projected %1, %2 to %3, %4 ]]]]]]" )
871 .arg( xorg, 0,
'g', 15 ).arg( yorg, 0,
'g', 15 )
872 .arg( *x, 0,
'g', 15 ).arg( *y, 0,
'g', 15 ) );
883 return !d->mIsValid || d->mShortCircuit;
888 return d->mProjCoordinateOperation;
893 #if PROJ_VERSION_MAJOR>=6
894 ProjData projData = d->threadLocalProjData();
895 return QgsDatumTransform::transformDetailsFromPj( projData );
904 d->mProjCoordinateOperation = operation;
905 d->mShouldReverseCoordinateOperation =
false;
911 d->mAllowFallbackTransforms = allowed;
916 return d->mAllowFallbackTransforms;
921 mBallparkTransformsAreAppropriate = appropriate;
926 mDisableFallbackHandler = disabled;
931 return mFallbackOperationOccurred;
938 proj = QApplication::applicationDirPath()
939 +
"/share/proj/" + QString( name );
943 return proj.toUtf8();
946 #if PROJ_VERSION_MAJOR>=6
952 const QString sourceKey = src.
authid().isEmpty() ?
954 const QString destKey = dest.
authid().isEmpty() ?
957 if ( sourceKey.isEmpty() || destKey.isEmpty() )
964 const QList< QgsCoordinateTransform > values = sTransforms.values( qMakePair( sourceKey, destKey ) );
965 for (
auto valIt = values.constBegin(); valIt != values.constEnd(); ++valIt )
967 if ( ( *valIt ).coordinateOperation() == coordinateOperationProj && ( *valIt ).allowFallbackTransforms() == allowFallback )
972 bool hasContext = mHasContext;
979 mHasContext = hasContext;
993 const QString sourceKey = src.
authid().isEmpty() ?
995 const QString destKey = dest.
authid().isEmpty() ?
998 if ( sourceKey.isEmpty() || destKey.isEmpty() )
1002 if ( sDisableCache )
1005 const QList< QgsCoordinateTransform > values = sTransforms.values( qMakePair( src.
authid(), dest.
authid() ) );
1006 for (
auto valIt = values.constBegin(); valIt != values.constEnd(); ++valIt )
1009 if ( ( *valIt ).sourceDatumTransformId() == srcDatumTransform &&
1010 ( *valIt ).destinationDatumTransformId() == destDatumTransform )
1015 bool hasContext = mHasContext;
1022 mHasContext = hasContext;
1033 void QgsCoordinateTransform::addToCache()
1035 if ( !d->mSourceCRS.isValid() || !d->mDestCRS.isValid() )
1038 const QString sourceKey = d->mSourceCRS.authid().isEmpty() ?
1040 const QString destKey = d->mDestCRS.authid().isEmpty() ?
1043 if ( sourceKey.isEmpty() || destKey.isEmpty() )
1047 if ( sDisableCache )
1050 sTransforms.insertMulti( qMakePair( sourceKey, destKey ), *
this );
1056 return d->mSourceDatumTransform;
1064 d->mSourceDatumTransform = dt;
1071 return d->mDestinationDatumTransform;
1079 d->mDestinationDatumTransform = dt;
1086 if ( sDisableCache )
1091 sDisableCache =
true;
1094 sTransforms.clear();
1097 #if PROJ_VERSION_MAJOR>=6
1098 void QgsCoordinateTransform::removeFromCacheObjectsBelongingToCurrentThread(
void *pj_context )
1104 if ( sDisableCache )
1109 if ( sDisableCache )
1112 for (
auto it = sTransforms.begin(); it != sTransforms.end(); )
1114 auto &v = it.value();
1115 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
1116 it = sTransforms.erase( it );
1127 double distSourceUnits = std::sqrt( source1.
sqrDist( source2 ) );
1130 double distDestUnits = std::sqrt( dest1.
sqrDist( dest2 ) );
1131 return distDestUnits / distSourceUnits;
1136 QgsCoordinateTransformPrivate::setCustomMissingRequiredGridHandler( handler );
1141 QgsCoordinateTransformPrivate::setCustomMissingPreferredGridHandler( handler );
1146 QgsCoordinateTransformPrivate::setCustomCoordinateOperationCreationErrorHandler( handler );
1151 QgsCoordinateTransformPrivate::setCustomMissingGridUsedByContextHandler( handler );
1156 sFallbackOperationOccurredHandler = handler;