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 ) );
214 #ifndef _MSC_VER // unreachable
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 );