23#include <QRegularExpression>
27#include <proj_experimental.h>
29#if defined(USE_THREAD_LOCAL) && !defined(Q_OS_WIN)
32QThreadStorage< QgsProjContext * > QgsProjContext::sProjContext;
37 mContext = proj_context_create();
44 QgsCoordinateTransform::removeFromCacheObjectsBelongingToCurrentThread( mContext );
45 QgsCoordinateReferenceSystem::removeFromCacheObjectsBelongingToCurrentThread( mContext );
46 proj_context_destroy( mContext );
51#if defined(USE_THREAD_LOCAL) && !defined(Q_OS_WIN)
52 return sProjContext.mContext;
55 if ( sProjContext.hasLocalData() )
57 pContext = sProjContext.localData()->mContext;
62 pContext = sProjContext.localData()->mContext;
70 proj_destroy(
object );
75 const QString crsDef = QStringLiteral(
"%1 +type=crs" ).arg( projDef );
78 if ( !projSingleOperation )
82 if ( !coordinateSystem )
85 const int axisCount = proj_cs_get_axis_count( context, coordinateSystem.get() );
88 const char *outUnitAuthName =
nullptr;
89 const char *outUnitAuthCode =
nullptr;
91 proj_cs_get_axis_info( context, coordinateSystem.get(), 0,
100 if ( outUnitAuthName && outUnitAuthCode )
102 const char *unitCategory =
nullptr;
103 if ( proj_uom_get_info_from_database( context, outUnitAuthName, outUnitAuthCode,
nullptr,
nullptr, &unitCategory ) )
105 return QString( unitCategory ).compare( QLatin1String(
"angular" ), Qt::CaseInsensitive ) == 0;
123 const int axisCount = proj_cs_get_axis_count( context, pjCs.get() );
126 const char *outDirection =
nullptr;
129 proj_cs_get_axis_info( context, pjCs.get(), 0,
138 return QString( outDirection ).compare( QLatin1String(
"north" ), Qt::CaseInsensitive ) == 0;
154 proj_pj_unique_ptr datum( candidate ? proj_crs_get_datum( context, candidate.get() ) :
nullptr );
157 const PJ_TYPE type = proj_get_type( datum.get() );
158 isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
159 type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
162 const QString authName( proj_get_id_auth_name( datum.get(), 0 ) );
163 const QString code( proj_get_id_code( datum.get(), 0 ) );
164 if ( authName == QLatin1String(
"EPSG" ) && code == QLatin1String(
"6326" ) )
172 proj_pj_unique_ptr ensemble( candidate ? proj_crs_get_datum_ensemble( context, candidate.get() ) :
nullptr );
175 proj_pj_unique_ptr member( proj_datum_ensemble_get_member( context, ensemble.get(), 0 ) );
178 const PJ_TYPE type = proj_get_type( member.get() );
179 isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
180 type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
193 switch ( proj_get_type(
crs ) )
195 case PJ_TYPE_COMPOUND_CRS:
199 while ( res && ( proj_get_type( res.get() ) == PJ_TYPE_VERTICAL_CRS || proj_get_type( res.get() ) == PJ_TYPE_TEMPORAL_CRS ) )
202 res.reset( proj_crs_get_sub_crs( context,
crs, i ) );
207 case PJ_TYPE_VERTICAL_CRS:
227 switch ( proj_get_type(
crs ) )
229 case PJ_TYPE_COMPOUND_CRS:
233 while ( res && ( proj_get_type( res.get() ) != PJ_TYPE_VERTICAL_CRS ) )
236 res.reset( proj_crs_get_sub_crs( context,
crs, i ) );
241 case PJ_TYPE_VERTICAL_CRS:
259 switch ( proj_get_type(
crs ) )
261 case PJ_TYPE_BOUND_CRS:
280#if PROJ_VERSION_MAJOR>=8
291 throw QgsNotSupportedException( QObject::tr(
"Calculating datum ensembles requires a QGIS build based on PROJ 8.0 or later" ) );
297 if ( !horizontalCrs || !verticalCrs )
303 const_cast< PJ *
>( horizontalCrs ),
304 const_cast< PJ *
>( verticalCrs ) ) );
315 int *confidence =
nullptr;
318 const int count = proj_list_get_count( crsList );
319 int bestConfidence = 0;
321 for (
int i = 0; i < count; ++i )
323 if ( confidence[i] >= bestConfidence )
326 switch ( proj_get_type( candidateCrs.get() ) )
328 case PJ_TYPE_BOUND_CRS:
341 const QString authName( proj_get_id_auth_name( candidateCrs.get(), 0 ) );
343 if ( confidence[i] > bestConfidence || ( confidence[i] == bestConfidence && authName == QLatin1String(
"EPSG" ) ) )
345 bestConfidence = confidence[i];
346 matchedCrs = std::move( candidateCrs );
350 proj_list_destroy( crsList );
351 proj_int_list_destroy( confidence );
352 if ( matchedCrs && bestConfidence >= 70 )
354 authName = QString( proj_get_id_auth_name( matchedCrs.get(), 0 ) );
355 authCode = QString( proj_get_id_code( matchedCrs.get(), 0 ) );
358 return !authName.isEmpty() && !authCode.isEmpty();
363 if ( projDef.isEmpty() )
368 if ( !coordinateOperation )
371 return static_cast< bool >( proj_coordoperation_is_instantiable( context, coordinateOperation.get() ) );
376 const thread_local QRegularExpression regex( QStringLiteral(
"\\+(?:nad)?grids=(.*?)\\s" ) );
378 QList< QgsDatumTransform::GridDetails > grids;
379 QRegularExpressionMatchIterator matches = regex.globalMatch( proj );
380 while ( matches.hasNext() )
382 const QRegularExpressionMatch match = matches.next();
383 const QString gridName = match.captured( 1 );
386 const char *fullName =
nullptr;
387 const char *packageName =
nullptr;
388 const char *url =
nullptr;
389 int directDownload = 0;
392 proj_grid_get_info_from_database(
QgsProjContext::get(), gridName.toUtf8().constData(), &fullName, &packageName, &url, &directDownload, &openLicense, &available );
393 grid.
fullName = QString( fullName );
395 grid.
url = QString( url );
399 grids.append( grid );
405QStringList QgsProjUtils::nonAvailableGrids(
const QString &projDef )
407 if ( projDef.isEmpty() )
408 return QStringList();
413 return QStringList();
416 for (
int j = 0; j < proj_coordoperation_get_grid_used_count( context, op.get() ); ++j )
418 const char *shortName =
nullptr;
420 proj_coordoperation_get_grid_used( context, op.get(), j, &shortName,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr, &isAvailable );
422 res << QString( shortName );
430 return PROJ_VERSION_MAJOR;
435 return PROJ_VERSION_MINOR;
441 const char *version = proj_context_get_database_metadata( context,
"EPSG.VERSION" );
442 return QString( version );
448 const char *date = proj_context_get_database_metadata( context,
"EPSG.DATE" );
449 return QDate::fromString( date, Qt::DateFormat::ISODate );
455 const char *version = proj_context_get_database_metadata( context,
"ESRI.VERSION" );
456 return QString( version );
462 const char *date = proj_context_get_database_metadata( context,
"ESRI.DATE" );
463 return QDate::fromString( date, Qt::DateFormat::ISODate );
469 const char *version = proj_context_get_database_metadata( context,
"IGNF.VERSION" );
470 return QString( version );
476 const char *date = proj_context_get_database_metadata( context,
"IGNF.DATE" );
477 return QDate::fromString( date, Qt::DateFormat::ISODate );
482 const QString path( proj_info().searchpath );
485 paths = path.split(
';' );
487 paths = path.split(
':' );
490 QSet<QString> existing;
493 res.reserve( paths.count() );
494 for (
const QString &p : std::as_const( paths ) )
496 if ( existing.contains( p ) )
499 existing.insert( p );
Custom exception class which is raised when an operation is not supported.
Used to create and store a proj context object, correctly freeing the context upon destruction.
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...
@ FlagMatchBoundCrsToUnderlyingSourceCrs
Allow matching a BoundCRS object to its underlying SourceCRS.
static QList< QgsDatumTransform::GridDetails > gridsUsed(const QString &proj)
Returns a list of grids used by the given proj string.
static bool isDynamic(const PJ *crs)
Returns true if the given proj coordinate system is a dynamic CRS.
static QDate epsgRegistryDate()
Returns the EPSG registry database release date used by the proj library.
static proj_pj_unique_ptr unboundCrs(const PJ *crs)
Given a PROJ crs (which may be a compound or bound crs, or some other type), ensure that it is not a ...
static bool identifyCrs(const PJ *crs, QString &authName, QString &authCode, IdentifyFlags flags=IdentifyFlags())
Attempts to identify a crs, matching it to a known authority and code within an acceptable level of t...
static proj_pj_unique_ptr createCompoundCrs(const PJ *horizontalCrs, const PJ *verticalCrs)
Given a PROJ horizontal and vertical CRS, attempt to create a compound CRS from them.
static QStringList searchPaths()
Returns the current list of Proj file search paths.
static proj_pj_unique_ptr crsToVerticalCrs(const PJ *crs)
Given a PROJ crs (which may be a compound crs, or some other type), extract the vertical crs from it.
static QString ignfDatabaseVersion()
Returns the IGNF database version used by the proj library (e.g.
static proj_pj_unique_ptr crsToDatumEnsemble(const PJ *crs)
Given a PROJ crs, attempt to retrieve the datum ensemble from it.
QFlags< IdentifyFlag > IdentifyFlags
static bool coordinateOperationIsAvailable(const QString &projDef)
Returns true if a coordinate operation (specified via proj string) is available.
static QString epsgRegistryVersion()
Returns the EPSG registry database version used by the proj library (e.g.
static QDate esriDatabaseDate()
Returns the ESRI projection engine database release date used by the proj library.
std::unique_ptr< PJ, ProjPJDeleter > proj_pj_unique_ptr
Scoped Proj PJ object.
static bool usesAngularUnit(const QString &projDef)
Returns true if the given proj coordinate system uses angular units.
static bool axisOrderIsSwapped(const PJ *crs)
Returns true if the given proj coordinate system uses requires y/x coordinate order instead of x/y.
static QString esriDatabaseVersion()
Returns the ESRI projection engine database version used by the proj library (e.g.
static int projVersionMajor()
Returns the proj library major version number.
static QDate ignfDatabaseDate()
Returns the IGNF database release date used by the proj library.
static int projVersionMinor()
Returns the proj library minor version number.
#define BUILTIN_UNREACHABLE
struct projCtx_t PJ_CONTEXT
const QgsCoordinateReferenceSystem & crs
void CORE_EXPORT operator()(PJ *object) const
Destroys an PJ object, using the correct proj calls.