20#include <proj_experimental.h>
28#include <QRegularExpression>
32#if defined(USE_THREAD_LOCAL) && !defined(Q_OS_WIN)
35QThreadStorage< QgsProjContext * > QgsProjContext::sProjContext;
40 mContext = proj_context_create();
47 QgsCoordinateTransform::removeFromCacheObjectsBelongingToCurrentThread( mContext );
48 QgsCoordinateReferenceSystem::removeFromCacheObjectsBelongingToCurrentThread( mContext );
49 proj_context_destroy( mContext );
54#if defined(USE_THREAD_LOCAL) && !defined(Q_OS_WIN)
55 return sProjContext.mContext;
58 if ( sProjContext.hasLocalData() )
60 pContext = sProjContext.localData()->mContext;
65 pContext = sProjContext.localData()->mContext;
73 proj_destroy(
object );
78 const QString crsDef = QStringLiteral(
"%1 +type=crs" ).arg( projDef );
81 if ( !projSingleOperation )
85 if ( !coordinateSystem )
88 const int axisCount = proj_cs_get_axis_count( context, coordinateSystem.get() );
91 const char *outUnitAuthName =
nullptr;
92 const char *outUnitAuthCode =
nullptr;
94 proj_cs_get_axis_info( context, coordinateSystem.get(), 0,
103 if ( outUnitAuthName && outUnitAuthCode )
105 const char *unitCategory =
nullptr;
106 if ( proj_uom_get_info_from_database( context, outUnitAuthName, outUnitAuthCode,
nullptr,
nullptr, &unitCategory ) )
108 return QString( unitCategory ).compare( QLatin1String(
"angular" ), Qt::CaseInsensitive ) == 0;
132 const int axisCount = proj_cs_get_axis_count( context, pjCs.get() );
135 const char *outDirection0 =
nullptr;
136 const char *outDirection1 =
nullptr;
137 const char *outName0 =
nullptr;
138 const char *outName1 =
nullptr;
140 proj_cs_get_axis_info( context, pjCs.get(), 0,
150 proj_cs_get_axis_info( context, pjCs.get(), 1,
160 if ( QString( outDirection0 ).compare( QLatin1String(
"north" ), Qt::CaseInsensitive ) == 0 &&
161 QString( outDirection1 ).compare( QLatin1String(
"east" ), Qt::CaseInsensitive ) == 0 )
167 if ( ( QString( outDirection0 ).compare( QLatin1String(
"north" ), Qt::CaseInsensitive ) == 0 &&
168 QString( outDirection1 ).compare( QLatin1String(
"north" ), Qt::CaseInsensitive ) == 0 ) ||
169 ( QString( outDirection0 ).compare( QLatin1String(
"south" ), Qt::CaseInsensitive ) == 0 &&
170 QString( outDirection1 ).compare( QLatin1String(
"south" ), Qt::CaseInsensitive ) == 0 ) )
172 return QString( outName0 ).startsWith( QLatin1String(
"northing" ), Qt::CaseInsensitive ) &&
173 QString( outName1 ).startsWith( QLatin1String(
"easting" ), Qt::CaseInsensitive ) ;
190 proj_pj_unique_ptr datum( candidate ? proj_crs_get_datum( context, candidate.get() ) :
nullptr );
193 const PJ_TYPE type = proj_get_type( datum.get() );
194 isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
195 type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
198 const QString authName( proj_get_id_auth_name( datum.get(), 0 ) );
199 const QString code( proj_get_id_code( datum.get(), 0 ) );
200 if ( authName == QLatin1String(
"EPSG" ) && code == QLatin1String(
"6326" ) )
208 proj_pj_unique_ptr ensemble( candidate ? proj_crs_get_datum_ensemble( context, candidate.get() ) :
nullptr );
211 proj_pj_unique_ptr member( proj_datum_ensemble_get_member( context, ensemble.get(), 0 ) );
214 const PJ_TYPE type = proj_get_type( member.get() );
215 isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
216 type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
229 switch ( proj_get_type( crs ) )
231 case PJ_TYPE_COMPOUND_CRS:
235 while ( res && ( proj_get_type( res.get() ) == PJ_TYPE_VERTICAL_CRS || proj_get_type( res.get() ) == PJ_TYPE_TEMPORAL_CRS ) )
238 res.reset( proj_crs_get_sub_crs( context, crs, i ) );
243 case PJ_TYPE_VERTICAL_CRS:
263 switch ( proj_get_type( crs ) )
265 case PJ_TYPE_COMPOUND_CRS:
269 while ( res && ( proj_get_type( res.get() ) != PJ_TYPE_VERTICAL_CRS ) )
272 res.reset( proj_crs_get_sub_crs( context, crs, i ) );
277 case PJ_TYPE_VERTICAL_CRS:
296 switch ( proj_get_type( crs ) )
298 case PJ_TYPE_COMPOUND_CRS:
307 res.reset( proj_crs_get_sub_crs( context, crs, i ) );
312 case PJ_TYPE_BOUND_CRS:
327 const int axisCount = proj_cs_get_axis_count( context, pjCs.get() );
328 for (
int axisIndex = 0; axisIndex < axisCount; ++axisIndex )
330 const char *outDirection =
nullptr;
331 proj_cs_get_axis_info( context, pjCs.get(), axisIndex,
340 const QString outDirectionString = QString( outDirection );
341 if ( outDirectionString.compare( QLatin1String(
"geocentricZ" ), Qt::CaseInsensitive ) == 0
342 || outDirectionString.compare( QLatin1String(
"up" ), Qt::CaseInsensitive ) == 0
343 || outDirectionString.compare( QLatin1String(
"down" ), Qt::CaseInsensitive ) == 0 )
357 switch ( proj_get_type( crs ) )
359 case PJ_TYPE_BOUND_CRS:
391 QStringList *dest =
reinterpret_cast< QStringList *
>( user_data );
392 QString messageString( message );
393 messageString.replace( QLatin1String(
"internal_proj_create: " ), QString() );
394 dest->append( messageString );
404 if ( level == PJ_LOG_ERROR )
406 const QString messageString( message );
407 if ( messageString == QLatin1String(
"push: Invalid latitude" ) )
417 else if ( level == PJ_LOG_DEBUG )
429 if ( !horizontalCrs || !verticalCrs )
440 const_cast< PJ *
>( horizontalCrs ),
441 const_cast< PJ *
>( verticalCrs ) ) );
444 *errors = projLogger.
errors();
457 int *confidence =
nullptr;
458 if ( PJ_OBJ_LIST *crsList = proj_identify(
QgsProjContext::get(), crs,
nullptr,
nullptr, &confidence ) )
460 const int count = proj_list_get_count( crsList );
461 int bestConfidence = 0;
463 for (
int i = 0; i < count; ++i )
465 if ( confidence[i] >= bestConfidence )
468 switch ( proj_get_type( candidateCrs.get() ) )
470 case PJ_TYPE_BOUND_CRS:
483 const QString authName( proj_get_id_auth_name( candidateCrs.get(), 0 ) );
485 if ( confidence[i] > bestConfidence || ( confidence[i] == bestConfidence && authName == QLatin1String(
"EPSG" ) ) )
487 bestConfidence = confidence[i];
488 matchedCrs = std::move( candidateCrs );
492 proj_list_destroy( crsList );
493 proj_int_list_destroy( confidence );
494 if ( matchedCrs && bestConfidence >= 70 )
496 authName = QString( proj_get_id_auth_name( matchedCrs.get(), 0 ) );
497 authCode = QString( proj_get_id_code( matchedCrs.get(), 0 ) );
500 return !authName.isEmpty() && !authCode.isEmpty();
505 if ( projDef.isEmpty() )
510 if ( !coordinateOperation )
513 return static_cast< bool >( proj_coordoperation_is_instantiable( context, coordinateOperation.get() ) );
518 const thread_local QRegularExpression regex( QStringLiteral(
"\\+(?:nad)?grids=(.*?)\\s" ) );
520 QList< QgsDatumTransform::GridDetails > grids;
521 QRegularExpressionMatchIterator matches = regex.globalMatch( proj );
522 while ( matches.hasNext() )
524 const QRegularExpressionMatch match = matches.next();
525 const QString gridName = match.captured( 1 );
528 const char *fullName =
nullptr;
529 const char *packageName =
nullptr;
530 const char *url =
nullptr;
531 int directDownload = 0;
534 proj_grid_get_info_from_database(
QgsProjContext::get(), gridName.toUtf8().constData(), &fullName, &packageName, &url, &directDownload, &openLicense, &available );
535 grid.
fullName = QString( fullName );
537 grid.
url = QString( url );
541 grids.append( grid );
547QStringList QgsProjUtils::nonAvailableGrids(
const QString &projDef )
549 if ( projDef.isEmpty() )
550 return QStringList();
555 return QStringList();
558 for (
int j = 0; j < proj_coordoperation_get_grid_used_count( context, op.get() ); ++j )
560 const char *shortName =
nullptr;
562 proj_coordoperation_get_grid_used( context, op.get(), j, &shortName,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr, &isAvailable );
564 res << QString( shortName );
572 return PROJ_VERSION_MAJOR;
577 return PROJ_VERSION_MINOR;
583 const char *version = proj_context_get_database_metadata( context,
"EPSG.VERSION" );
584 return QString( version );
590 const char *date = proj_context_get_database_metadata( context,
"EPSG.DATE" );
591 return QDate::fromString( date, Qt::DateFormat::ISODate );
597 const char *version = proj_context_get_database_metadata( context,
"ESRI.VERSION" );
598 return QString( version );
604 const char *date = proj_context_get_database_metadata( context,
"ESRI.DATE" );
605 return QDate::fromString( date, Qt::DateFormat::ISODate );
611 const char *version = proj_context_get_database_metadata( context,
"IGNF.VERSION" );
612 return QString( version );
618 const char *date = proj_context_get_database_metadata( context,
"IGNF.DATE" );
619 return QDate::fromString( date, Qt::DateFormat::ISODate );
624 const QString path( proj_info().searchpath );
627 paths = path.split(
';' );
629 paths = path.split(
':' );
632 QSet<QString> existing;
635 res.reserve( paths.count() );
636 for (
const QString &p : std::as_const( paths ) )
638 if ( existing.contains( p ) )
641 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)
void CORE_EXPORT operator()(PJ *object) const
Destroys an PJ object, using the correct proj calls.