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 )
 
  521   const int nPoints = 1000;
 
  522   const double d = std::sqrt( ( rect.
width() * rect.
height() ) / std::pow( std::sqrt( 
static_cast< double >( nPoints ) ) - 1, 2.0 ) );
 
  523   const int nXPoints = std::min( 
static_cast< int >( std::ceil( rect.
width() / d ) ) + 1, 1000 );
 
  524   const int nYPoints = std::min( 
static_cast< int >( std::ceil( rect.
height() / d ) ) + 1, 1000 );
 
  531   QVector<double> x( nXPoints * nYPoints );
 
  532   QVector<double> y( nXPoints * nYPoints );
 
  533   QVector<double> z( nXPoints * nYPoints );
 
  535   QgsDebugMsgLevel( QStringLiteral( 
"Entering transformBoundingBox..." ), 4 );
 
  539   const double dx = rect.
width()  / 
static_cast< double >( nXPoints - 1 );
 
  540   const double dy = rect.
height() / 
static_cast< double >( nYPoints - 1 );
 
  544   for ( 
int i = 0; i < nYPoints ; i++ )
 
  550     for ( 
int j = 0; j < nXPoints; j++ )
 
  552       x[( i * nXPoints ) + j] = pointX;
 
  553       y[( i * nXPoints ) + j] = pointY;
 
  555       z[( i * nXPoints ) + j] = 0.0;
 
  566     transformCoords( nXPoints * nYPoints, x.data(), y.data(), z.data(), direction );
 
  577   for ( 
int i = 0; i < nXPoints * nYPoints; i++ )
 
  579     if ( !std::isfinite( x[i] ) || !std::isfinite( y[i] ) )
 
  584     if ( handle180Crossover )
 
  598     throw QgsCsException( QObject::tr( 
"Could not transform bounding box to target CRS" ) );
 
  601   if ( handle180Crossover )
 
  622   if ( !d->mIsValid || d->mShortCircuit )
 
  625   if ( !d->mSourceCRS.isValid() )
 
  628                                             "The coordinates can not be reprojected. The CRS is: %1" )
 
  629                                .arg( d->mSourceCRS.toProj() ), QObject::tr( 
"CRS" ) );
 
  632   if ( !d->mDestCRS.isValid() )
 
  635                                             "The coordinates can not be reprojected. The CRS is: %1" ).arg( d->mDestCRS.toProj() ), QObject::tr( 
"CRS" ) );
 
  639   std::vector< int > zNanPositions;
 
  640   for ( 
int i = 0; i < numPoints; i++ )
 
  642     if ( std::isnan( z[i] ) )
 
  644       zNanPositions.push_back( i );
 
  649   std::vector< double > xprev( numPoints );
 
  650   memcpy( xprev.data(), x, 
sizeof( 
double ) * numPoints );
 
  651   std::vector< double > yprev( numPoints );
 
  652   memcpy( yprev.data(), y, 
sizeof( 
double ) * numPoints );
 
  653   std::vector< double > zprev( numPoints );
 
  654   memcpy( zprev.data(), z, 
sizeof( 
double ) * numPoints );
 
  656   const bool useTime = !std::isnan( d->mDefaultTime );
 
  657   std::vector< double > t( useTime ? numPoints : 0, d->mDefaultTime );
 
  659 #ifdef COORDINATE_TRANSFORM_VERBOSE 
  662   QgsDebugMsg( QStringLiteral( 
"[[[[[[ Number of points to transform: %1 ]]]]]]" ).arg( numPoints ) );
 
  667     QgsDebugMsgLevel( QStringLiteral( 
"No QgsCoordinateTransformContext context set for transform" ), 4 );
 
  674   ProjData projData = d->threadLocalProjData();
 
  678   proj_errno_reset( projData );
 
  679   proj_trans_generic( projData, ( direction == Qgis::TransformDirection::Forward && !d->mIsReversed ) || ( direction == Qgis::TransformDirection::Reverse && d->mIsReversed ) ? PJ_FWD : PJ_INV,
 
  680                       x, 
sizeof( 
double ), numPoints,
 
  681                       y, 
sizeof( 
double ), numPoints,
 
  682                       z, 
sizeof( 
double ), numPoints,
 
  683                       useTime ? t.data() : 
nullptr, 
sizeof( 
double ), useTime ? numPoints : 0 );
 
  693   if ( numPoints == 1 )
 
  695     projResult = proj_errno( projData );
 
  696     actualRes = projResult;
 
  700     actualRes =  proj_errno( projData );
 
  702   if ( actualRes == 0 )
 
  706     if ( std::any_of( x, x + numPoints, []( 
double v ) { 
return std::isinf( v ); } )
 
  707     || std::any_of( y, y + numPoints, []( 
double v ) { 
return std::isinf( v ); } )
 
  708     || std::any_of( z, z + numPoints, []( 
double v ) { 
return std::isinf( v ); } ) )
 
  714   mFallbackOperationOccurred = 
false;
 
  716        && ( d->mAvailableOpCount > 1 || d->mAvailableOpCount == -1 ) 
 
  717        && ( d->mAllowFallbackTransforms || mBallparkTransformsAreAppropriate ) )
 
  720     if ( 
PJ *
transform = d->threadLocalFallbackProjData() )
 
  724       proj_trans_generic( 
transform, direction == Qgis::TransformDirection::Forward ? PJ_FWD : PJ_INV,
 
  725                           xprev.data(), 
sizeof( 
double ), numPoints,
 
  726                           yprev.data(), 
sizeof( 
double ), numPoints,
 
  727                           zprev.data(), 
sizeof( 
double ), numPoints,
 
  728                           useTime ? t.data() : 
nullptr, 
sizeof( 
double ), useTime ? numPoints : 0 );
 
  737       if ( numPoints == 1 )
 
  742         projResult = std::isinf( xprev[0] ) || std::isinf( yprev[0] ) || std::isinf( zprev[0] ) ? 1 : 0;
 
  745       if ( projResult == 0 )
 
  747         memcpy( x, xprev.data(), 
sizeof( 
double ) * numPoints );
 
  748         memcpy( y, yprev.data(), 
sizeof( 
double ) * numPoints );
 
  749         memcpy( z, zprev.data(), 
sizeof( 
double ) * numPoints );
 
  750         mFallbackOperationOccurred = 
true;
 
  753       if ( !mBallparkTransformsAreAppropriate && !mDisableFallbackHandler && sFallbackOperationOccurredHandler )
 
  755         sFallbackOperationOccurredHandler( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation );
 
  757         const QString warning = QStringLiteral( 
"A fallback coordinate operation was used between %1 and %2" ).arg( d->mSourceCRS.authid(),
 
  758                                 d->mDestCRS.authid() );
 
  759         qWarning( 
"%s", warning.toLatin1().constData() );
 
  765   for ( 
const int &pos : zNanPositions )
 
  767     z[pos] = std::numeric_limits<double>::quiet_NaN();
 
  770   if ( projResult != 0 )
 
  775     for ( 
int i = 0; i < numPoints; ++i )
 
  777       if ( direction == Qgis::TransformDirection::Forward )
 
  779         points += QStringLiteral( 
"(%1, %2)\n" ).arg( x[i], 0, 
'f' ).arg( y[i], 0, 
'f' );
 
  783         points += QStringLiteral( 
"(%1, %2)\n" ).arg( x[i], 0, 
'f' ).arg( y[i], 0, 
'f' );
 
  787     const QString dir = ( direction == Qgis::TransformDirection::Forward ) ? QObject::tr( 
"forward transform" ) : QObject::tr( 
"inverse transform" );
 
  789     const QString msg = QObject::tr( 
"%1 of\n" 
  794                               projResult < 0 ? QString::fromUtf8( proj_errno_string( projResult ) ) : QObject::tr( 
"Fallback transform failed" ) );
 
  798     if ( msg != mLastError )
 
  800       QgsDebugMsg( 
"Projection failed emitting invalid transform signal: " + msg );
 
  808 #ifdef COORDINATE_TRANSFORM_VERBOSE 
  809   QgsDebugMsg( QStringLiteral( 
"[[[[[[ Projected %1, %2 to %3, %4 ]]]]]]" )
 
  810                .arg( xorg, 0, 
'g', 15 ).arg( yorg, 0, 
'g', 15 )
 
  811                .arg( *x, 0, 
'g', 15 ).arg( *y, 0, 
'g', 15 ) );
 
  822   return !d->mIsValid || d->mShortCircuit;
 
  827   return d->mProjCoordinateOperation;
 
  832   ProjData projData = d->threadLocalProjData();
 
  839   d->mProjCoordinateOperation = operation;
 
  840   d->mShouldReverseCoordinateOperation = 
false;
 
  846   d->mAllowFallbackTransforms = allowed;
 
  851   return d->mAllowFallbackTransforms;
 
  856   mBallparkTransformsAreAppropriate = appropriate;
 
  861   mDisableFallbackHandler = disabled;
 
  866   return mFallbackOperationOccurred;
 
  873   proj = QApplication::applicationDirPath()
 
  874          + 
"/share/proj/" + QString( name );
 
  878   return proj.toUtf8();
 
  886   const QString sourceKey = src.
authid().isEmpty() ?
 
  888   const QString destKey = dest.
authid().isEmpty() ?
 
  891   if ( sourceKey.isEmpty() || destKey.isEmpty() )
 
  898   const QList< QgsCoordinateTransform > values = sTransforms.values( qMakePair( sourceKey, destKey ) );
 
  899   for ( 
auto valIt = values.constBegin(); valIt != values.constEnd(); ++valIt )
 
  901     if ( ( *valIt ).coordinateOperation() == coordinateOperationProj
 
  902          && ( *valIt ).allowFallbackTransforms() == allowFallback
 
  910       const bool hasContext = mHasContext;
 
  917       mHasContext = hasContext;
 
  926 void QgsCoordinateTransform::addToCache()
 
  928   if ( !d->mSourceCRS.isValid() || !d->mDestCRS.isValid() )
 
  931   const QString sourceKey = d->mSourceCRS.authid().isEmpty() ?
 
  933   const QString destKey = d->mDestCRS.authid().isEmpty() ?
 
  936   if ( sourceKey.isEmpty() || destKey.isEmpty() )
 
  943   sTransforms.insert( qMakePair( sourceKey, destKey ), *
this );
 
  949   return d->mSourceDatumTransform;
 
  957   d->mSourceDatumTransform = dt;
 
  964   return d->mDestinationDatumTransform;
 
  972   d->mDestinationDatumTransform = dt;
 
  984     sDisableCache = 
true;
 
  990 void QgsCoordinateTransform::removeFromCacheObjectsBelongingToCurrentThread( 
void *pj_context )
 
 1001   if ( sDisableCache )
 
 1004   for ( 
auto it = sTransforms.begin(); it != sTransforms.end(); )
 
 1006     auto &v = it.value();
 
 1007     if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
 
 1008       it = sTransforms.erase( it );
 
 1018   const double distSourceUnits = std::sqrt( source1.
sqrDist( source2 ) );
 
 1021   const double distDestUnits = std::sqrt( dest1.
sqrDist( dest2 ) );
 
 1022   return distDestUnits / distSourceUnits;
 
 1027   QgsCoordinateTransformPrivate::setCustomMissingRequiredGridHandler( handler );
 
 1032   QgsCoordinateTransformPrivate::setCustomMissingPreferredGridHandler( handler );
 
 1037   QgsCoordinateTransformPrivate::setCustomCoordinateOperationCreationErrorHandler( handler );
 
 1042   QgsCoordinateTransformPrivate::setCustomMissingGridUsedByContextHandler( handler );
 
 1047   sFallbackOperationOccurredHandler = handler;
 
 1052   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.
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,...
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.
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
bool qgsNanCompatibleEquals(double a, double b)
Compare two doubles, treating nan values as equal.
#define QgsDebugMsgLevel(str, level)
const QgsCoordinateReferenceSystem & crs