30 #include <QDomElement> 31 #include <QApplication> 33 #include <QStringList> 36 #if PROJ_VERSION_MAJOR>=6 48 QReadWriteLock QgsCoordinateTransform::sCacheLock;
50 bool QgsCoordinateTransform::sDisableCache =
false;
54 d =
new QgsCoordinateTransformPrivate();
60 d =
new QgsCoordinateTransformPrivate( source, destination, mContext );
65 if ( !d->checkValidity() )
69 #if PROJ_VERSION_MAJOR>=6 70 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation ) )
72 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
84 d =
new QgsCoordinateTransformPrivate( source, destination, mContext );
90 if ( !d->checkValidity() )
94 #if PROJ_VERSION_MAJOR>=6 95 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation ) )
97 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
108 d =
new QgsCoordinateTransformPrivate( source, destination, sourceDatumTransform, destinationDatumTransform );
113 if ( !d->checkValidity() )
117 #if PROJ_VERSION_MAJOR>=6 118 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation ) )
120 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
130 : mContext( o.mContext )
132 , mHasContext( o.mHasContext )
142 mHasContext = o.mHasContext;
144 mContext = o.mContext;
154 if ( !d->checkValidity() )
157 d->calculateTransforms( mContext );
159 #if PROJ_VERSION_MAJOR>=6 160 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation ) )
162 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
174 if ( !d->checkValidity() )
177 d->calculateTransforms( mContext );
179 #if PROJ_VERSION_MAJOR>=6 180 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation ) )
182 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
198 if ( !d->checkValidity() )
201 d->calculateTransforms( mContext );
203 #if PROJ_VERSION_MAJOR>=6 204 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation ) )
206 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mSourceDatumTransform, d->mDestinationDatumTransform ) )
222 return d->mSourceCRS;
232 if ( !d->mIsValid || d->mShortCircuit )
236 double x = point.
x();
237 double y = point.
y();
246 QgsDebugMsg( QStringLiteral(
"rethrowing exception" ) );
263 QgsDebugMsg( QStringLiteral(
"rethrowing exception" ) );
270 if ( !d->mIsValid || d->mShortCircuit )
290 QgsDebugMsg( QStringLiteral(
"rethrowing exception" ) );
294 #ifdef COORDINATE_TRANSFORM_VERBOSE 295 QgsDebugMsg( QStringLiteral(
"Rect projection..." ) );
307 if ( !d->mIsValid || d->mShortCircuit )
320 QgsDebugMsg( QStringLiteral(
"rethrowing exception" ) );
328 double xd =
static_cast< double >( x ), yd = static_cast< double >( y );
337 if ( !d->mIsValid || d->mShortCircuit )
356 QgsDebugMsg( QStringLiteral(
"rethrowing exception" ) );
363 if ( !d->mIsValid || d->mShortCircuit )
369 int nVertices = poly.size();
371 QVector<double> x( nVertices );
372 QVector<double> y( nVertices );
373 QVector<double> z( nVertices );
374 double *destX = x.data();
375 double *destY = y.data();
376 double *destZ = z.data();
378 const QPointF *polyData = poly.constData();
379 for (
int i = 0; i < nVertices; ++i )
381 *destX++ = polyData->x();
382 *destY++ = polyData->y();
398 QPointF *destPoint = poly.data();
399 const double *srcX = x.constData();
400 const double *srcY = y.constData();
401 for (
int i = 0; i < nVertices; ++i )
403 destPoint->rx() = *srcX++;
404 destPoint->ry() = *srcY++;
409 if ( !err.isEmpty() )
414 QVector<double> &x, QVector<double> &y, QVector<double> &z,
418 if ( !d->mIsValid || d->mShortCircuit )
421 Q_ASSERT( x.size() == y.size() );
435 QgsDebugMsg( QStringLiteral(
"rethrowing exception" ) );
442 QVector<float> &x, QVector<float> &y, QVector<float> &z,
445 if ( !d->mIsValid || d->mShortCircuit )
448 Q_ASSERT( x.size() == y.size() );
458 int vectorSize = x.size();
459 QVector<double> xd( x.size() );
460 QVector<double> yd( y.size() );
461 QVector<double> zd( z.size() );
463 double *destX = xd.data();
464 double *destY = yd.data();
465 double *destZ = zd.data();
467 const float *srcX = x.constData();
468 const float *srcY = y.constData();
469 const float *srcZ = z.constData();
471 for (
int i = 0; i < vectorSize; ++i )
473 *destX++ =
static_cast< double >( *srcX++ );
474 *destY++ =
static_cast< double >( *srcY++ );
475 *destZ++ =
static_cast< double >( *srcZ++ );
481 float *destFX = x.data();
482 float *destFY = y.data();
483 float *destFZ = z.data();
484 const double *srcXD = xd.constData();
485 const double *srcYD = yd.constData();
486 const double *srcZD = zd.constData();
487 for (
int i = 0; i < vectorSize; ++i )
489 *destFX++ =
static_cast< float >( *srcXD++ );
490 *destFY++ =
static_cast< float >( *srcYD++ );
491 *destFZ++ =
static_cast< float >( *srcZD++ );
497 QgsDebugMsg( QStringLiteral(
"rethrowing exception" ) );
509 if ( !d->mIsValid || d->mShortCircuit )
522 const int nPoints = 1000;
523 double d = std::sqrt( ( rect.
width() * rect.
height() ) / std::pow( std::sqrt( static_cast< double >( nPoints ) ) - 1, 2.0 ) );
524 int nXPoints = std::min( static_cast< int >( std::ceil( rect.
width() / d ) ) + 1, 1000 );
525 int nYPoints = std::min( static_cast< int >( std::ceil( rect.
height() / d ) ) + 1, 1000 );
532 QVector<double> x( nXPoints * nYPoints );
533 QVector<double> y( nXPoints * nYPoints );
534 QVector<double> z( nXPoints * nYPoints );
536 QgsDebugMsgLevel( QStringLiteral(
"Entering transformBoundingBox..." ), 4 );
540 double dx = rect.
width() /
static_cast< double >( nXPoints - 1 );
541 double dy = rect.
height() /
static_cast< double >( nYPoints - 1 );
545 for (
int i = 0; i < nYPoints ; i++ )
551 for (
int j = 0; j < nXPoints; j++ )
553 x[( i * nXPoints ) + j] = pointX;
554 y[( i * nXPoints ) + j] = pointY;
556 z[( i * nXPoints ) + j] = 0.0;
567 transformCoords( nXPoints * nYPoints, x.data(), y.data(), z.data(), direction );
572 QgsDebugMsg( QStringLiteral(
"rethrowing exception" ) );
578 for (
int i = 0; i < nXPoints * nYPoints; i++ )
580 if ( !std::isfinite( x[i] ) || !std::isfinite( y[i] ) )
585 if ( handle180Crossover )
588 bb_rect.combineExtentWith( x[i] >= 0.0 ? x[i] : x[i] + 360.0, y[i] );
592 bb_rect.combineExtentWith( x[i], y[i] );
596 if ( bb_rect.isNull() )
599 throw QgsCsException( QObject::tr(
"Could not transform bounding box to target CRS" ) );
602 if ( handle180Crossover )
605 if ( bb_rect.xMinimum() > 180.0 )
606 bb_rect.setXMinimum( bb_rect.xMinimum() - 360.0 );
607 if ( bb_rect.xMaximum() > 180.0 )
608 bb_rect.setXMaximum( bb_rect.xMaximum() - 360.0 );
613 if ( bb_rect.isEmpty() )
623 if ( !d->mIsValid || d->mShortCircuit )
626 if ( !d->mSourceCRS.isValid() )
629 "The coordinates can not be reprojected. The CRS is: %1" )
630 .arg( d->mSourceCRS.toProj4() ), QObject::tr(
"CRS" ) );
633 if ( !d->mDestCRS.isValid() )
636 "The coordinates can not be reprojected. The CRS is: %1" ).arg( d->mDestCRS.toProj4() ), QObject::tr(
"CRS" ) );
640 #ifdef COORDINATE_TRANSFORM_VERBOSE 643 QgsDebugMsg( QStringLiteral(
"[[[[[[ Number of points to transform: %1 ]]]]]]" ).arg( numPoints ) );
648 QgsDebugMsgLevel( QStringLiteral(
"No QgsCoordinateTransformContext context set for transform" ), 4 );
655 ProjData projData = d->threadLocalProjData();
658 #if PROJ_VERSION_MAJOR>=6 659 proj_errno_reset( projData );
660 proj_trans_generic( projData, direction ==
ForwardTransform ? PJ_FWD : PJ_INV,
661 x,
sizeof(
double ), numPoints,
662 y,
sizeof(
double ), numPoints,
663 z,
sizeof(
double ), numPoints,
664 nullptr,
sizeof(
double ), 0 );
665 projResult = proj_errno( projData );
667 bool sourceIsLatLong =
false;
668 bool destIsLatLong =
false;
670 projPJ sourceProj = projData.first;
671 projPJ destProj = projData.second;
672 sourceIsLatLong = pj_is_latlong( sourceProj );
673 destIsLatLong = pj_is_latlong( destProj );
678 for (
int i = 0; i < numPoints; ++i )
686 #if PROJ_VERSION_MAJOR<6 689 projResult = pj_transform( destProj, sourceProj, numPoints, 0, x, y, z );
693 Q_ASSERT( sourceProj );
694 Q_ASSERT( destProj );
695 projResult = pj_transform( sourceProj, destProj, numPoints, 0, x, y, z );
699 if ( projResult != 0 )
704 for (
int i = 0; i < numPoints; ++i )
708 points += QStringLiteral(
"(%1, %2)\n" ).arg( x[i], 0,
'f' ).arg( y[i], 0,
'f' );
712 #if PROJ_VERSION_MAJOR>=6 713 points += QStringLiteral(
"(%1, %2)\n" ).arg( x[i], 0,
'f' ).arg( y[i], 0,
'f' );
715 points += QStringLiteral(
"(%1, %2)\n" ).arg( x[i] * RAD_TO_DEG, 0,
'f' ).arg( y[i] * RAD_TO_DEG, 0,
'f' );
720 QString dir = ( direction ==
ForwardTransform ) ? QObject::tr(
"forward transform" ) : QObject::tr(
"inverse transform" );
722 #if PROJ_VERSION_MAJOR>=6 723 QgsProjUtils::proj_pj_unique_ptr src( proj_get_source_crs(
QgsProjContext::get(), projData ) );
724 QgsProjUtils::proj_pj_unique_ptr dest( proj_get_source_crs(
QgsProjContext::get(), projData ) );
725 QString msg = QObject::tr(
"%1 of\n" 732 QString::fromUtf8( proj_errno_string( projResult ) ) );
734 char *srcdef = pj_get_def( sourceProj, 0 );
735 char *dstdef = pj_get_def( destProj, 0 );
737 QString msg = QObject::tr(
"%1 of\n" 744 QString::fromUtf8( pj_strerrno( projResult ) ) );
750 QgsDebugMsg(
"Projection failed emitting invalid transform signal: " + msg );
751 QgsDebugMsg( QStringLiteral(
"throwing exception" ) );
756 #if PROJ_VERSION_MAJOR<6 762 for (
int i = 0; i < numPoints; ++i )
769 #ifdef COORDINATE_TRANSFORM_VERBOSE 770 QgsDebugMsg( QStringLiteral(
"[[[[[[ Projected %1, %2 to %3, %4 ]]]]]]" )
771 .arg( xorg, 0,
'g', 15 ).arg( yorg, 0,
'g', 15 )
772 .arg( *x, 0,
'g', 15 ).arg( *y, 0,
'g', 15 ) );
783 return !d->mIsValid || d->mShortCircuit;
788 return d->mProjCoordinateOperation;
794 d->mProjCoordinateOperation = operation;
801 proj = QApplication::applicationDirPath()
802 +
"/share/proj/" + QString( name );
806 return proj.toUtf8();
809 #if PROJ_VERSION_MAJOR>=6 815 const QString sourceKey = src.
authid().isEmpty() ?
817 const QString destKey = dest.
authid().isEmpty() ?
820 if ( sourceKey.isEmpty() || destKey.isEmpty() )
827 const QList< QgsCoordinateTransform > values = sTransforms.values( qMakePair( sourceKey, destKey ) );
828 for (
auto valIt = values.constBegin(); valIt != values.constEnd(); ++valIt )
830 if ( ( *valIt ).coordinateOperation() == coordinateOperationProj )
835 bool hasContext = mHasContext;
842 mHasContext = hasContext;
856 const QString sourceKey = src.
authid().isEmpty() ?
858 const QString destKey = dest.
authid().isEmpty() ?
861 if ( sourceKey.isEmpty() || destKey.isEmpty() )
868 const QList< QgsCoordinateTransform > values = sTransforms.values( qMakePair( src.
authid(), dest.
authid() ) );
869 for (
auto valIt = values.constBegin(); valIt != values.constEnd(); ++valIt )
872 if ( ( *valIt ).sourceDatumTransformId() == srcDatumTransform &&
873 ( *valIt ).destinationDatumTransformId() == destDatumTransform )
878 bool hasContext = mHasContext;
885 mHasContext = hasContext;
896 void QgsCoordinateTransform::addToCache()
898 if ( !d->mSourceCRS.isValid() || !d->mDestCRS.isValid() )
901 const QString sourceKey = d->mSourceCRS.authid().isEmpty() ?
902 d->mSourceCRS.toWkt() : d->mSourceCRS.authid();
903 const QString destKey = d->mDestCRS.authid().isEmpty() ?
904 d->mDestCRS.toWkt() : d->mDestCRS.authid();
906 if ( sourceKey.isEmpty() || destKey.isEmpty() )
913 sTransforms.insertMulti( qMakePair( sourceKey, destKey ), *
this );
919 return d->mSourceDatumTransform;
927 d->mSourceDatumTransform = dt;
934 return d->mDestinationDatumTransform;
942 d->mDestinationDatumTransform = dt;
954 sDisableCache =
true;
960 #if PROJ_VERSION_MAJOR>=6 961 void QgsCoordinateTransform::removeFromCacheObjectsBelongingToCurrentThread(
void *pj_context )
974 for (
auto it = sTransforms.begin(); it != sTransforms.end(); )
976 auto &v = it.value();
977 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
978 it = sTransforms.erase( it );
989 double distSourceUnits = std::sqrt( source1.sqrDist( source2 ) );
992 double distDestUnits = std::sqrt( dest1.sqrDist( dest2 ) );
993 return distDestUnits / distSourceUnits;
998 QgsCoordinateTransformPrivate::setCustomMissingRequiredGridHandler( handler );
1003 QgsCoordinateTransformPrivate::setCustomMissingPreferredGridHandler( handler );
1008 QgsCoordinateTransformPrivate::setCustomCoordinateOperationCreationErrorHandler( handler );
1013 QgsCoordinateTransformPrivate::setCustomMissingGridUsedByContextHandler( 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.
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).
static PJ_CONTEXT * get()
Returns a thread local instance of a proj context, safe for use in the current thread.
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.