23 #include <QRegularExpression>
25 #if PROJ_VERSION_MAJOR>=6
32 #if defined(USE_THREAD_LOCAL) && !defined(Q_OS_WIN)
35 QThreadStorage< QgsProjContext * > QgsProjContext::sProjContext;
40 #if PROJ_VERSION_MAJOR>=6
41 mContext = proj_context_create();
43 mContext = pj_ctx_alloc();
49 #if PROJ_VERSION_MAJOR>=6
52 QgsCoordinateTransform::removeFromCacheObjectsBelongingToCurrentThread( mContext );
53 QgsCoordinateReferenceSystem::removeFromCacheObjectsBelongingToCurrentThread( mContext );
54 proj_context_destroy( mContext );
56 pj_ctx_free( mContext );
62 #if defined(USE_THREAD_LOCAL) && !defined(Q_OS_WIN)
63 return sProjContext.mContext;
66 if ( sProjContext.hasLocalData() )
68 pContext = sProjContext.localData()->mContext;
73 pContext = sProjContext.localData()->mContext;
79 #if PROJ_VERSION_MAJOR>=6
80 void QgsProjUtils::ProjPJDeleter::operator()( PJ *
object )
82 proj_destroy(
object );
85 bool QgsProjUtils::usesAngularUnit(
const QString &projDef )
87 const QString crsDef = QStringLiteral(
"%1 +type=crs" ).arg( projDef );
89 QgsProjUtils::proj_pj_unique_ptr projSingleOperation( proj_create( context, crsDef.toUtf8().constData() ) );
90 if ( !projSingleOperation )
93 QgsProjUtils::proj_pj_unique_ptr coordinateSystem( proj_crs_get_coordinate_system( context, projSingleOperation.get() ) );
94 if ( !coordinateSystem )
97 const int axisCount = proj_cs_get_axis_count( context, coordinateSystem.get() );
100 const char *outUnitAuthName =
nullptr;
101 const char *outUnitAuthCode =
nullptr;
103 proj_cs_get_axis_info( context, coordinateSystem.get(), 0,
112 if ( outUnitAuthName && outUnitAuthCode )
114 const char *unitCategory =
nullptr;
115 if ( proj_uom_get_info_from_database( context, outUnitAuthName, outUnitAuthCode,
nullptr,
nullptr, &unitCategory ) )
117 return QString( unitCategory ).compare( QLatin1String(
"angular" ), Qt::CaseInsensitive ) == 0;
124 bool QgsProjUtils::axisOrderIsSwapped(
const PJ *
crs )
131 QgsProjUtils::proj_pj_unique_ptr pjCs( proj_crs_get_coordinate_system( context,
crs ) );
135 const int axisCount = proj_cs_get_axis_count( context, pjCs.get() );
138 const char *outDirection =
nullptr;
141 proj_cs_get_axis_info( context, pjCs.get(), 0,
150 return QString( outDirection ).compare( QLatin1String(
"north" ), Qt::CaseInsensitive ) == 0;
156 QgsProjUtils::proj_pj_unique_ptr QgsProjUtils::crsToSingleCrs(
const PJ *
crs )
162 switch ( proj_get_type(
crs ) )
164 case PJ_TYPE_BOUND_CRS:
165 return QgsProjUtils::proj_pj_unique_ptr( proj_get_source_crs( context,
crs ) );
167 case PJ_TYPE_COMPOUND_CRS:
170 QgsProjUtils::proj_pj_unique_ptr res( proj_crs_get_sub_crs( context,
crs, i ) );
171 while ( res && ( proj_get_type( res.get() ) == PJ_TYPE_VERTICAL_CRS || proj_get_type( res.get() ) == PJ_TYPE_TEMPORAL_CRS ) )
174 res.reset( proj_crs_get_sub_crs( context,
crs, i ) );
182 return QgsProjUtils::proj_pj_unique_ptr( proj_clone( context,
crs ) );
185 #ifndef _MSC_VER // unreachable
190 bool QgsProjUtils::identifyCrs(
const PJ *
crs, QString &authName, QString &authCode, IdentifyFlags flags )
198 int *confidence =
nullptr;
201 const int count = proj_list_get_count( crsList );
202 int bestConfidence = 0;
203 QgsProjUtils::proj_pj_unique_ptr matchedCrs;
204 for (
int i = 0; i < count; ++i )
206 if ( confidence[i] >= bestConfidence )
208 QgsProjUtils::proj_pj_unique_ptr candidateCrs( proj_list_get(
QgsProjContext::get(), crsList, i ) );
209 switch ( proj_get_type( candidateCrs.get() ) )
211 case PJ_TYPE_BOUND_CRS:
223 candidateCrs = QgsProjUtils::crsToSingleCrs( candidateCrs.get() );
224 const QString authName( proj_get_id_auth_name( candidateCrs.get(), 0 ) );
226 if ( confidence[i] > bestConfidence || ( confidence[i] == bestConfidence && authName == QLatin1String(
"EPSG" ) ) )
228 bestConfidence = confidence[i];
229 matchedCrs = std::move( candidateCrs );
233 proj_list_destroy( crsList );
234 proj_int_list_destroy( confidence );
235 if ( matchedCrs && bestConfidence >= 70 )
237 authName = QString( proj_get_id_auth_name( matchedCrs.get(), 0 ) );
238 authCode = QString( proj_get_id_code( matchedCrs.get(), 0 ) );
241 return !authName.isEmpty() && !authCode.isEmpty();
244 bool QgsProjUtils::coordinateOperationIsAvailable(
const QString &projDef )
246 if ( projDef.isEmpty() )
250 QgsProjUtils::proj_pj_unique_ptr coordinateOperation( proj_create( context, projDef.toUtf8().constData() ) );
251 if ( !coordinateOperation )
254 return static_cast< bool >( proj_coordoperation_is_instantiable( context, coordinateOperation.get() ) );
257 QList<QgsDatumTransform::GridDetails> QgsProjUtils::gridsUsed(
const QString &proj )
259 static QRegularExpression sRegex( QStringLiteral(
"\\+(?:nad)?grids=(.*?)\\s" ) );
261 QList< QgsDatumTransform::GridDetails > grids;
262 QRegularExpressionMatchIterator matches = sRegex.globalMatch( proj );
263 while ( matches.hasNext() )
265 const QRegularExpressionMatch match = matches.next();
266 const QString gridName = match.captured( 1 );
269 const char *fullName =
nullptr;
270 const char *packageName =
nullptr;
271 const char *url =
nullptr;
272 int directDownload = 0;
275 proj_grid_get_info_from_database(
QgsProjContext::get(), gridName.toUtf8().constData(), &fullName, &packageName, &url, &directDownload, &openLicense, &available );
276 grid.
fullName = QString( fullName );
278 grid.
url = QString( url );
282 grids.append( grid );
288 QStringList QgsProjUtils::nonAvailableGrids(
const QString &projDef )
290 if ( projDef.isEmpty() )
291 return QStringList();
294 QgsProjUtils::proj_pj_unique_ptr op( proj_create( context, projDef.toUtf8().constData() ) ); < ---- -
this always fails
if grids are missing
296 return QStringList();
299 for (
int j = 0; j < proj_coordoperation_get_grid_used_count( context, op.get() ); ++j )
301 const char *shortName =
nullptr;
303 proj_coordoperation_get_grid_used( context, op.get(), j, &shortName,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr, &isAvailable );
305 res << QString( shortName );
315 #if PROJ_VERSION_MAJOR>=6
316 const QString path( proj_info().searchpath );
319 paths = path.split(
';' );
321 paths = path.split(
':' );
324 QSet<QString> existing;
327 res.reserve( paths.count() );
328 for (
const QString &p : qgis::as_const( paths ) )
330 if ( existing.contains( p ) )
333 existing.insert( p );
338 return QStringList();