QGIS API Documentation 3.41.0-Master (88383c3d16f)
Loading...
Searching...
No Matches
qgsprojutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsprojutils.h
3 -------------------
4 begin : March 2019
5 copyright : (C) 2019 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17#include "qgsprojutils.h"
18#include "qgis.h"
20#include "qgsexception.h"
21#include "qgslogger.h"
22#include <QString>
23#include <QSet>
24#include <QRegularExpression>
25#include <QDate>
26
27#include <proj.h>
28#include <proj_experimental.h>
29
30#if defined(USE_THREAD_LOCAL) && !defined(Q_OS_WIN)
31thread_local QgsProjContext QgsProjContext::sProjContext;
32#else
33QThreadStorage< QgsProjContext * > QgsProjContext::sProjContext;
34#endif
35
37{
38 mContext = proj_context_create();
39}
40
42{
43 // Call removeFromCacheObjectsBelongingToCurrentThread() before
44 // destroying the context
45 QgsCoordinateTransform::removeFromCacheObjectsBelongingToCurrentThread( mContext );
46 QgsCoordinateReferenceSystem::removeFromCacheObjectsBelongingToCurrentThread( mContext );
47 proj_context_destroy( mContext );
48}
49
51{
52#if defined(USE_THREAD_LOCAL) && !defined(Q_OS_WIN)
53 return sProjContext.mContext;
54#else
55 PJ_CONTEXT *pContext = nullptr;
56 if ( sProjContext.hasLocalData() )
57 {
58 pContext = sProjContext.localData()->mContext;
59 }
60 else
61 {
62 sProjContext.setLocalData( new QgsProjContext() );
63 pContext = sProjContext.localData()->mContext;
64 }
65 return pContext;
66#endif
67}
68
70{
71 proj_destroy( object );
72}
73
74bool QgsProjUtils::usesAngularUnit( const QString &projDef )
75{
76 const QString crsDef = QStringLiteral( "%1 +type=crs" ).arg( projDef );
78 QgsProjUtils::proj_pj_unique_ptr projSingleOperation( proj_create( context, crsDef.toUtf8().constData() ) );
79 if ( !projSingleOperation )
80 return false;
81
82 QgsProjUtils::proj_pj_unique_ptr coordinateSystem( proj_crs_get_coordinate_system( context, projSingleOperation.get() ) );
83 if ( !coordinateSystem )
84 return false;
85
86 const int axisCount = proj_cs_get_axis_count( context, coordinateSystem.get() );
87 if ( axisCount > 0 )
88 {
89 const char *outUnitAuthName = nullptr;
90 const char *outUnitAuthCode = nullptr;
91 // Read only first axis
92 proj_cs_get_axis_info( context, coordinateSystem.get(), 0,
93 nullptr,
94 nullptr,
95 nullptr,
96 nullptr,
97 nullptr,
98 &outUnitAuthName,
99 &outUnitAuthCode );
100
101 if ( outUnitAuthName && outUnitAuthCode )
102 {
103 const char *unitCategory = nullptr;
104 if ( proj_uom_get_info_from_database( context, outUnitAuthName, outUnitAuthCode, nullptr, nullptr, &unitCategory ) )
105 {
106 return QString( unitCategory ).compare( QLatin1String( "angular" ), Qt::CaseInsensitive ) == 0;
107 }
108 }
109 }
110 return false;
111}
112
114{
115 //ported from https://github.com/pramsey/postgis/blob/7ecf6839c57a838e2c8540001a3cd35b78a730db/liblwgeom/lwgeom_transform.c#L299
116 //and GDAL OGRSpatialReference::isNorthEastAxisOrder https://github.com/OSGeo/gdal/blob/release/3.10/ogr/ogrspatialreference.cpp#L419
117 if ( !crs )
118 return false;
119
120 PJ_CONTEXT *context = QgsProjContext::get();
121
123 if ( !horizCrs )
124 return false;
125
126 QgsProjUtils::proj_pj_unique_ptr pjCs( proj_crs_get_coordinate_system( context, horizCrs.get() ) );
127 if ( !pjCs )
128 return false;
129
130 const int axisCount = proj_cs_get_axis_count( context, pjCs.get() );
131 if ( axisCount > 0 )
132 {
133 const char *outDirection0 = nullptr;
134 const char *outDirection1 = nullptr;
135 const char *outName0 = nullptr;
136 const char *outName1 = nullptr;
137
138 proj_cs_get_axis_info( context, pjCs.get(), 0,
139 &outName0,
140 nullptr,
141 &outDirection0,
142 nullptr,
143 nullptr,
144 nullptr,
145 nullptr
146 );
147
148 proj_cs_get_axis_info( context, pjCs.get(), 1,
149 &outName1,
150 nullptr,
151 &outDirection1,
152 nullptr,
153 nullptr,
154 nullptr,
155 nullptr
156 );
157
158 if ( QString( outDirection0 ).compare( QLatin1String( "north" ), Qt::CaseInsensitive ) == 0 &&
159 QString( outDirection1 ).compare( QLatin1String( "east" ), Qt::CaseInsensitive ) == 0 )
160 {
161 return true;
162 }
163
164 // Handle polar projections with NE-order
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 ) )
169 {
170 return QString( outName0 ).startsWith( QLatin1String( "northing" ), Qt::CaseInsensitive ) &&
171 QString( outName1 ).startsWith( QLatin1String( "easting" ), Qt::CaseInsensitive ) ;
172 }
173 }
174 return false;
175}
176
178{
179 // ported from GDAL OGRSpatialReference::IsDynamic()
180 bool isDynamic = false;
181 PJ_CONTEXT *context = QgsProjContext::get();
182
183 // prefer horizontal crs if possible
185 if ( !crs )
186 candidate = unboundCrs( crs );
187
188 proj_pj_unique_ptr datum( candidate ? proj_crs_get_datum( context, candidate.get() ) : nullptr );
189 if ( datum )
190 {
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;
194 if ( !isDynamic )
195 {
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" ) )
199 {
200 isDynamic = true;
201 }
202 }
203 }
204 else
205 {
206 proj_pj_unique_ptr ensemble( candidate ? proj_crs_get_datum_ensemble( context, candidate.get() ) : nullptr );
207 if ( ensemble )
208 {
209 proj_pj_unique_ptr member( proj_datum_ensemble_get_member( context, ensemble.get(), 0 ) );
210 if ( member )
211 {
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;
215 }
216 }
217 }
218 return isDynamic;
219}
220
222{
223 if ( !crs )
224 return nullptr;
225
226 PJ_CONTEXT *context = QgsProjContext::get();
227 switch ( proj_get_type( crs ) )
228 {
229 case PJ_TYPE_COMPOUND_CRS:
230 {
231 int i = 0;
232 QgsProjUtils::proj_pj_unique_ptr res( proj_crs_get_sub_crs( context, crs, i ) );
233 while ( res && ( proj_get_type( res.get() ) == PJ_TYPE_VERTICAL_CRS || proj_get_type( res.get() ) == PJ_TYPE_TEMPORAL_CRS ) )
234 {
235 i++;
236 res.reset( proj_crs_get_sub_crs( context, crs, i ) );
237 }
238 return res;
239 }
240
241 case PJ_TYPE_VERTICAL_CRS:
242 return nullptr;
243
244 // maybe other types to handle??
245
246 default:
247 return unboundCrs( crs );
248 }
249
250#ifndef _MSC_VER // unreachable
251 return nullptr;
252#endif
253}
254
256{
257 if ( !crs )
258 return nullptr;
259
260 PJ_CONTEXT *context = QgsProjContext::get();
261 switch ( proj_get_type( crs ) )
262 {
263 case PJ_TYPE_COMPOUND_CRS:
264 {
265 int i = 0;
266 QgsProjUtils::proj_pj_unique_ptr res( proj_crs_get_sub_crs( context, crs, i ) );
267 while ( res && ( proj_get_type( res.get() ) != PJ_TYPE_VERTICAL_CRS ) )
268 {
269 i++;
270 res.reset( proj_crs_get_sub_crs( context, crs, i ) );
271 }
272 return res;
273 }
274
275 case PJ_TYPE_VERTICAL_CRS:
276 return QgsProjUtils::proj_pj_unique_ptr( proj_clone( context, crs ) );
277
278 // maybe other types to handle??
279
280 default:
281 return nullptr;
282 }
283
285}
286
288{
289 if ( !crs )
290 return false;
291
292 PJ_CONTEXT *context = QgsProjContext::get();
293
294 switch ( proj_get_type( crs ) )
295 {
296 case PJ_TYPE_COMPOUND_CRS:
297 {
298 int i = 0;
299 QgsProjUtils::proj_pj_unique_ptr res( proj_crs_get_sub_crs( context, crs, i ) );
300 while ( res )
301 {
302 if ( hasVerticalAxis( res.get() ) )
303 return true;
304 i++;
305 res.reset( proj_crs_get_sub_crs( context, crs, i ) );
306 }
307 return false;
308 }
309
310 case PJ_TYPE_BOUND_CRS:
311 {
312 return hasVerticalAxis( proj_get_source_crs( context, crs ) );
313 }
314
315 // maybe other types to handle like this??
316
317 default:
318 break;
319 }
320
321 QgsProjUtils::proj_pj_unique_ptr pjCs( proj_crs_get_coordinate_system( context, crs ) );
322 if ( !pjCs )
323 return false;
324
325 const int axisCount = proj_cs_get_axis_count( context, pjCs.get() );
326 for ( int axisIndex = 0; axisIndex < axisCount; ++axisIndex )
327 {
328 const char *outDirection = nullptr;
329 proj_cs_get_axis_info( context, pjCs.get(), axisIndex,
330 nullptr,
331 nullptr,
332 &outDirection,
333 nullptr,
334 nullptr,
335 nullptr,
336 nullptr
337 );
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 )
342 {
343 return true;
344 }
345 }
346 return false;
347}
348
350{
351 if ( !crs )
352 return nullptr;
353
354 PJ_CONTEXT *context = QgsProjContext::get();
355 switch ( proj_get_type( crs ) )
356 {
357 case PJ_TYPE_BOUND_CRS:
358 return QgsProjUtils::proj_pj_unique_ptr( proj_get_source_crs( context, crs ) );
359
360 // maybe other types to handle??
361
362 default:
363 return QgsProjUtils::proj_pj_unique_ptr( proj_clone( context, crs ) );
364 }
365
366#ifndef _MSC_VER // unreachable
367 return nullptr;
368#endif
369}
370
372{
373 if ( !crs )
374 return nullptr;
375
376 PJ_CONTEXT *context = QgsProjContext::get();
378 if ( !candidate ) // purely vertical CRS
379 candidate = unboundCrs( crs );
380
381 if ( !candidate )
382 return nullptr;
383
384 return QgsProjUtils::proj_pj_unique_ptr( proj_crs_get_datum_ensemble( context, candidate.get() ) );
385}
386
387void QgsProjUtils::proj_collecting_logger( void *user_data, int /*level*/, const char *message )
388{
389 QStringList *dest = reinterpret_cast< QStringList * >( user_data );
390 QString messageString( message );
391 messageString.replace( QLatin1String( "internal_proj_create: " ), QString() );
392 dest->append( messageString );
393}
394
395void QgsProjUtils::proj_silent_logger( void * /*user_data*/, int /*level*/, const char * /*message*/ )
396{
397}
398
399void QgsProjUtils::proj_logger( void *, int level, const char *message )
400{
401#ifdef QGISDEBUG
402 if ( level == PJ_LOG_ERROR )
403 {
404 const QString messageString( message );
405 if ( messageString == QLatin1String( "push: Invalid latitude" ) )
406 {
407 // these messages tend to spam the console as they can be repeated 1000s of times
408 QgsDebugMsgLevel( messageString, 3 );
409 }
410 else
411 {
412 QgsDebugError( messageString );
413 }
414 }
415 else if ( level == PJ_LOG_DEBUG )
416 {
417 QgsDebugMsgLevel( QString( message ), 3 );
418 }
419#else
420 ( void )level;
421 ( void )message;
422#endif
423}
424
425QgsProjUtils::proj_pj_unique_ptr QgsProjUtils::createCompoundCrs( const PJ *horizontalCrs, const PJ *verticalCrs, QStringList *errors )
426{
427 if ( !horizontalCrs || !verticalCrs )
428 return nullptr;
429
430 PJ_CONTEXT *context = QgsProjContext::get();
431 // collect errors instead of dumping them to terminal
432
434
435 // const cast here is for compatibility with proj < 9.5
436 QgsProjUtils::proj_pj_unique_ptr compoundCrs( proj_create_compound_crs( context,
437 nullptr,
438 const_cast< PJ *>( horizontalCrs ),
439 const_cast< PJ * >( verticalCrs ) ) );
440
441 if ( errors )
442 *errors = projLogger.errors();
443
444 return compoundCrs;
445}
446
447bool QgsProjUtils::identifyCrs( const PJ *crs, QString &authName, QString &authCode, IdentifyFlags flags )
448{
449 authName.clear();
450 authCode.clear();
451
452 if ( !crs )
453 return false;
454
455 int *confidence = nullptr;
456 if ( PJ_OBJ_LIST *crsList = proj_identify( QgsProjContext::get(), crs, nullptr, nullptr, &confidence ) )
457 {
458 const int count = proj_list_get_count( crsList );
459 int bestConfidence = 0;
461 for ( int i = 0; i < count; ++i )
462 {
463 if ( confidence[i] >= bestConfidence )
464 {
465 QgsProjUtils::proj_pj_unique_ptr candidateCrs( proj_list_get( QgsProjContext::get(), crsList, i ) );
466 switch ( proj_get_type( candidateCrs.get() ) )
467 {
468 case PJ_TYPE_BOUND_CRS:
469 // proj_identify also matches bound CRSes to the source CRS. But they are not the same as the source CRS, so we don't
470 // consider them a candidate for a match here (depending on the identify flags, that is!)
472 break;
473 else
474 continue;
475
476 default:
477 break;
478 }
479
480 candidateCrs = QgsProjUtils::unboundCrs( candidateCrs.get() );
481 const QString authName( proj_get_id_auth_name( candidateCrs.get(), 0 ) );
482 // if a match is identical confidence, we prefer EPSG codes for compatibility with earlier qgis conversions
483 if ( confidence[i] > bestConfidence || ( confidence[i] == bestConfidence && authName == QLatin1String( "EPSG" ) ) )
484 {
485 bestConfidence = confidence[i];
486 matchedCrs = std::move( candidateCrs );
487 }
488 }
489 }
490 proj_list_destroy( crsList );
491 proj_int_list_destroy( confidence );
492 if ( matchedCrs && bestConfidence >= 70 )
493 {
494 authName = QString( proj_get_id_auth_name( matchedCrs.get(), 0 ) );
495 authCode = QString( proj_get_id_code( matchedCrs.get(), 0 ) );
496 }
497 }
498 return !authName.isEmpty() && !authCode.isEmpty();
499}
500
502{
503 if ( projDef.isEmpty() )
504 return true;
505
506 PJ_CONTEXT *context = QgsProjContext::get();
507 QgsProjUtils::proj_pj_unique_ptr coordinateOperation( proj_create( context, projDef.toUtf8().constData() ) );
508 if ( !coordinateOperation )
509 return false;
510
511 return static_cast< bool >( proj_coordoperation_is_instantiable( context, coordinateOperation.get() ) );
512}
513
514QList<QgsDatumTransform::GridDetails> QgsProjUtils::gridsUsed( const QString &proj )
515{
516 const thread_local QRegularExpression regex( QStringLiteral( "\\+(?:nad)?grids=(.*?)\\s" ) );
517
518 QList< QgsDatumTransform::GridDetails > grids;
519 QRegularExpressionMatchIterator matches = regex.globalMatch( proj );
520 while ( matches.hasNext() )
521 {
522 const QRegularExpressionMatch match = matches.next();
523 const QString gridName = match.captured( 1 );
525 grid.shortName = gridName;
526 const char *fullName = nullptr;
527 const char *packageName = nullptr;
528 const char *url = nullptr;
529 int directDownload = 0;
530 int openLicense = 0;
531 int available = 0;
532 proj_grid_get_info_from_database( QgsProjContext::get(), gridName.toUtf8().constData(), &fullName, &packageName, &url, &directDownload, &openLicense, &available );
533 grid.fullName = QString( fullName );
534 grid.packageName = QString( packageName );
535 grid.url = QString( url );
536 grid.directDownload = directDownload;
537 grid.openLicense = openLicense;
538 grid.isAvailable = available;
539 grids.append( grid );
540 }
541 return grids;
542}
543
544#if 0
545QStringList QgsProjUtils::nonAvailableGrids( const QString &projDef )
546{
547 if ( projDef.isEmpty() )
548 return QStringList();
549
550 PJ_CONTEXT *context = QgsProjContext::get();
551 QgsProjUtils::proj_pj_unique_ptr op( proj_create( context, projDef.toUtf8().constData() ) ); < ---- - this always fails if grids are missing
552 if ( !op )
553 return QStringList();
554
555 QStringList res;
556 for ( int j = 0; j < proj_coordoperation_get_grid_used_count( context, op.get() ); ++j )
557 {
558 const char *shortName = nullptr;
559 int isAvailable = 0;
560 proj_coordoperation_get_grid_used( context, op.get(), j, &shortName, nullptr, nullptr, nullptr, nullptr, nullptr, &isAvailable );
561 if ( !isAvailable )
562 res << QString( shortName );
563 }
564 return res;
565}
566#endif
567
569{
570 return PROJ_VERSION_MAJOR;
571}
572
574{
575 return PROJ_VERSION_MINOR;
576}
577
579{
580 PJ_CONTEXT *context = QgsProjContext::get();
581 const char *version = proj_context_get_database_metadata( context, "EPSG.VERSION" );
582 return QString( version );
583}
584
586{
587 PJ_CONTEXT *context = QgsProjContext::get();
588 const char *date = proj_context_get_database_metadata( context, "EPSG.DATE" );
589 return QDate::fromString( date, Qt::DateFormat::ISODate );
590}
591
593{
594 PJ_CONTEXT *context = QgsProjContext::get();
595 const char *version = proj_context_get_database_metadata( context, "ESRI.VERSION" );
596 return QString( version );
597}
598
600{
601 PJ_CONTEXT *context = QgsProjContext::get();
602 const char *date = proj_context_get_database_metadata( context, "ESRI.DATE" );
603 return QDate::fromString( date, Qt::DateFormat::ISODate );
604}
605
607{
608 PJ_CONTEXT *context = QgsProjContext::get();
609 const char *version = proj_context_get_database_metadata( context, "IGNF.VERSION" );
610 return QString( version );
611}
612
614{
615 PJ_CONTEXT *context = QgsProjContext::get();
616 const char *date = proj_context_get_database_metadata( context, "IGNF.DATE" );
617 return QDate::fromString( date, Qt::DateFormat::ISODate );
618}
619
621{
622 const QString path( proj_info().searchpath );
623 QStringList paths;
624#ifdef Q_OS_WIN
625 paths = path.split( ';' );
626#else
627 paths = path.split( ':' );
628#endif
629
630 QSet<QString> existing;
631 // thin out duplicates from paths -- see https://github.com/OSGeo/proj.4/pull/1498
632 QStringList res;
633 res.reserve( paths.count() );
634 for ( const QString &p : std::as_const( paths ) )
635 {
636 if ( existing.contains( p ) )
637 continue;
638
639 existing.insert( p );
640 res << p;
641 }
642 return res;
643}
644
645//
646// QgsScopedProjCollectingLogger
647//
648
653
655{
656 // reset logger back to terminal output
657 proj_log_func( QgsProjContext::get(), nullptr, QgsProjUtils::proj_logger );
658}
659
660//
661// QgsScopedProjSilentLogger
662//
663
668
670{
671 // reset logger back to terminal output
672 proj_log_func( QgsProjContext::get(), nullptr, QgsProjUtils::proj_logger );
673}
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
Definition qgis.h:6779
struct pj_ctx PJ_CONTEXT
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:41
#define QgsDebugError(str)
Definition qgslogger.h:40
struct pj_ctx PJ_CONTEXT
struct PJconsts PJ
const QgsCoordinateReferenceSystem & crs
Contains information about a projection transformation grid file.
QString shortName
Short name of transform grid.
bool isAvailable
true if grid is currently available for use
QString fullName
Full name of transform grid.
bool directDownload
true if direct download of grid is possible
QString packageName
Name of package the grid is included within.
QString url
Url to download grid from.
bool openLicense
true if grid is available under an open license
void CORE_EXPORT operator()(PJ *object) const
Destroys an PJ object, using the correct proj calls.