23#include <QRegularExpression>
28#if defined(USE_THREAD_LOCAL) && !defined(Q_OS_WIN)
31QThreadStorage< QgsProjContext * > QgsProjContext::sProjContext;
36 mContext = proj_context_create();
43 QgsCoordinateTransform::removeFromCacheObjectsBelongingToCurrentThread( mContext );
44 QgsCoordinateReferenceSystem::removeFromCacheObjectsBelongingToCurrentThread( mContext );
45 proj_context_destroy( mContext );
50#if defined(USE_THREAD_LOCAL) && !defined(Q_OS_WIN)
51 return sProjContext.mContext;
54 if ( sProjContext.hasLocalData() )
56 pContext = sProjContext.localData()->mContext;
61 pContext = sProjContext.localData()->mContext;
69 proj_destroy(
object );
74 const QString crsDef = QStringLiteral(
"%1 +type=crs" ).arg( projDef );
77 if ( !projSingleOperation )
81 if ( !coordinateSystem )
84 const int axisCount = proj_cs_get_axis_count( context, coordinateSystem.get() );
87 const char *outUnitAuthName =
nullptr;
88 const char *outUnitAuthCode =
nullptr;
90 proj_cs_get_axis_info( context, coordinateSystem.get(), 0,
99 if ( outUnitAuthName && outUnitAuthCode )
101 const char *unitCategory =
nullptr;
102 if ( proj_uom_get_info_from_database( context, outUnitAuthName, outUnitAuthCode,
nullptr,
nullptr, &unitCategory ) )
104 return QString( unitCategory ).compare( QLatin1String(
"angular" ), Qt::CaseInsensitive ) == 0;
122 const int axisCount = proj_cs_get_axis_count( context, pjCs.get() );
125 const char *outDirection =
nullptr;
128 proj_cs_get_axis_info( context, pjCs.get(), 0,
137 return QString( outDirection ).compare( QLatin1String(
"north" ), Qt::CaseInsensitive ) == 0;
153 proj_pj_unique_ptr datum( candidate ? proj_crs_get_datum( context, candidate.get() ) : nullptr );
156 const PJ_TYPE type = proj_get_type( datum.get() );
157 isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
158 type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
161 const QString authName( proj_get_id_auth_name( datum.get(), 0 ) );
162 const QString code( proj_get_id_code( datum.get(), 0 ) );
163 if ( authName == QLatin1String(
"EPSG" ) && code == QLatin1String(
"6326" ) )
171 proj_pj_unique_ptr ensemble( candidate ? proj_crs_get_datum_ensemble( context, candidate.get() ) : nullptr );
174 proj_pj_unique_ptr member( proj_datum_ensemble_get_member( context, ensemble.get(), 0 ) );
177 const PJ_TYPE type = proj_get_type( member.get() );
178 isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
179 type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
192 switch ( proj_get_type(
crs ) )
194 case PJ_TYPE_COMPOUND_CRS:
198 while ( res && ( proj_get_type( res.get() ) == PJ_TYPE_VERTICAL_CRS || proj_get_type( res.get() ) == PJ_TYPE_TEMPORAL_CRS ) )
201 res.reset( proj_crs_get_sub_crs( context,
crs, i ) );
206 case PJ_TYPE_VERTICAL_CRS:
226 switch ( proj_get_type(
crs ) )
228 case PJ_TYPE_BOUND_CRS:
247#if PROJ_VERSION_MAJOR>=8
258 throw QgsNotSupportedException( QObject::tr(
"Calculating datum ensembles requires a QGIS build based on PROJ 8.0 or later" ) );
270 int *confidence =
nullptr;
273 const int count = proj_list_get_count( crsList );
274 int bestConfidence = 0;
276 for (
int i = 0; i < count; ++i )
278 if ( confidence[i] >= bestConfidence )
281 switch ( proj_get_type( candidateCrs.get() ) )
283 case PJ_TYPE_BOUND_CRS:
296 const QString authName( proj_get_id_auth_name( candidateCrs.get(), 0 ) );
298 if ( confidence[i] > bestConfidence || ( confidence[i] == bestConfidence && authName == QLatin1String(
"EPSG" ) ) )
300 bestConfidence = confidence[i];
301 matchedCrs = std::move( candidateCrs );
305 proj_list_destroy( crsList );
306 proj_int_list_destroy( confidence );
307 if ( matchedCrs && bestConfidence >= 70 )
309 authName = QString( proj_get_id_auth_name( matchedCrs.get(), 0 ) );
310 authCode = QString( proj_get_id_code( matchedCrs.get(), 0 ) );
313 return !authName.isEmpty() && !authCode.isEmpty();
318 if ( projDef.isEmpty() )
323 if ( !coordinateOperation )
326 return static_cast< bool >( proj_coordoperation_is_instantiable( context, coordinateOperation.get() ) );
331 const thread_local QRegularExpression regex( QStringLiteral(
"\\+(?:nad)?grids=(.*?)\\s" ) );
333 QList< QgsDatumTransform::GridDetails > grids;
334 QRegularExpressionMatchIterator matches = regex.globalMatch( proj );
335 while ( matches.hasNext() )
337 const QRegularExpressionMatch match = matches.next();
338 const QString gridName = match.captured( 1 );
341 const char *fullName =
nullptr;
342 const char *packageName =
nullptr;
343 const char *url =
nullptr;
344 int directDownload = 0;
347 proj_grid_get_info_from_database(
QgsProjContext::get(), gridName.toUtf8().constData(), &fullName, &packageName, &url, &directDownload, &openLicense, &available );
348 grid.
fullName = QString( fullName );
350 grid.
url = QString( url );
354 grids.append( grid );
360QStringList QgsProjUtils::nonAvailableGrids(
const QString &projDef )
362 if ( projDef.isEmpty() )
363 return QStringList();
368 return QStringList();
371 for (
int j = 0; j < proj_coordoperation_get_grid_used_count( context, op.get() ); ++j )
373 const char *shortName =
nullptr;
375 proj_coordoperation_get_grid_used( context, op.get(), j, &shortName,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr, &isAvailable );
377 res << QString( shortName );
385 return PROJ_VERSION_MAJOR;
390 return PROJ_VERSION_MINOR;
396 const char *version = proj_context_get_database_metadata( context,
"EPSG.VERSION" );
397 return QString( version );
403 const char *date = proj_context_get_database_metadata( context,
"EPSG.DATE" );
404 return QDate::fromString( date, Qt::DateFormat::ISODate );
410 const char *version = proj_context_get_database_metadata( context,
"ESRI.VERSION" );
411 return QString( version );
417 const char *date = proj_context_get_database_metadata( context,
"ESRI.DATE" );
418 return QDate::fromString( date, Qt::DateFormat::ISODate );
424 const char *version = proj_context_get_database_metadata( context,
"IGNF.VERSION" );
425 return QString( version );
431 const char *date = proj_context_get_database_metadata( context,
"IGNF.DATE" );
432 return QDate::fromString( date, Qt::DateFormat::ISODate );
437 const QString path( proj_info().searchpath );
440 paths = path.split(
';' );
442 paths = path.split(
':' );
445 QSet<QString> existing;
448 res.reserve( paths.count() );
449 for (
const QString &p : std::as_const( paths ) )
451 if ( existing.contains( p ) )
454 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 QStringList searchPaths()
Returns the current list of Proj file search paths.
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.
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.
struct projCtx_t PJ_CONTEXT
struct projCtx_t PJ_CONTEXT
const QgsCoordinateReferenceSystem & crs
void CORE_EXPORT operator()(PJ *object) const
Destroys an PJ object, using the correct proj calls.