24#include <QRegularExpression>
28#include <proj_experimental.h>
30#if defined(USE_THREAD_LOCAL) && !defined(Q_OS_WIN)
33QThreadStorage< QgsProjContext * > QgsProjContext::sProjContext;
38 mContext = proj_context_create();
45 QgsCoordinateTransform::removeFromCacheObjectsBelongingToCurrentThread( mContext );
46 QgsCoordinateReferenceSystem::removeFromCacheObjectsBelongingToCurrentThread( mContext );
47 proj_context_destroy( mContext );
52#if defined(USE_THREAD_LOCAL) && !defined(Q_OS_WIN)
53 return sProjContext.mContext;
56 if ( sProjContext.hasLocalData() )
58 pContext = sProjContext.localData()->mContext;
63 pContext = sProjContext.localData()->mContext;
71 proj_destroy(
object );
76 const QString crsDef = QStringLiteral(
"%1 +type=crs" ).arg( projDef );
79 if ( !projSingleOperation )
83 if ( !coordinateSystem )
86 const int axisCount = proj_cs_get_axis_count( context, coordinateSystem.get() );
89 const char *outUnitAuthName =
nullptr;
90 const char *outUnitAuthCode =
nullptr;
92 proj_cs_get_axis_info( context, coordinateSystem.get(), 0,
101 if ( outUnitAuthName && outUnitAuthCode )
103 const char *unitCategory =
nullptr;
104 if ( proj_uom_get_info_from_database( context, outUnitAuthName, outUnitAuthCode,
nullptr,
nullptr, &unitCategory ) )
106 return QString( unitCategory ).compare( QLatin1String(
"angular" ), Qt::CaseInsensitive ) == 0;
130 const int axisCount = proj_cs_get_axis_count( context, pjCs.get() );
133 const char *outDirection0 =
nullptr;
134 const char *outDirection1 =
nullptr;
135 const char *outName0 =
nullptr;
136 const char *outName1 =
nullptr;
138 proj_cs_get_axis_info( context, pjCs.get(), 0,
148 proj_cs_get_axis_info( context, pjCs.get(), 1,
158 if ( QString( outDirection0 ).compare( QLatin1String(
"north" ), Qt::CaseInsensitive ) == 0 &&
159 QString( outDirection1 ).compare( QLatin1String(
"east" ), Qt::CaseInsensitive ) == 0 )
165 if ( ( QString( outDirection0 ).compare( QLatin1String(
"north" ), Qt::CaseInsensitive ) == 0 &&
166 QString( outDirection1 ).compare( QLatin1String(
"north" ), Qt::CaseInsensitive ) == 0 ) ||
167 ( QString( outDirection0 ).compare( QLatin1String(
"south" ), Qt::CaseInsensitive ) == 0 &&
168 QString( outDirection1 ).compare( QLatin1String(
"south" ), Qt::CaseInsensitive ) == 0 ) )
170 return QString( outName0 ).startsWith( QLatin1String(
"northing" ), Qt::CaseInsensitive ) &&
171 QString( outName1 ).startsWith( QLatin1String(
"easting" ), Qt::CaseInsensitive ) ;
188 proj_pj_unique_ptr datum( candidate ? proj_crs_get_datum( context, candidate.get() ) : nullptr );
191 const PJ_TYPE type = proj_get_type( datum.get() );
192 isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
193 type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
196 const QString authName( proj_get_id_auth_name( datum.get(), 0 ) );
197 const QString code( proj_get_id_code( datum.get(), 0 ) );
198 if ( authName == QLatin1String(
"EPSG" ) && code == QLatin1String(
"6326" ) )
206 proj_pj_unique_ptr ensemble( candidate ? proj_crs_get_datum_ensemble( context, candidate.get() ) : nullptr );
209 proj_pj_unique_ptr member( proj_datum_ensemble_get_member( context, ensemble.get(), 0 ) );
212 const PJ_TYPE type = proj_get_type( member.get() );
213 isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
214 type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
227 switch ( proj_get_type(
crs ) )
229 case PJ_TYPE_COMPOUND_CRS:
233 while ( res && ( proj_get_type( res.get() ) == PJ_TYPE_VERTICAL_CRS || proj_get_type( res.get() ) == PJ_TYPE_TEMPORAL_CRS ) )
236 res.reset( proj_crs_get_sub_crs( context,
crs, i ) );
241 case PJ_TYPE_VERTICAL_CRS:
261 switch ( proj_get_type(
crs ) )
263 case PJ_TYPE_COMPOUND_CRS:
267 while ( res && ( proj_get_type( res.get() ) != PJ_TYPE_VERTICAL_CRS ) )
270 res.reset( proj_crs_get_sub_crs( context,
crs, i ) );
275 case PJ_TYPE_VERTICAL_CRS:
294 switch ( proj_get_type(
crs ) )
296 case PJ_TYPE_COMPOUND_CRS:
305 res.reset( proj_crs_get_sub_crs( context,
crs, i ) );
310 case PJ_TYPE_BOUND_CRS:
325 const int axisCount = proj_cs_get_axis_count( context, pjCs.get() );
326 for (
int axisIndex = 0; axisIndex < axisCount; ++axisIndex )
328 const char *outDirection =
nullptr;
329 proj_cs_get_axis_info( context, pjCs.get(), axisIndex,
338 const QString outDirectionString = QString( outDirection );
339 if ( outDirectionString.compare( QLatin1String(
"geocentricZ" ), Qt::CaseInsensitive ) == 0
340 || outDirectionString.compare( QLatin1String(
"up" ), Qt::CaseInsensitive ) == 0
341 || outDirectionString.compare( QLatin1String(
"down" ), Qt::CaseInsensitive ) == 0 )
355 switch ( proj_get_type(
crs ) )
357 case PJ_TYPE_BOUND_CRS:
389 QStringList *dest =
reinterpret_cast< QStringList *
>( user_data );
390 QString messageString( message );
391 messageString.replace( QLatin1String(
"internal_proj_create: " ), QString() );
392 dest->append( messageString );
402 if ( level == PJ_LOG_ERROR )
404 const QString messageString( message );
405 if ( messageString == QLatin1String(
"push: Invalid latitude" ) )
415 else if ( level == PJ_LOG_DEBUG )
427 if ( !horizontalCrs || !verticalCrs )
438 const_cast< PJ *
>( horizontalCrs ),
439 const_cast< PJ *
>( verticalCrs ) ) );
442 *errors = projLogger.
errors();
455 int *confidence =
nullptr;
458 const int count = proj_list_get_count( crsList );
459 int bestConfidence = 0;
461 for (
int i = 0; i < count; ++i )
463 if ( confidence[i] >= bestConfidence )
466 switch ( proj_get_type( candidateCrs.get() ) )
468 case PJ_TYPE_BOUND_CRS:
481 const QString authName( proj_get_id_auth_name( candidateCrs.get(), 0 ) );
483 if ( confidence[i] > bestConfidence || ( confidence[i] == bestConfidence && authName == QLatin1String(
"EPSG" ) ) )
485 bestConfidence = confidence[i];
486 matchedCrs = std::move( candidateCrs );
490 proj_list_destroy( crsList );
491 proj_int_list_destroy( confidence );
492 if ( matchedCrs && bestConfidence >= 70 )
494 authName = QString( proj_get_id_auth_name( matchedCrs.get(), 0 ) );
495 authCode = QString( proj_get_id_code( matchedCrs.get(), 0 ) );
498 return !authName.isEmpty() && !authCode.isEmpty();
503 if ( projDef.isEmpty() )
508 if ( !coordinateOperation )
511 return static_cast< bool >( proj_coordoperation_is_instantiable( context, coordinateOperation.get() ) );
516 const thread_local QRegularExpression regex( QStringLiteral(
"\\+(?:nad)?grids=(.*?)\\s" ) );
518 QList< QgsDatumTransform::GridDetails > grids;
519 QRegularExpressionMatchIterator matches = regex.globalMatch( proj );
520 while ( matches.hasNext() )
522 const QRegularExpressionMatch match = matches.next();
523 const QString gridName = match.captured( 1 );
526 const char *fullName =
nullptr;
527 const char *packageName =
nullptr;
528 const char *url =
nullptr;
529 int directDownload = 0;
532 proj_grid_get_info_from_database(
QgsProjContext::get(), gridName.toUtf8().constData(), &fullName, &packageName, &url, &directDownload, &openLicense, &available );
533 grid.
fullName = QString( fullName );
535 grid.
url = QString( url );
539 grids.append( grid );
545QStringList QgsProjUtils::nonAvailableGrids(
const QString &projDef )
547 if ( projDef.isEmpty() )
548 return QStringList();
553 return QStringList();
556 for (
int j = 0; j < proj_coordoperation_get_grid_used_count( context, op.get() ); ++j )
558 const char *shortName =
nullptr;
560 proj_coordoperation_get_grid_used( context, op.get(), j, &shortName,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr, &isAvailable );
562 res << QString( shortName );
570 return PROJ_VERSION_MAJOR;
575 return PROJ_VERSION_MINOR;
581 const char *version = proj_context_get_database_metadata( context,
"EPSG.VERSION" );
582 return QString( version );
588 const char *date = proj_context_get_database_metadata( context,
"EPSG.DATE" );
589 return QDate::fromString( date, Qt::DateFormat::ISODate );
595 const char *version = proj_context_get_database_metadata( context,
"ESRI.VERSION" );
596 return QString( version );
602 const char *date = proj_context_get_database_metadata( context,
"ESRI.DATE" );
603 return QDate::fromString( date, Qt::DateFormat::ISODate );
609 const char *version = proj_context_get_database_metadata( context,
"IGNF.VERSION" );
610 return QString( version );
616 const char *date = proj_context_get_database_metadata( context,
"IGNF.DATE" );
617 return QDate::fromString( date, Qt::DateFormat::ISODate );
622 const QString path( proj_info().searchpath );
625 paths = path.split(
';' );
627 paths = path.split(
':' );
630 QSet<QString> existing;
633 res.reserve( paths.count() );
634 for (
const QString &p : std::as_const( paths ) )
636 if ( existing.contains( p ) )
639 existing.insert( p );
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 proj_pj_unique_ptr createCompoundCrs(const PJ *horizontalCrs, const PJ *verticalCrs, QStringList *errors=nullptr)
Given a PROJ horizontal and vertical CRS, attempt to create a compound CRS from them.
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 bool hasVerticalAxis(const PJ *crs)
Returns true if a PROJ crs has a vertical axis.
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.
static void proj_collecting_logger(void *user_data, int level, const char *message)
QGIS proj log function which collects errors to a QStringList.
QFlags< IdentifyFlag > IdentifyFlags
static void proj_logger(void *user_data, int level, const char *message)
Default QGIS proj log function.
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.
static void proj_silent_logger(void *user_data, int level, const char *message)
QGIS proj log function which ignores errors.
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.
Scoped object for temporary swapping to an error-collecting PROJ log function.
QStringList errors() const
Returns the (possibly empty) list of collected errors.
QgsScopedProjCollectingLogger()
Constructor for QgsScopedProjCollectingLogger.
~QgsScopedProjCollectingLogger()
Returns the PROJ logger back to the default QGIS PROJ logger.
~QgsScopedProjSilentLogger()
Returns the PROJ logger back to the default QGIS PROJ logger.
QgsScopedProjSilentLogger()
Constructor for QgsScopedProjSilentLogger.
#define BUILTIN_UNREACHABLE
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)
const QgsCoordinateReferenceSystem & crs
void CORE_EXPORT operator()(PJ *object) const
Destroys an PJ object, using the correct proj calls.