34using namespace Qt::StringLiterals;
39#include <QApplication>
55QReadWriteLock QgsCoordinateTransform::sCacheLock;
57bool QgsCoordinateTransform::sDisableCache =
false;
60 QgsCoordinateTransform::sFallbackOperationOccurredHandler =
nullptr;
64 d =
new QgsCoordinateTransformPrivate();
72 d =
new QgsCoordinateTransformPrivate( source, destination, mContext );
75 mIgnoreImpossible =
true;
87 if ( !d->checkValidity() )
91 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
99 mBallparkTransformsAreAppropriate =
true;
107 d =
new QgsCoordinateTransformPrivate( source, destination, mContext );
114 mIgnoreImpossible =
true;
122 if ( !d->checkValidity() )
126 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
134 mBallparkTransformsAreAppropriate =
true;
139 d =
new QgsCoordinateTransformPrivate( source, destination, sourceDatumTransform, destinationDatumTransform );
144 if ( !d->checkValidity() )
148 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
157 : mContext( o.mContext )
159 , mHasContext( o.mHasContext )
165 , mIgnoreImpossible( false )
166 , mBallparkTransformsAreAppropriate( false )
167 , mDisableFallbackHandler( false )
168 , mFallbackOperationOccurred( false )
180 mHasContext = o.mHasContext;
182 mContext = o.mContext;
183 mLastError = QString();
192 return d->mSourceCRS == other.d->mSourceCRS
193 && d->mDestCRS == other.d->mDestCRS
194 && mBallparkTransformsAreAppropriate == other.mBallparkTransformsAreAppropriate
195 && d->mProjCoordinateOperation == other.d->mProjCoordinateOperation
201 return !( *
this == other );
226 if ( !d->checkValidity() )
229 d->calculateTransforms( mContext );
231 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
249 if ( !d->checkValidity() )
252 d->calculateTransforms( mContext );
254 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
276 if ( !d->checkValidity() )
279 d->calculateTransforms( mContext );
281 if ( !setFromCache( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation, d->mAllowFallbackTransforms ) )
296 return d->mSourceCRS;
306 if ( !d->mIsValid || d->mShortCircuit )
310 double x = point.
x();
311 double y = point.
y();
344 if ( !d->mIsValid || d->mShortCircuit )
368#ifdef COORDINATE_TRANSFORM_VERBOSE
380 double x = point.
x();
381 double y = point.
y();
382 double z = point.
z();
398 if ( !d->mIsValid || d->mShortCircuit )
418 double xd =
static_cast< double >( x ), yd =
static_cast< double >( y );
426 if ( !d->mIsValid || d->mShortCircuit )
452 if ( !d->mIsValid || d->mShortCircuit )
458 const int nVertices = poly.size();
460 QVector<double> x( nVertices );
461 QVector<double> y( nVertices );
462 QVector<double> z( nVertices );
463 double *destX = x.data();
464 double *destY = y.data();
465 double *destZ = z.data();
467 const QPointF *polyData = poly.constData();
468 for (
int i = 0; i < nVertices; ++i )
470 *destX++ = polyData->x();
471 *destY++ = polyData->y();
487 QPointF *destPoint = poly.data();
488 const double *srcX = x.constData();
489 const double *srcY = y.constData();
490 for (
int i = 0; i < nVertices; ++i )
492 destPoint->rx() = *srcX++;
493 destPoint->ry() = *srcY++;
498 if ( !err.isEmpty() )
504 if ( !d->mIsValid || d->mShortCircuit )
507 Q_ASSERT( x.size() == y.size() );
529 if ( !d->mIsValid || d->mShortCircuit )
532 Q_ASSERT( x.size() == y.size() );
542 const int vectorSize = x.size();
543 QVector<double> xd( x.size() );
544 QVector<double> yd( y.size() );
545 QVector<double> zd( z.size() );
547 double *destX = xd.data();
548 double *destY = yd.data();
549 double *destZ = zd.data();
551 const float *srcX = x.constData();
552 const float *srcY = y.constData();
553 const float *srcZ = z.constData();
555 for (
int i = 0; i < vectorSize; ++i )
557 *destX++ =
static_cast< double >( *srcX++ );
558 *destY++ =
static_cast< double >( *srcY++ );
559 *destZ++ =
static_cast< double >( *srcZ++ );
565 float *destFX = x.data();
566 float *destFY = y.data();
567 float *destFZ = z.data();
568 const double *srcXD = xd.constData();
569 const double *srcYD = yd.constData();
570 const double *srcZD = zd.constData();
571 for (
int i = 0; i < vectorSize; ++i )
573 *destFX++ =
static_cast< float >( *srcXD++ );
574 *destFY++ =
static_cast< float >( *srcYD++ );
575 *destFZ++ =
static_cast< float >( *srcZD++ );
593 if ( !d->mIsValid || d->mShortCircuit )
605 QgsDebugMsgLevel( u
"No QgsCoordinateTransformContext context set for transform"_s, 4 );
613 throw QgsCsException( QObject::tr(
"Could not transform bounding box for geocentric CRS %1" ).arg( d->mSourceCRS.authid() ) );
617 throw QgsCsException( QObject::tr(
"Could not transform bounding box for geocentric CRS %1" ).arg( d->mDestCRS.authid() ) );
620 const double xMin = rect.
xMinimum();
621 const double xMax = rect.
xMaximum();
631 constexpr double EPS = 1e-1;
632 if ( yMin < -90 + EPS )
634 if ( yMax < -90 + EPS )
635 throw QgsCsException( QObject::tr(
"Could not transform bounding box to target CRS" ) );
638 if ( yMax > 90 - EPS )
640 if ( yMin > 90 - EPS )
641 throw QgsCsException( QObject::tr(
"Could not transform bounding box to target CRS" ) );
650#if PROJ_VERSION_MAJOR < 9 || ( PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR < 7 )
651 const auto legacyImplementation = [
this, &rect, xMin, yMin, yMax, direction, handle180Crossover]() {
658 const int nPoints = 1000;
659 const double dst = std::sqrt( ( rect.
width() * ( yMax - yMin ) ) / std::pow( std::sqrt(
static_cast< double >( nPoints ) ) - 1, 2.0 ) );
660 const int nXPoints =
static_cast<int>( std::clamp( std::ceil( rect.
width() / dst ) + 1, 3.0, 1000.0 ) );
661 const int nYPoints =
static_cast<int>( std::clamp( std::ceil( ( yMax - yMin ) / dst ) + 1, 3.0, 1000.0 ) );
666 std::vector<double> x( nXPoints *
static_cast< std::size_t
>( nYPoints ) );
667 std::vector<double> y( nXPoints *
static_cast< std::size_t
>( nYPoints ) );
668 std::vector<double> z( nXPoints *
static_cast< std::size_t
>( nYPoints ) );
672 const double dx = rect.
width() /
static_cast< double >( nXPoints - 1 );
673 const double dy = ( yMax - yMin ) /
static_cast< double >( nYPoints - 1 );
675 double pointY = yMin;
677 for (
int i = 0; i < nYPoints; i++ )
680 double pointX = xMin;
682 for (
int j = 0; j < nXPoints; j++ )
684 x[( i * nXPoints ) + j] = pointX;
685 y[( i * nXPoints ) + j] = pointY;
687 z[( i * nXPoints ) + j] = 0.0;
698 transformCoords( nXPoints * nYPoints, x.data(), y.data(), z.data(), direction );
708 bool doHandle180Crossover =
false;
711 const double xMin = std::fmod( x[0], 180.0 );
712 const double xMax = std::fmod( x[nXPoints - 1], 180.0 );
713 if ( handle180Crossover
720 doHandle180Crossover =
true;
725 for (
int i = 0; i < nXPoints * nYPoints; i++ )
727 if ( !std::isfinite( x[i] ) || !std::isfinite( y[i] ) )
732 if ( doHandle180Crossover )
746 throw QgsCsException( QObject::tr(
"Could not transform bounding box to target CRS" ) );
749 if ( doHandle180Crossover )
770#if PROJ_VERSION_MAJOR > 8 || ( PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR >= 2 )
772#if PROJ_VERSION_MAJOR < 9 || ( PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR < 7 )
779 return legacyImplementation();
783 ProjData projData = d->threadLocalProjData();
786#if PROJ_VERSION_MAJOR < 9 || ( PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR < 6 )
800 transform2D.reset( proj_create_crs_to_crs_from_pj( projContext, srcCrsHorizontal.get(), destCrsHorizontal.get(),
nullptr,
nullptr ) );
803 const QString err = u
"proj_create_crs_to_crs_from_pj(horizontalCrs(%1), horizontalCrs(%2)) failed"_s.arg( d->mSourceCRS.authid(), d->mSourceCRS.authid() );
804 throw QgsCsException( QObject::tr(
"Could not transform bounding box to target CRS: %1" ).arg( err ) );
806 transform2D.reset( proj_normalize_for_visualization( projContext, transform2D.get() ) );
809 const QString err = u
"Cannot normalize transform between horizontalCrs(%1) and horizontalCrs(%2)"_s.arg( d->mSourceCRS.authid(), d->mDestCRS.authid() );
810 throw QgsCsException( QObject::tr(
"Could not transform bounding box to target CRS: %1" ).arg( err ) );
812 projData = transform2D.get();
816 double transXMin = 0;
817 double transYMin = 0;
818 double transXMax = 0;
819 double transYMax = 0;
821 proj_errno_reset( projData );
824 constexpr int DENSIFY_POINTS = 30;
825 int projResult = proj_trans_bounds(
841 ( projResult != 1 || !std::isfinite( transXMin ) || !std::isfinite( transXMax ) || !std::isfinite( transYMin ) || !std::isfinite( transYMax ) )
842 && ( d->mAvailableOpCount > 1 || d->mAvailableOpCount == -1 )
846 if (
PJ *
transform = d->threadLocalFallbackProjData() )
848 projResult = proj_trans_bounds(
865 if ( projResult != 1 || !std::isfinite( transXMin ) || !std::isfinite( transXMax ) || !std::isfinite( transYMin ) || !std::isfinite( transYMax ) )
867#if PROJ_VERSION_MAJOR < 9 || ( PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR < 7 )
871 return legacyImplementation();
873 const QString projErr = QString::fromUtf8( proj_context_errno_string( projContext, proj_errno( projData ) ) );
875 const QString msg = QObject::tr(
"%1 (%2 to %3) of bounding box failed: %4" )
888 bool doHandle180Crossover =
false;
890 if ( handle180Crossover
892 && ( transXMax < transXMin ) )
895 std::swap( transXMax, transXMin );
900 doHandle180Crossover =
true;
903 QgsRectangle boundingBoxRect { transXMin, transYMin, transXMax, transYMax };
904 if ( boundingBoxRect.
isNull() )
907 throw QgsCsException( QObject::tr(
"Could not transform bounding box to target CRS" ) );
910 if ( doHandle180Crossover )
913 if ( boundingBoxRect.
xMinimum() > 180.0 )
915 if ( boundingBoxRect.
xMaximum() > 180.0 )
921 if ( boundingBoxRect.
isEmpty() )
926 return boundingBoxRect;
928#if PROJ_VERSION_MAJOR < 9 || ( PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR < 7 )
929 return legacyImplementation();
935 if ( !d->mIsValid || d->mShortCircuit || box.
isNull() )
941 QgsDebugMsgLevel( u
"No QgsCoordinateTransformContext context set for transform"_s, 4 );
950#if PROJ_VERSION_MAJOR < 9 || ( PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR < 6 )
952 const auto legacy = [
this, &box, zMin, zMax, direction]() {
953 constexpr int POINTS_PER_AXIS = 31;
954 const int nX = box.
width() > 0 ? POINTS_PER_AXIS : 1;
955 const int nY = box.
height() > 0 ? POINTS_PER_AXIS : 1;
956 const int nZ = zMax > zMin ? POINTS_PER_AXIS : 1;
958 std::vector< double > x;
959 std::vector< double > y;
960 std::vector< double > z;
961 const std::size_t pointCount =
static_cast< std::size_t
>( nX ) *
static_cast< std::size_t
>( nY ) *
static_cast< std::size_t
>( nZ );
962 x.reserve( pointCount );
963 y.reserve( pointCount );
964 z.reserve( pointCount );
966 for (
int i = 0; i < nX; ++i )
969 for (
int j = 0; j < nY; ++j )
972 for (
int k = 0; k < nZ; ++k )
974 const double pointZ = nZ == 1 ? zMin : zMin + ( zMax - zMin ) * k / ( nZ - 1 );
975 x.push_back( pointX );
976 y.push_back( pointY );
977 z.push_back( pointZ );
982 transformCoords(
static_cast< int >( pointCount ), x.data(), y.data(), z.data(), direction );
986 for ( std::size_t i = 0; i < pointCount; ++i )
988 if ( !std::isfinite( x[i] ) || !std::isfinite( y[i] ) || !std::isfinite( z[i] ) )
996 throw QgsCsException( QObject::tr(
"Could not transform box to target CRS" ) );
1007 ProjData projData = d->threadLocalProjData();
1017 constexpr int DENSIFY_POINTS = 30;
1019 proj_errno_reset( projData );
1020 int projResult = proj_trans_bounds_3D(
1040 ( projResult != 1 || !std::isfinite( outXMin ) || !std::isfinite( outXMax ) || !std::isfinite( outYMin ) || !std::isfinite( outYMax ) || !std::isfinite( outZMin ) || !std::isfinite( outZMax ) )
1041 && ( d->mAvailableOpCount > 1 || d->mAvailableOpCount == -1 )
1045 if (
PJ *
transform = d->threadLocalFallbackProjData() )
1047 projResult = proj_trans_bounds_3D(
1068 if ( projResult != 1 || !std::isfinite( outXMin ) || !std::isfinite( outXMax ) || !std::isfinite( outYMin ) || !std::isfinite( outYMax ) || !std::isfinite( outZMin ) || !std::isfinite( outZMax ) )
1070 throw QgsCsException( QObject::tr(
"Could not transform box to target CRS" ) );
1073 const QgsBox3D result( outXMin, outYMin, outZMin, outXMax, outYMax, outZMax );
1081 if ( !d->mIsValid || d->mShortCircuit )
1084 if ( !d->mSourceCRS.isValid() )
1088 "The source spatial reference system (CRS) is not valid. "
1089 "The coordinates can not be reprojected. The CRS is: %1"
1091 .arg( d->mSourceCRS.toProj() ),
1092 QObject::tr(
"CRS" )
1096 if ( !d->mDestCRS.isValid() )
1100 "The destination spatial reference system (CRS) is not valid. "
1101 "The coordinates can not be reprojected. The CRS is: %1"
1103 .arg( d->mDestCRS.toProj() ),
1104 QObject::tr(
"CRS" )
1109 std::vector< int > zNanPositions;
1110 for (
int i = 0; i < numPoints; i++ )
1112 if ( std::isnan( z[i] ) )
1114 zNanPositions.push_back( i );
1119 std::vector< double > xprev( numPoints );
1120 memcpy( xprev.data(), x,
sizeof(
double ) * numPoints );
1121 std::vector< double > yprev( numPoints );
1122 memcpy( yprev.data(), y,
sizeof(
double ) * numPoints );
1123 std::vector< double > zprev( numPoints );
1124 memcpy( zprev.data(), z,
sizeof(
double ) * numPoints );
1126 const bool useTime = !std::isnan( d->mDefaultTime );
1127 std::vector< double > t( useTime ? numPoints : 0, d->mDefaultTime );
1129#ifdef COORDINATE_TRANSFORM_VERBOSE
1132 QgsDebugMsgLevel( u
"[[[[[[ Number of points to transform: %1 ]]]]]]"_s.arg( numPoints ), 2 );
1138 QgsDebugMsgLevel( u
"No QgsCoordinateTransformContext context set for transform"_s, 4 );
1143 ProjData projData = d->threadLocalProjData();
1147 proj_errno_reset( projData );
1160 useTime ? t.data() :
nullptr,
1162 useTime ? numPoints : 0
1173 if ( numPoints == 1 )
1175 projResult = proj_errno( projData );
1176 actualRes = projResult;
1180 actualRes = proj_errno( projData );
1182 if ( actualRes == 0 )
1186 if ( std::any_of( x, x + numPoints, [](
double v ) {
return std::isinf( v ); } )
1187 || std::any_of( y, y + numPoints, [](
double v ) {
return std::isinf( v ); } )
1188 || std::any_of( z, z + numPoints, [](
double v ) {
return std::isinf( v ); } ) )
1194 mFallbackOperationOccurred =
false;
1195 bool errorOccurredDuringFallbackOperation =
false;
1197 && ( d->mAvailableOpCount > 1 || d->mAvailableOpCount == -1 )
1198 && ( d->mAllowFallbackTransforms || mBallparkTransformsAreAppropriate ) )
1201 if (
PJ *
transform = d->threadLocalFallbackProjData() )
1205 memcpy( x, xprev.data(),
sizeof(
double ) * numPoints );
1206 memcpy( y, yprev.data(),
sizeof(
double ) * numPoints );
1207 memcpy( z, zprev.data(),
sizeof(
double ) * numPoints );
1220 useTime ? t.data() :
nullptr,
1222 useTime ? numPoints : 0
1232 if ( numPoints == 1 )
1238 errorOccurredDuringFallbackOperation = std::isinf( x[0] ) || std::isinf( y[0] ) || std::isinf( z[0] );
1241 if ( !errorOccurredDuringFallbackOperation )
1243 mFallbackOperationOccurred =
true;
1246 if ( !mBallparkTransformsAreAppropriate && !mDisableFallbackHandler && sFallbackOperationOccurredHandler )
1248 sFallbackOperationOccurredHandler( d->mSourceCRS, d->mDestCRS, d->mProjCoordinateOperation );
1250 const QString warning = u
"A fallback coordinate operation was used between %1 and %2"_s.arg( d->mSourceCRS.authid(),
1251 d->mDestCRS.authid() );
1252 qWarning(
"%s", warning.toLatin1().constData() );
1258 for (
const int &pos : zNanPositions )
1260 z[pos] = std::numeric_limits<double>::quiet_NaN();
1263 if ( projResult != 0 || errorOccurredDuringFallbackOperation )
1268 const QChar delim = numPoints > 1 ?
'\n' :
' ';
1269 for (
int i = 0; i < numPoints; ++i )
1271 points += u
"(%1, %2)"_s.arg( xprev[i], 0,
'f' ).arg( yprev[i], 0,
'f' ) + delim;
1277 const QString projError = !errorOccurredDuringFallbackOperation ? QString::fromUtf8( proj_context_errno_string( projContext, projResult ) ) : QObject::tr(
"Fallback transform failed" );
1279 const QString msg = QObject::tr(
"%1 (%2 to %3) of%4%5Error: %6" )
1290 if ( msg != mLastError )
1292 QgsDebugError(
"Projection failed emitting invalid transform signal: " + msg );
1300#ifdef COORDINATE_TRANSFORM_VERBOSE
1301 QgsDebugMsgLevel( u
"[[[[[[ Projected %1, %2 to %3, %4 ]]]]]]"_s.arg( xorg, 0,
'g', 15 ).arg( yorg, 0,
'g', 15 ).arg( *x, 0,
'g', 15 ).arg( *y, 0,
'g', 15 ), 2 );
1312 return !d->mIsValid || d->mShortCircuit;
1317 return d->mIsValid && d->mHasVerticalComponent;
1322 return d->mProjCoordinateOperation;
1327 ProjData projData = d->threadLocalProjData();
1334 d->mProjCoordinateOperation = operation;
1335 d->mShouldReverseCoordinateOperation =
false;
1341 d->mAllowFallbackTransforms = allowed;
1346 return d->mAllowFallbackTransforms;
1351 mBallparkTransformsAreAppropriate = appropriate;
1356 mDisableFallbackHandler = disabled;
1361 return mFallbackOperationOccurred;
1368 proj = QApplication::applicationDirPath() +
"/share/proj/" + QString( name );
1372 return proj.toUtf8();
1383 if ( sourceKey.isEmpty() || destKey.isEmpty() )
1387 if ( sDisableCache )
1390 const QList< QgsCoordinateTransform > values = sTransforms.values( qMakePair( sourceKey, destKey ) );
1391 for (
auto valIt = values.constBegin(); valIt != values.constEnd(); ++valIt )
1393 if ( ( *valIt ).coordinateOperation() == coordinateOperationProj
1394 && ( *valIt ).allowFallbackTransforms() == allowFallback
1399 const QgsCoordinateTransformContext
context = mContext;
1401 const bool hasContext = mHasContext;
1408 mHasContext = hasContext;
1417void QgsCoordinateTransform::addToCache()
1419 if ( !d->mSourceCRS.isValid() || !d->mDestCRS.isValid() )
1425 if ( sourceKey.isEmpty() || destKey.isEmpty() )
1429 if ( sDisableCache )
1432 sTransforms.insert( qMakePair( sourceKey, destKey ), *
this );
1438 return d->mSourceDatumTransform;
1446 d->mSourceDatumTransform = dt;
1453 return d->mDestinationDatumTransform;
1461 d->mDestinationDatumTransform = dt;
1468 if ( sDisableCache )
1473 sDisableCache =
true;
1476 sTransforms.clear();
1479void QgsCoordinateTransform::removeFromCacheObjectsBelongingToCurrentThread(
void *pj_context )
1485 if ( sDisableCache )
1490 if ( sDisableCache )
1493 for (
auto it = sTransforms.begin(); it != sTransforms.end(); )
1495 auto &v = it.value();
1496 if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
1497 it = sTransforms.erase( it );
1507 const double distSourceUnits = std::sqrt( source1.
sqrDist( source2 ) );
1510 const double distDestUnits = std::sqrt( dest1.
sqrDist( dest2 ) );
1511 return distDestUnits / distSourceUnits;
1518 QgsCoordinateTransformPrivate::setCustomMissingRequiredGridHandler( handler );
1525 QgsCoordinateTransformPrivate::setCustomMissingPreferredGridHandler( handler );
1530 QgsCoordinateTransformPrivate::setCustomCoordinateOperationCreationErrorHandler( handler );
1537 QgsCoordinateTransformPrivate::setCustomMissingGridUsedByContextHandler( handler );
1542 sFallbackOperationOccurredHandler = handler;
1547 QgsCoordinateTransformPrivate::setDynamicCrsToDynamicCrsWarningHandler( handler );
@ Geocentric
Geocentric CRS.
QFlags< CoordinateTransformationFlag > CoordinateTransformationFlags
Coordinate transformation flags.
@ Preferred
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
@ BallparkTransformsAreAppropriate
Indicates that approximate "ballpark" results are appropriate for this coordinate transform....
@ IgnoreImpossibleTransformations
Indicates that impossible transformations (such as those which attempt to transform between two diffe...
TransformDirection
Indicates the direction (forward or inverse) of a transform.
@ Forward
Forward transform (from source to destination).
@ Reverse
Reverse/inverse transform (from destination to source).
A 3-dimensional box composed of x, y, z coordinates.
double yMaximum() const
Returns the maximum y value.
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin,zmin : xmax,ymax,zmax Coordinates will be truncated...
double xMinimum() const
Returns the minimum x value.
double zMaximum() const
Returns the maximum z value.
double xMaximum() const
Returns the maximum x value.
void combineWith(const QgsBox3D &box)
Expands the bbox so that it covers both the original rectangle and the given rectangle.
double width() const
Returns the width of the box.
void setNull()
Mark a box as being null (holding no spatial information).
double zMinimum() const
Returns the minimum z value.
double yMinimum() const
Returns the minimum y value.
double height() const
Returns the height of the box.
bool isNull() const
Test if the box is null (holding no spatial information).
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString toWkt(Qgis::CrsWktVariant variant=Qgis::CrsWktVariant::Wkt1Gdal, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
QString celestialBodyName() const
Attempts to retrieve the name of the celestial body associated with the CRS (e.g.
double coordinateEpoch() const
Returns the coordinate epoch, as a decimal year.
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, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
Adds a message to the log instance (and creates it if necessary).
double sqrDist(double x, double y) const
Returns the squared distance between this point a specified x, y coordinate.
static PJ_CONTEXT * get()
Returns a thread local instance of a proj context, safe for use in the current thread.
static proj_pj_unique_ptr crsToHorizontalCrs(const PJ *crs)
Given a PROJ crs (which may be a compound or bound crs, or some other type), extract the horizontal c...
static bool hasVerticalAxis(const PJ *crs)
Returns true if a PROJ crs has a vertical axis.
std::unique_ptr< PJ, ProjPJDeleter > proj_pj_unique_ptr
Scoped Proj PJ object.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
QgsCoordinateTransformContext transformContext
A convenience class that simplifies locking and unlocking QReadWriteLocks.
A rectangle specified with double values.
Q_INVOKABLE QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be rounded to the spec...
void setXMinimum(double x)
Set the minimum x value.
void setXMaximum(double x)
Set the maximum x value.
void combineExtentWith(const QgsRectangle &rect)
Expands the rectangle so that it covers both the original rectangle and the given rectangle.
void setNull()
Mark a rectangle as being null (holding no spatial information).
Scoped object for temporary suppression of PROJ logging output.
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
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)
#define QgsDebugError(str)