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.