23 #include <QRegularExpression>
28 #if defined(USE_THREAD_LOCAL) && !defined(Q_OS_WIN)
31 QThreadStorage< 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;
150 proj_pj_unique_ptr datum( horiz ? proj_crs_get_datum( context, horiz.get() ) :
nullptr );
153 const PJ_TYPE type = proj_get_type( datum.get() );
154 isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
155 type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
158 const QString authName( proj_get_id_auth_name( datum.get(), 0 ) );
159 const QString code( proj_get_id_code( datum.get(), 0 ) );
160 if ( authName == QLatin1String(
"EPSG" ) && code == QLatin1String(
"6326" ) )
166 #if PROJ_VERSION_MAJOR > 7 || (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
169 proj_pj_unique_ptr ensemble( horiz ? proj_crs_get_datum_ensemble( context, horiz.get() ) :
nullptr );
172 proj_pj_unique_ptr member( proj_datum_ensemble_get_member( context, ensemble.get(), 0 ) );
175 const PJ_TYPE type = proj_get_type( member.get() );
176 isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
177 type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
191 switch ( proj_get_type(
crs ) )
193 case PJ_TYPE_BOUND_CRS:
196 case PJ_TYPE_COMPOUND_CRS:
200 while ( res && ( proj_get_type( res.get() ) == PJ_TYPE_VERTICAL_CRS || proj_get_type( res.get() ) == PJ_TYPE_TEMPORAL_CRS ) )
203 res.reset( proj_crs_get_sub_crs( context,
crs, i ) );
224 #if PROJ_VERSION_MAJOR>=8
232 throw QgsNotSupportedException( QStringLiteral(
"Calculating datum ensembles requires a QGIS build based on PROJ 8.0 or later" ) );
244 int *confidence =
nullptr;
247 const int count = proj_list_get_count( crsList );
248 int bestConfidence = 0;
250 for (
int i = 0; i < count; ++i )
252 if ( confidence[i] >= bestConfidence )
255 switch ( proj_get_type( candidateCrs.get() ) )
257 case PJ_TYPE_BOUND_CRS:
270 const QString authName( proj_get_id_auth_name( candidateCrs.get(), 0 ) );
272 if ( confidence[i] > bestConfidence || ( confidence[i] == bestConfidence && authName == QLatin1String(
"EPSG" ) ) )
274 bestConfidence = confidence[i];
275 matchedCrs = std::move( candidateCrs );
279 proj_list_destroy( crsList );
280 proj_int_list_destroy( confidence );
281 if ( matchedCrs && bestConfidence >= 70 )
283 authName = QString( proj_get_id_auth_name( matchedCrs.get(), 0 ) );
284 authCode = QString( proj_get_id_code( matchedCrs.get(), 0 ) );
287 return !authName.isEmpty() && !authCode.isEmpty();
292 if ( projDef.isEmpty() )
297 if ( !coordinateOperation )
300 return static_cast< bool >( proj_coordoperation_is_instantiable( context, coordinateOperation.get() ) );
305 const thread_local QRegularExpression regex( QStringLiteral(
"\\+(?:nad)?grids=(.*?)\\s" ) );
307 QList< QgsDatumTransform::GridDetails > grids;
308 QRegularExpressionMatchIterator matches = regex.globalMatch( proj );
309 while ( matches.hasNext() )
311 const QRegularExpressionMatch match = matches.next();
312 const QString gridName = match.captured( 1 );
315 const char *fullName =
nullptr;
316 const char *packageName =
nullptr;
317 const char *url =
nullptr;
318 int directDownload = 0;
321 proj_grid_get_info_from_database(
QgsProjContext::get(), gridName.toUtf8().constData(), &fullName, &packageName, &url, &directDownload, &openLicense, &available );
322 grid.
fullName = QString( fullName );
324 grid.
url = QString( url );
328 grids.append( grid );
334 QStringList QgsProjUtils::nonAvailableGrids(
const QString &projDef )
336 if ( projDef.isEmpty() )
337 return QStringList();
342 return QStringList();
345 for (
int j = 0; j < proj_coordoperation_get_grid_used_count( context, op.get() ); ++j )
347 const char *shortName =
nullptr;
349 proj_coordoperation_get_grid_used( context, op.get(), j, &shortName,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr, &isAvailable );
351 res << QString( shortName );
359 return PROJ_VERSION_MAJOR;
364 return PROJ_VERSION_MINOR;
370 const char *version = proj_context_get_database_metadata( context,
"EPSG.VERSION" );
371 return QString( version );
377 const char *date = proj_context_get_database_metadata( context,
"EPSG.DATE" );
378 return QDate::fromString( date, Qt::DateFormat::ISODate );
384 const char *version = proj_context_get_database_metadata( context,
"ESRI.VERSION" );
385 return QString( version );
391 const char *date = proj_context_get_database_metadata( context,
"ESRI.DATE" );
392 return QDate::fromString( date, Qt::DateFormat::ISODate );
398 const char *version = proj_context_get_database_metadata( context,
"IGNF.VERSION" );
399 return QString( version );
405 const char *date = proj_context_get_database_metadata( context,
"IGNF.DATE" );
406 return QDate::fromString( date, Qt::DateFormat::ISODate );
411 const QString path( proj_info().searchpath );
414 paths = path.split(
';' );
416 paths = path.split(
':' );
419 QSet<QString> existing;
422 res.reserve( paths.count() );
423 for (
const QString &p : std::as_const( paths ) )
425 if ( existing.contains( p ) )
428 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.
@ 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 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 crsToSingleCrs(const PJ *crs)
Given a PROJ crs (which may be a compound or bound crs, or some other type), extract a single crs fro...
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
const QgsCoordinateReferenceSystem & crs
void CORE_EXPORT operator()(PJ *object)
Destroys an PJ object, using the correct proj calls.