31 #include <QDomElement>
32 #include <QApplication>
34 #include <QStringList>
37 #if PROJ_VERSION_MAJOR>=6
52 QReadWriteLock QgsCoordinateTransform::sCacheLock;
54 bool QgsCoordinateTransform::sDisableCache =
false;
58 const QString &desiredOperation )> QgsCoordinateTransform::sFallbackOperationOccurredHandler =
nullptr;
62 d =
new QgsCoordinateTransformPrivate();
68 d =
new QgsCoordinateTransformPrivate( source, destination, mContext );
73 if ( !d->checkValidity() )
77 #if PROJ_VERSION_MAJOR>=6
78 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
80 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
92 d =
new QgsCoordinateTransformPrivate( source, destination, mContext );
98 if ( !d->checkValidity() )
102 #if PROJ_VERSION_MAJOR>=6
103 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
105 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
116 d =
new QgsCoordinateTransformPrivate( source, destination, sourceDatumTransform, destinationDatumTransform );
121 if ( !d->checkValidity() )
125 #if PROJ_VERSION_MAJOR>=6
126 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
128 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
138 : mContext( o.mContext )
140 , mHasContext( o.mHasContext )
151 mHasContext = o.mHasContext;
153 mContext = o.mContext;
154 mLastError = QString();
164 if ( !d->checkValidity() )
167 d->calculateTransforms( mContext );
169 #if PROJ_VERSION_MAJOR>=6
170 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
172 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
184 if ( !d->checkValidity() )
187 d->calculateTransforms( mContext );
189 #if PROJ_VERSION_MAJOR>=6
190 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
192 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
208 if ( !d->checkValidity() )
211 d->calculateTransforms( mContext );
213 #if PROJ_VERSION_MAJOR>=6
214 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
216 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
232 return d->mSourceCRS;
242 if ( !d->mIsValid || d->mShortCircuit )
246 double x = point.
x();
247 double y = point.
y();
280 if ( !d->mIsValid || d->mShortCircuit )
304 #ifdef COORDINATE_TRANSFORM_VERBOSE
305 QgsDebugMsg( QStringLiteral(
"Rect projection..." ) );
316 double x = point.
x();
317 double y = point.
y();
318 double z = point.
z();
335 if ( !d->mIsValid || d->mShortCircuit )
356 double xd =
static_cast< double >( x ), yd =
static_cast< double >( y );
365 if ( !d->mIsValid || d->mShortCircuit )
391 if ( !d->mIsValid || d->mShortCircuit )
397 int nVertices = poly.size();
399 QVector<double> x( nVertices );
400 QVector<double> y( nVertices );
401 QVector<double> z( nVertices );
402 double *destX = x.data();
403 double *destY = y.data();
404 double *destZ = z.data();
406 const QPointF *polyData = poly.constData();
407 for (
int i = 0; i < nVertices; ++i )
409 *destX++ = polyData->x();
410 *destY++ = polyData->y();
426 QPointF *destPoint = poly.data();
427 const double *srcX = x.constData();
428 const double *srcY = y.constData();
429 for (
int i = 0; i < nVertices; ++i )
431 destPoint->rx() = *srcX++;
432 destPoint->ry() = *srcY++;
437 if ( !err.isEmpty() )
442 QVector<double> &x, QVector<double> &y, QVector<double> &z,
446 if ( !d->mIsValid || d->mShortCircuit )
449 Q_ASSERT( x.size() == y.size() );
470 QVector<float> &x, QVector<float> &y, QVector<float> &z,
473 if ( !d->mIsValid || d->mShortCircuit )
476 Q_ASSERT( x.size() == y.size() );
486 int vectorSize = x.size();
487 QVector<double> xd( x.size() );
488 QVector<double> yd( y.size() );
489 QVector<double> zd( z.size() );
491 double *destX = xd.data();
492 double *destY = yd.data();
493 double *destZ = zd.data();
495 const float *srcX = x.constData();
496 const float *srcY = y.constData();
497 const float *srcZ = z.constData();
499 for (
int i = 0; i < vectorSize; ++i )
501 *destX++ =
static_cast< double >( *srcX++ );
502 *destY++ =
static_cast< double >( *srcY++ );
503 *destZ++ =
static_cast< double >( *srcZ++ );
509 float *destFX = x.data();
510 float *destFY = y.data();
511 float *destFZ = z.data();
512 const double *srcXD = xd.constData();
513 const double *srcYD = yd.constData();
514 const double *srcZD = zd.constData();
515 for (
int i = 0; i < vectorSize; ++i )
517 *destFX++ =
static_cast< float >( *srcXD++ );
518 *destFY++ =
static_cast< float >( *srcYD++ );
519 *destFZ++ =
static_cast< float >( *srcZD++ );
537 if ( !d->mIsValid || d->mShortCircuit )
550 const int nPoints = 1000;
551 double d = std::sqrt( ( rect.
width() * rect.
height() ) / std::pow( std::sqrt(
static_cast< double >( nPoints ) ) - 1, 2.0 ) );
552 int nXPoints = std::min(
static_cast< int >( std::ceil( rect.
width() / d ) ) + 1, 1000 );
553 int nYPoints = std::min(
static_cast< int >( std::ceil( rect.
height() / d ) ) + 1, 1000 );
560 QVector<double> x( nXPoints * nYPoints );
561 QVector<double> y( nXPoints * nYPoints );
562 QVector<double> z( nXPoints * nYPoints );
564 QgsDebugMsgLevel( QStringLiteral(
"Entering transformBoundingBox..." ), 4 );
568 double dx = rect.
width() /
static_cast< double >( nXPoints - 1 );
569 double dy = rect.
height() /
static_cast< double >( nYPoints - 1 );
573 for (
int i = 0; i < nYPoints ; i++ )
579 for (
int j = 0; j < nXPoints; j++ )
581 x[( i * nXPoints ) + j] = pointX;
582 y[( i * nXPoints ) + j] = pointY;
584 z[( i * nXPoints ) + j] = 0.0;
595 transformCoords( nXPoints * nYPoints, x.data(), y.data(), z.data(), direction );
606 for (
int i = 0; i < nXPoints * nYPoints; i++ )
608 if ( !std::isfinite( x[i] ) || !std::isfinite( y[i] ) )
613 if ( handle180Crossover )
627 throw QgsCsException( QObject::tr(
"Could not transform bounding box to target CRS" ) );
630 if ( handle180Crossover )
651 if ( !d->mIsValid || d->mShortCircuit )
654 if ( !d->mSourceCRS.isValid() )
657 "The coordinates can not be reprojected. The CRS is: %1" )
658 .arg( d->mSourceCRS.toProj() ), QObject::tr(
"CRS" ) );
661 if ( !d->mDestCRS.isValid() )
664 "The coordinates can not be reprojected. The CRS is: %1" ).arg( d->mDestCRS.toProj() ), QObject::tr(
"CRS" ) );
668 std::vector< int > zNanPositions;
669 for (
int i = 0; i < numPoints; i++ )
671 if ( std::isnan( z[i] ) )
673 zNanPositions.push_back( i );
678 #if PROJ_VERSION_MAJOR>=6
679 std::vector< double > xprev( numPoints );
680 memcpy( xprev.data(), x,
sizeof(
double ) * numPoints );
681 std::vector< double > yprev( numPoints );
682 memcpy( yprev.data(), y,
sizeof(
double ) * numPoints );
683 std::vector< double > zprev( numPoints );
684 memcpy( zprev.data(), z,
sizeof(
double ) * numPoints );
687 #ifdef COORDINATE_TRANSFORM_VERBOSE
690 QgsDebugMsg( QStringLiteral(
"[[[[[[ Number of points to transform: %1 ]]]]]]" ).arg( numPoints ) );
695 QgsDebugMsgLevel( QStringLiteral(
"No QgsCoordinateTransformContext context set for transform" ), 4 );
702 ProjData projData = d->threadLocalProjData();
705 #if PROJ_VERSION_MAJOR>=6
706 proj_errno_reset( projData );
707 proj_trans_generic( projData, ( direction ==
ForwardTransform && !d->mIsReversed ) || ( direction ==
ReverseTransform && d->mIsReversed ) ? PJ_FWD : PJ_INV,
708 x,
sizeof(
double ), numPoints,
709 y,
sizeof(
double ), numPoints,
710 z,
sizeof(
double ), numPoints,
711 nullptr,
sizeof(
double ), 0 );
721 if ( numPoints == 1 )
723 projResult = proj_errno( projData );
724 actualRes = projResult;
728 actualRes = proj_errno( projData );
730 if ( actualRes == 0 )
734 if ( std::any_of( x, x + numPoints, [](
double v ) {
return std::isinf( v ); } )
735 || std::any_of( y, y + numPoints, [](
double v ) {
return std::isinf( v ); } )
736 || std::any_of( z, z + numPoints, [](
double v ) {
return std::isinf( v ); } ) )
742 bool sourceIsLatLong =
false;
743 bool destIsLatLong =
false;
745 projPJ sourceProj = projData.first;
746 projPJ destProj = projData.second;
747 sourceIsLatLong = pj_is_latlong( sourceProj );
748 destIsLatLong = pj_is_latlong( destProj );
753 for (
int i = 0; i < numPoints; ++i )
761 #if PROJ_VERSION_MAJOR<6
764 projResult = pj_transform( destProj, sourceProj, numPoints, 0, x, y, z );
768 Q_ASSERT( sourceProj );
769 Q_ASSERT( destProj );
770 projResult = pj_transform( sourceProj, destProj, numPoints, 0, x, y, z );
774 #if PROJ_VERSION_MAJOR>=6
776 mFallbackOperationOccurred =
false;
778 && ( d->mAvailableOpCount > 1 || d->mAvailableOpCount == -1 )
779 && ( d->mAllowFallbackTransforms || mBallparkTransformsAreAppropriate ) )
782 if ( PJ *
transform = d->threadLocalFallbackProjData() )
787 xprev.data(),
sizeof(
double ), numPoints,
788 yprev.data(),
sizeof(
double ), numPoints,
789 zprev.data(),
sizeof(
double ), numPoints,
790 nullptr,
sizeof(
double ), 0 );
799 if ( numPoints == 1 )
804 projResult = std::isinf( xprev[0] ) || std::isinf( yprev[0] ) || std::isinf( zprev[0] ) ? 1 : 0;
807 if ( projResult == 0 )
809 memcpy( x, xprev.data(),
sizeof(
double ) * numPoints );
810 memcpy( y, yprev.data(),
sizeof(
double ) * numPoints );
811 memcpy( z, zprev.data(),
sizeof(
double ) * numPoints );
812 mFallbackOperationOccurred =
true;
815 if ( !mBallparkTransformsAreAppropriate && !mDisableFallbackHandler && sFallbackOperationOccurredHandler )
817 sFallbackOperationOccurredHandler( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation );
819 const QString warning = QStringLiteral(
"A fallback coordinate operation was used between %1 and %2" ).arg( d->mSourceCRS.authid(),
820 d->mDestCRS.authid() );
821 qWarning(
"%s", warning.toLatin1().constData() );
828 for (
const int &pos : zNanPositions )
830 z[pos] = std::numeric_limits<double>::quiet_NaN();
833 if ( projResult != 0 )
838 for (
int i = 0; i < numPoints; ++i )
842 points += QStringLiteral(
"(%1, %2)\n" ).arg( x[i], 0,
'f' ).arg( y[i], 0,
'f' );
846 #if PROJ_VERSION_MAJOR>=6
847 points += QStringLiteral(
"(%1, %2)\n" ).arg( x[i], 0,
'f' ).arg( y[i], 0,
'f' );
849 points += QStringLiteral(
"(%1, %2)\n" ).arg( x[i] * RAD_TO_DEG, 0,
'f' ).arg( y[i] * RAD_TO_DEG, 0,
'f' );
854 QString dir = ( direction ==
ForwardTransform ) ? QObject::tr(
"forward transform" ) : QObject::tr(
"inverse transform" );
856 #if PROJ_VERSION_MAJOR>=6
857 QString msg = QObject::tr(
"%1 of\n"
862 projResult < 0 ? QString::fromUtf8( proj_errno_string( projResult ) ) : QObject::tr(
"Fallback transform failed" ) );
864 char *srcdef = pj_get_def( sourceProj, 0 );
865 char *dstdef = pj_get_def( destProj, 0 );
867 QString msg = QObject::tr(
"%1 of\n"
874 QString::fromUtf8( pj_strerrno( projResult ) ) );
881 if ( msg != mLastError )
883 QgsDebugMsg(
"Projection failed emitting invalid transform signal: " + msg );
891 #if PROJ_VERSION_MAJOR<6
897 for (
int i = 0; i < numPoints; ++i )
904 #ifdef COORDINATE_TRANSFORM_VERBOSE
905 QgsDebugMsg( QStringLiteral(
"[[[[[[ Projected %1, %2 to %3, %4 ]]]]]]" )
906 .arg( xorg, 0,
'g', 15 ).arg( yorg, 0,
'g', 15 )
907 .arg( *x, 0,
'g', 15 ).arg( *y, 0,
'g', 15 ) );
918 return !d->mIsValid || d->mShortCircuit;
923 return d->mProjCoordinateOperation;
928 #if PROJ_VERSION_MAJOR>=6
929 ProjData projData = d->threadLocalProjData();
930 return QgsDatumTransform::transformDetailsFromPj( projData );
939 d->mProjCoordinateOperation = operation;
940 d->mShouldReverseCoordinateOperation =
false;
946 d->mAllowFallbackTransforms = allowed;
951 return d->mAllowFallbackTransforms;
956 mBallparkTransformsAreAppropriate = appropriate;
961 mDisableFallbackHandler = disabled;
966 return mFallbackOperationOccurred;
973 proj = QApplication::applicationDirPath()
974 +
"/share/proj/" + QString( name );
978 return proj.toUtf8();
981 #if PROJ_VERSION_MAJOR>=6
987 const QString sourceKey = src.
authid().isEmpty() ?
989 const QString destKey = dest.
authid().isEmpty() ?
992 if ( sourceKey.isEmpty() || destKey.isEmpty() )
999 const QList< QgsCoordinateTransform > values = sTransforms.values( qMakePair( sourceKey, destKey ) );
1000 for (
auto valIt = values.constBegin(); valIt != values.constEnd(); ++valIt )
1002 if ( ( *valIt ).coordinateOperation() == coordinateOperationProj && ( *valIt ).allowFallbackTransforms() == allowFallback )
1007 bool hasContext = mHasContext;
1014 mHasContext = hasContext;
1028 const QString sourceKey = src.
authid().isEmpty() ?
1030 const QString destKey = dest.
authid().isEmpty() ?
1033 if ( sourceKey.isEmpty() || destKey.isEmpty() )
1037 if ( sDisableCache )
1040 const QList< QgsCoordinateTransform > values = sTransforms.values( qMakePair( src.
authid(), dest.
authid() ) );
1041 for (
auto valIt = values.constBegin(); valIt != values.constEnd(); ++valIt )
1044 if ( ( *valIt ).sourceDatumTransformId() == srcDatumTransform &&
1045 ( *valIt ).destinationDatumTransformId() == destDatumTransform )
1050 bool hasContext = mHasContext;
1057 mHasContext = hasContext;
1068 void QgsCoordinateTransform::addToCache()
1070 if ( !d->mSourceCRS.isValid() || !d->mDestCRS.isValid() )
1073 const QString sourceKey = d->mSourceCRS.authid().isEmpty() ?
1075 const QString destKey = d->mDestCRS.authid().isEmpty() ?
1078 if ( sourceKey.isEmpty() || destKey.isEmpty() )
1082 if ( sDisableCache )
1085 sTransforms.insert( qMakePair( sourceKey, destKey ), *
this );
1091 return d->mSourceDatumTransform;
1099 d->mSourceDatumTransform = dt;
1106 return d->mDestinationDatumTransform;
1114 d->mDestinationDatumTransform = dt;
1121 if ( sDisableCache )
1126 sDisableCache =
true;
1129 sTransforms.clear();
1132 #if PROJ_VERSION_MAJOR>=6
1133 void QgsCoordinateTransform::removeFromCacheObjectsBelongingToCurrentThread(
void *pj_context )
1139 if ( sDisableCache )
1144 if ( sDisableCache )
1147 for (
auto it = sTransforms.begin(); it != sTransforms.end(); )
1149 auto &v = it.value();
1150 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
1151 it = sTransforms.erase( it );
1162 double distSourceUnits = std::sqrt( source1.
sqrDist( source2 ) );
1165 double distDestUnits = std::sqrt( dest1.
sqrDist( dest2 ) );
1166 return distDestUnits / distSourceUnits;
1171 QgsCoordinateTransformPrivate::setCustomMissingRequiredGridHandler( handler );
1176 QgsCoordinateTransformPrivate::setCustomMissingPreferredGridHandler( handler );
1181 QgsCoordinateTransformPrivate::setCustomCoordinateOperationCreationErrorHandler( handler );
1186 QgsCoordinateTransformPrivate::setCustomMissingGridUsedByContextHandler( handler );
1191 sFallbackOperationOccurredHandler = handler;
This class represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString authid() const
Returns the authority identifier for the CRS.
@ WKT_PREFERRED
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
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::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.
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
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
#define QgsDebugMsgLevel(str, level)
const QgsCoordinateReferenceSystem & crs