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 )
149 mHasContext = o.mHasContext;
151 mContext = o.mContext;
161 if ( !d->checkValidity() )
164 d->calculateTransforms( mContext );
166 #if PROJ_VERSION_MAJOR>=6 167 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
169 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
181 if ( !d->checkValidity() )
184 d->calculateTransforms( mContext );
186 #if PROJ_VERSION_MAJOR>=6 187 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
189 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
205 if ( !d->checkValidity() )
208 d->calculateTransforms( mContext );
210 #if PROJ_VERSION_MAJOR>=6 211 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
213 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
229 return d->mSourceCRS;
239 if ( !d->mIsValid || d->mShortCircuit )
243 double x = point.
x();
244 double y = point.
y();
277 if ( !d->mIsValid || d->mShortCircuit )
301 #ifdef COORDINATE_TRANSFORM_VERBOSE 302 QgsDebugMsg( QStringLiteral(
"Rect projection..." ) );
314 if ( !d->mIsValid || d->mShortCircuit )
335 double xd =
static_cast< double >( x ), yd = static_cast< double >( y );
344 if ( !d->mIsValid || d->mShortCircuit )
370 if ( !d->mIsValid || d->mShortCircuit )
376 int nVertices = poly.size();
378 QVector<double> x( nVertices );
379 QVector<double> y( nVertices );
380 QVector<double> z( nVertices );
381 double *destX = x.data();
382 double *destY = y.data();
383 double *destZ = z.data();
385 const QPointF *polyData = poly.constData();
386 for (
int i = 0; i < nVertices; ++i )
388 *destX++ = polyData->x();
389 *destY++ = polyData->y();
405 QPointF *destPoint = poly.data();
406 const double *srcX = x.constData();
407 const double *srcY = y.constData();
408 for (
int i = 0; i < nVertices; ++i )
410 destPoint->rx() = *srcX++;
411 destPoint->ry() = *srcY++;
416 if ( !err.isEmpty() )
421 QVector<double> &x, QVector<double> &y, QVector<double> &z,
425 if ( !d->mIsValid || d->mShortCircuit )
428 Q_ASSERT( x.size() == y.size() );
449 QVector<float> &x, QVector<float> &y, QVector<float> &z,
452 if ( !d->mIsValid || d->mShortCircuit )
455 Q_ASSERT( x.size() == y.size() );
465 int vectorSize = x.size();
466 QVector<double> xd( x.size() );
467 QVector<double> yd( y.size() );
468 QVector<double> zd( z.size() );
470 double *destX = xd.data();
471 double *destY = yd.data();
472 double *destZ = zd.data();
474 const float *srcX = x.constData();
475 const float *srcY = y.constData();
476 const float *srcZ = z.constData();
478 for (
int i = 0; i < vectorSize; ++i )
480 *destX++ =
static_cast< double >( *srcX++ );
481 *destY++ =
static_cast< double >( *srcY++ );
482 *destZ++ =
static_cast< double >( *srcZ++ );
488 float *destFX = x.data();
489 float *destFY = y.data();
490 float *destFZ = z.data();
491 const double *srcXD = xd.constData();
492 const double *srcYD = yd.constData();
493 const double *srcZD = zd.constData();
494 for (
int i = 0; i < vectorSize; ++i )
496 *destFX++ =
static_cast< float >( *srcXD++ );
497 *destFY++ =
static_cast< float >( *srcYD++ );
498 *destFZ++ =
static_cast< float >( *srcZD++ );
516 if ( !d->mIsValid || d->mShortCircuit )
529 const int nPoints = 1000;
530 double d = std::sqrt( ( rect.
width() * rect.
height() ) / std::pow( std::sqrt( static_cast< double >( nPoints ) ) - 1, 2.0 ) );
531 int nXPoints = std::min( static_cast< int >( std::ceil( rect.
width() / d ) ) + 1, 1000 );
532 int nYPoints = std::min( static_cast< int >( std::ceil( rect.
height() / d ) ) + 1, 1000 );
539 QVector<double> x( nXPoints * nYPoints );
540 QVector<double> y( nXPoints * nYPoints );
541 QVector<double> z( nXPoints * nYPoints );
543 QgsDebugMsgLevel( QStringLiteral(
"Entering transformBoundingBox..." ), 4 );
547 double dx = rect.
width() /
static_cast< double >( nXPoints - 1 );
548 double dy = rect.
height() /
static_cast< double >( nYPoints - 1 );
552 for (
int i = 0; i < nYPoints ; i++ )
558 for (
int j = 0; j < nXPoints; j++ )
560 x[( i * nXPoints ) + j] = pointX;
561 y[( i * nXPoints ) + j] = pointY;
563 z[( i * nXPoints ) + j] = 0.0;
574 transformCoords( nXPoints * nYPoints, x.data(), y.data(), z.data(), direction );
585 for (
int i = 0; i < nXPoints * nYPoints; i++ )
587 if ( !std::isfinite( x[i] ) || !std::isfinite( y[i] ) )
592 if ( handle180Crossover )
595 bb_rect.combineExtentWith( x[i] >= 0.0 ? x[i] : x[i] + 360.0, y[i] );
599 bb_rect.combineExtentWith( x[i], y[i] );
603 if ( bb_rect.isNull() )
606 throw QgsCsException( QObject::tr(
"Could not transform bounding box to target CRS" ) );
609 if ( handle180Crossover )
612 if ( bb_rect.xMinimum() > 180.0 )
613 bb_rect.setXMinimum( bb_rect.xMinimum() - 360.0 );
614 if ( bb_rect.xMaximum() > 180.0 )
615 bb_rect.setXMaximum( bb_rect.xMaximum() - 360.0 );
620 if ( bb_rect.isEmpty() )
630 if ( !d->mIsValid || d->mShortCircuit )
633 if ( !d->mSourceCRS.isValid() )
636 "The coordinates can not be reprojected. The CRS is: %1" )
637 .arg( d->mSourceCRS.toProj() ), QObject::tr(
"CRS" ) );
640 if ( !d->mDestCRS.isValid() )
643 "The coordinates can not be reprojected. The CRS is: %1" ).arg( d->mDestCRS.toProj() ), QObject::tr(
"CRS" ) );
647 #if PROJ_VERSION_MAJOR>=6 648 std::vector< double > xprev( numPoints );
649 memcpy( xprev.data(), x,
sizeof( double ) * numPoints );
650 std::vector< double > yprev( numPoints );
651 memcpy( yprev.data(), y,
sizeof( double ) * numPoints );
652 std::vector< double > zprev( numPoints );
653 memcpy( zprev.data(), z,
sizeof( double ) * numPoints );
656 #ifdef COORDINATE_TRANSFORM_VERBOSE 659 QgsDebugMsg( QStringLiteral(
"[[[[[[ Number of points to transform: %1 ]]]]]]" ).arg( numPoints ) );
664 QgsDebugMsgLevel( QStringLiteral(
"No QgsCoordinateTransformContext context set for transform" ), 4 );
671 ProjData projData = d->threadLocalProjData();
674 #if PROJ_VERSION_MAJOR>=6 675 proj_errno_reset( projData );
676 proj_trans_generic( projData, ( direction ==
ForwardTransform && !d->mIsReversed ) || ( direction ==
ReverseTransform && d->mIsReversed ) ? PJ_FWD : PJ_INV,
677 x,
sizeof(
double ), numPoints,
678 y,
sizeof(
double ), numPoints,
679 z,
sizeof(
double ), numPoints,
680 nullptr,
sizeof(
double ), 0 );
690 if ( numPoints == 1 )
692 projResult = proj_errno( projData );
693 actualRes = projResult;
697 actualRes = proj_errno( projData );
699 if ( actualRes == 0 )
703 if ( std::any_of( x, x + numPoints, [](
double v ) {
return std::isinf( v ); } )
704 || std::any_of( y, y + numPoints, [](
double v ) {
return std::isinf( v ); } )
705 || std::any_of( z, z + numPoints, [](
double v ) {
return std::isinf( v ); } ) )
711 bool sourceIsLatLong =
false;
712 bool destIsLatLong =
false;
714 projPJ sourceProj = projData.first;
715 projPJ destProj = projData.second;
716 sourceIsLatLong = pj_is_latlong( sourceProj );
717 destIsLatLong = pj_is_latlong( destProj );
722 for (
int i = 0; i < numPoints; ++i )
730 #if PROJ_VERSION_MAJOR<6 733 projResult = pj_transform( destProj, sourceProj, numPoints, 0, x, y, z );
737 Q_ASSERT( sourceProj );
738 Q_ASSERT( destProj );
739 projResult = pj_transform( sourceProj, destProj, numPoints, 0, x, y, z );
743 #if PROJ_VERSION_MAJOR>=6 745 mFallbackOperationOccurred =
false;
747 && ( d->mAvailableOpCount > 1 || d->mAvailableOpCount == -1 )
748 && ( d->mAllowFallbackTransforms || mBallparkTransformsAreAppropriate ) )
751 if ( PJ *
transform = d->threadLocalFallbackProjData() )
756 xprev.data(),
sizeof( double ), numPoints,
757 yprev.data(),
sizeof( double ), numPoints,
758 zprev.data(),
sizeof( double ), numPoints,
759 nullptr,
sizeof(
double ), 0 );
768 if ( numPoints == 1 )
773 projResult = std::isinf( xprev[0] ) || std::isinf( yprev[0] ) || std::isinf( zprev[0] ) ? 1 : 0;
776 if ( projResult == 0 )
778 memcpy( x, xprev.data(),
sizeof( double ) * numPoints );
779 memcpy( y, yprev.data(),
sizeof( double ) * numPoints );
780 memcpy( z, zprev.data(),
sizeof( double ) * numPoints );
781 mFallbackOperationOccurred =
true;
784 if ( !mBallparkTransformsAreAppropriate && !mDisableFallbackHandler && sFallbackOperationOccurredHandler )
786 sFallbackOperationOccurredHandler( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation );
788 const QString warning = QStringLiteral(
"A fallback coordinate operation was used between %1 and %2" ).arg( d->mSourceCRS.authid(),
789 d->mDestCRS.authid() );
790 qWarning(
"%s", warning.toLatin1().constData() );
796 if ( projResult != 0 )
801 for (
int i = 0; i < numPoints; ++i )
805 points += QStringLiteral(
"(%1, %2)\n" ).arg( x[i], 0,
'f' ).arg( y[i], 0,
'f' );
809 #if PROJ_VERSION_MAJOR>=6 810 points += QStringLiteral(
"(%1, %2)\n" ).arg( x[i], 0,
'f' ).arg( y[i], 0,
'f' );
812 points += QStringLiteral(
"(%1, %2)\n" ).arg( x[i] * RAD_TO_DEG, 0,
'f' ).arg( y[i] * RAD_TO_DEG, 0,
'f' );
817 QString dir = ( direction ==
ForwardTransform ) ? QObject::tr(
"forward transform" ) : QObject::tr(
"inverse transform" );
819 #if PROJ_VERSION_MAJOR>=6 820 QString msg = QObject::tr(
"%1 of\n" 825 projResult < 0 ? QString::fromUtf8( proj_errno_string( projResult ) ) : QObject::tr(
"Fallback transform failed" ) );
827 char *srcdef = pj_get_def( sourceProj, 0 );
828 char *dstdef = pj_get_def( destProj, 0 );
830 QString msg = QObject::tr(
"%1 of\n" 837 QString::fromUtf8( pj_strerrno( projResult ) ) );
844 if ( msg != mLastError )
846 QgsDebugMsg(
"Projection failed emitting invalid transform signal: " + msg );
854 #if PROJ_VERSION_MAJOR<6 860 for (
int i = 0; i < numPoints; ++i )
867 #ifdef COORDINATE_TRANSFORM_VERBOSE 868 QgsDebugMsg( QStringLiteral(
"[[[[[[ Projected %1, %2 to %3, %4 ]]]]]]" )
869 .arg( xorg, 0,
'g', 15 ).arg( yorg, 0,
'g', 15 )
870 .arg( *x, 0,
'g', 15 ).arg( *y, 0,
'g', 15 ) );
881 return !d->mIsValid || d->mShortCircuit;
886 return d->mProjCoordinateOperation;
891 #if PROJ_VERSION_MAJOR>=6 892 ProjData projData = d->threadLocalProjData();
893 return QgsDatumTransform::transformDetailsFromPj( projData );
902 d->mProjCoordinateOperation = operation;
903 d->mShouldReverseCoordinateOperation =
false;
909 d->mAllowFallbackTransforms = allowed;
914 return d->mAllowFallbackTransforms;
919 mBallparkTransformsAreAppropriate = appropriate;
924 mDisableFallbackHandler = disabled;
929 return mFallbackOperationOccurred;
936 proj = QApplication::applicationDirPath()
937 +
"/share/proj/" + QString( name );
941 return proj.toUtf8();
944 #if PROJ_VERSION_MAJOR>=6 950 const QString sourceKey = src.
authid().isEmpty() ?
952 const QString destKey = dest.
authid().isEmpty() ?
955 if ( sourceKey.isEmpty() || destKey.isEmpty() )
962 const QList< QgsCoordinateTransform > values = sTransforms.values( qMakePair( sourceKey, destKey ) );
963 for (
auto valIt = values.constBegin(); valIt != values.constEnd(); ++valIt )
965 if ( ( *valIt ).coordinateOperation() == coordinateOperationProj && ( *valIt ).allowFallbackTransforms() == allowFallback )
970 bool hasContext = mHasContext;
977 mHasContext = hasContext;
991 const QString sourceKey = src.
authid().isEmpty() ?
993 const QString destKey = dest.
authid().isEmpty() ?
996 if ( sourceKey.isEmpty() || destKey.isEmpty() )
1000 if ( sDisableCache )
1003 const QList< QgsCoordinateTransform > values = sTransforms.values( qMakePair( src.
authid(), dest.
authid() ) );
1004 for (
auto valIt = values.constBegin(); valIt != values.constEnd(); ++valIt )
1007 if ( ( *valIt ).sourceDatumTransformId() == srcDatumTransform &&
1008 ( *valIt ).destinationDatumTransformId() == destDatumTransform )
1013 bool hasContext = mHasContext;
1020 mHasContext = hasContext;
1031 void QgsCoordinateTransform::addToCache()
1033 if ( !d->mSourceCRS.isValid() || !d->mDestCRS.isValid() )
1036 const QString sourceKey = d->mSourceCRS.authid().isEmpty() ?
1038 const QString destKey = d->mDestCRS.authid().isEmpty() ?
1041 if ( sourceKey.isEmpty() || destKey.isEmpty() )
1045 if ( sDisableCache )
1048 sTransforms.insertMulti( qMakePair( sourceKey, destKey ), *
this );
1054 return d->mSourceDatumTransform;
1062 d->mSourceDatumTransform = dt;
1069 return d->mDestinationDatumTransform;
1077 d->mDestinationDatumTransform = dt;
1084 if ( sDisableCache )
1089 sDisableCache =
true;
1092 sTransforms.clear();
1095 #if PROJ_VERSION_MAJOR>=6 1096 void QgsCoordinateTransform::removeFromCacheObjectsBelongingToCurrentThread(
void *pj_context )
1102 if ( sDisableCache )
1106 if ( sDisableCache )
1109 for (
auto it = sTransforms.begin(); it != sTransforms.end(); )
1111 auto &v = it.value();
1112 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
1113 it = sTransforms.erase( it );
1124 double distSourceUnits = std::sqrt( source1.sqrDist( source2 ) );
1127 double distDestUnits = std::sqrt( dest1.sqrDist( dest2 ) );
1128 return distDestUnits / distSourceUnits;
1133 QgsCoordinateTransformPrivate::setCustomMissingRequiredGridHandler( handler );
1138 QgsCoordinateTransformPrivate::setCustomMissingPreferredGridHandler( handler );
1143 QgsCoordinateTransformPrivate::setCustomCoordinateOperationCreationErrorHandler( handler );
1148 QgsCoordinateTransformPrivate::setCustomMissingGridUsedByContextHandler( handler );
1153 sFallbackOperationOccurredHandler = handler;
A rectangle specified with double values.
void setMinimal()
Set a rectangle so that min corner is at max and max corner is at min.
A class to represent a 2D point.
#define Q_NOWARN_DEPRECATED_PUSH
const QgsCoordinateReferenceSystem & crs
The QgsReadWriteLocker class is a convenience class that simplifies locking and unlocking QReadWriteL...
#define QgsDebugMsgLevel(str, level)
bool isEmpty() const
Returns true if the rectangle is empty.
double width() const
Returns the width of the rectangle.
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).
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
Encapsulates a QGIS project, including sets of map layers and their styles, layouts, annotations, canvases, etc.
Contains information about the context in which a coordinate transform is executed.
Full WKT2 string, conforming to ISO 19162:2018 / OGC 18-010, with all possible nodes and new keyword ...
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
double xMaximum() const
Returns the x maximum value (right side of rectangle).
QgsCoordinateTransformContext transformContext
#define Q_NOWARN_DEPRECATED_POP
void unlock()
Unlocks the lock.
This class represents a coordinate reference system (CRS).
double xMinimum() const
Returns the x minimum value (left side of rectangle).
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Custom exception class for Coordinate Reference System related exceptions.
QString authid() const
Returns the authority identifier for the CRS.
QString toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
double height() const
Returns the height of the rectangle.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.