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 #ifndef QGSPROJUTILS_H
18 #define QGSPROJUTILS_H
19 
20 #include <QtGlobal>
21 
22 #include "qgis_core.h"
23 #include "qgis_sip.h"
24 #include "qgsconfig.h"
25 #include "qgsdatumtransform.h"
26 #include <memory>
27 #include <QStringList>
28 
29 #if !defined(USE_THREAD_LOCAL) || defined(Q_OS_WIN)
30 #include <QThreadStorage>
31 #endif
32 
33 #ifndef SIP_RUN
34 struct PJconsts;
35 typedef struct PJconsts PJ;
36 #endif
37 
38 /**
39  * \class QgsProjUtils
40  * \ingroup core
41  * \brief Utility functions for working with the proj library.
42  * \since QGIS 3.8
43  */
44 class CORE_EXPORT QgsProjUtils
45 {
46   public:
47 
48     /**
49      * Returns the proj library major version number.
50      */
51     static int projVersionMajor();
52 
53     /**
54      * Returns the proj library minor version number.
55      *
56      * \since QGIS 3.20
57      */
58     static int projVersionMinor();
59 
60     /**
61      * Returns the EPSG registry database version used by the proj library (e.g. "v9.8.6").
62      *
63      * \see epsgRegistryDate()
64      * \since QGIS 3.20
65      */
66     static QString epsgRegistryVersion();
67 
68     /**
69      * Returns the EPSG registry database release date used by the proj library.
70      *
71      * \see epsgRegistryVersion()
72      * \since QGIS 3.20
73      */
74     static QDate epsgRegistryDate();
75 
76     /**
77      * Returns the ESRI projection engine database version used by the proj library (e.g. "ArcMap 10.8.0").
78      *
79      * \see esriDatabaseDate()
80      * \since QGIS 3.20
81      */
82     static QString esriDatabaseVersion();
83 
84     /**
85      * Returns the ESRI projection engine database release date used by the proj library.
86      *
87      * \see esriDatabaseVersion()
88      * \since QGIS 3.20
89      */
90     static QDate esriDatabaseDate();
91 
92     /**
93      * Returns the IGNF database version used by the proj library (e.g. "3.1.0").
94      *
95      * \see ignfDatabaseDate()
96      * \since QGIS 3.20
97      */
98     static QString ignfDatabaseVersion();
99 
100     /**
101      * Returns the IGNF database release date used by the proj library.
102      *
103      * \see ignfDatabaseVersion()
104      * \since QGIS 3.20
105      */
106     static QDate ignfDatabaseDate();
107 
108     /**
109      * Returns the current list of Proj file search paths.
110      *
111      * \note Only available on builds based on Proj >= 6.0. Builds based on
112      * earlier Proj versions will always return an empty list.
113      */
114     static QStringList searchPaths();
115 
116 #ifndef SIP_RUN
117 
118     //! Flags controlling CRS identification behavior
119     enum IdentifyFlag
120     {
121       FlagMatchBoundCrsToUnderlyingSourceCrs = 1 << 0, //!< Allow matching a BoundCRS object to its underlying SourceCRS
122     };
123     Q_DECLARE_FLAGS( IdentifyFlags, IdentifyFlag )
124 
125     /**
126      * Destroys Proj PJ objects.
127      */
128     struct ProjPJDeleter
129     {
130 
131       /**
132        * Destroys an PJ \a object, using the correct proj calls.
133        */
134       void CORE_EXPORT operator()( PJ *object );
135 
136     };
137 
138     /**
139      * Scoped Proj PJ object.
140      */
141     using proj_pj_unique_ptr = std::unique_ptr< PJ, ProjPJDeleter >;
142 
143     /**
144      * Returns TRUE if the given proj coordinate system uses angular units. \a projDef must be
145      * a proj string defining a CRS object.
146      */
147     static bool usesAngularUnit( const QString &projDef );
148 
149     //TODO - remove when proj 6.1 is minimum supported version, and replace with proj_normalize_for_visualization
150 
151     /**
152      * Returns TRUE if the given proj coordinate system uses requires y/x coordinate
153      * order instead of x/y.
154      */
155     static bool axisOrderIsSwapped( const PJ *crs );
156 
157     /**
158      * Returns TRUE if the given proj coordinate system is a dynamic CRS.
159      *
160      * A dynamic CRS relies on a dynamic datum, that is a datum that is not
161      * plate-fixed.
162      *
163      * \since QGIS 3.20
164      */
165     static bool isDynamic( const PJ *crs );
166 
167     /**
168      * Given a PROJ crs (which may be a compound or bound crs, or some other type), extract a single crs
169      * from it.
170      */
171     static proj_pj_unique_ptr crsToSingleCrs( const PJ *crs );
172 
173     /**
174      * Given a PROJ \a crs, attempt to retrieve the datum ensemble from it.
175      *
176      * \warning This method requires PROJ 8.0 or later
177      *
178      * \throws QgsNotSupportedException on QGIS builds based on PROJ 7 or earlier.
179      *
180      * \since QGIS 3.20
181      */
182     static proj_pj_unique_ptr crsToDatumEnsemble( const PJ *crs );
183 
184     /**
185      * Attempts to identify a \a crs, matching it to a known authority and code within
186      * an acceptable level of tolerance.
187      *
188      * Returns TRUE if a matching authority and code was found.
189      */
190     static bool identifyCrs( const PJ *crs, QString &authName, QString &authCode, IdentifyFlags flags = IdentifyFlags() );
191 
192     /**
193      * Returns TRUE if a coordinate operation (specified via proj string) is available.
194      */
195     static bool coordinateOperationIsAvailable( const QString &projDef );
196 
197     /**
198      * Returns a list of grids used by the given \a proj string.
199      */
200     static QList< QgsDatumTransform::GridDetails > gridsUsed( const QString &proj );
201 
202 #if 0 // not possible in current Proj 6 API
203 
204     /**
205      * Given a coordinate operation (specified via proj string), returns a list of
206      * any required grids which are not currently available for use.
207      */
208     static QStringList nonAvailableGrids( const QString &projDef );
209 #endif
210 #endif
211 };
212 
213 #ifndef SIP_RUN
214 
215 #if PROJ_VERSION_MAJOR>=8
216 struct pj_ctx;
217 typedef struct pj_ctx PJ_CONTEXT;
218 #else
219 struct projCtx_t;
220 typedef struct projCtx_t PJ_CONTEXT;
221 #endif
222 
223 /**
224  * \class QgsProjContext
225  * \ingroup core
226  * \brief Used to create and store a proj context object, correctly freeing the context upon destruction.
227  * \note Not available in Python bindings
228  * \since QGIS 3.8
229  */
230 class CORE_EXPORT QgsProjContext
231 {
232   public:
233 
234     QgsProjContext();
235     ~QgsProjContext();
236 
237     /**
238      * Returns a thread local instance of a proj context, safe for use in the current thread.
239      */
240     static PJ_CONTEXT *get();
241 
242   private:
243     PJ_CONTEXT *mContext = nullptr;
244 
245     /**
246      * Thread local proj context storage. A new proj context will be created
247      * for every thread.
248      */
249 #if defined(USE_THREAD_LOCAL) && !defined(Q_OS_WIN)
250     static thread_local QgsProjContext sProjContext;
251 #else
252     static QThreadStorage< QgsProjContext * > sProjContext;
253 #endif
254 };
255 
256 Q_DECLARE_OPERATORS_FOR_FLAGS( QgsProjUtils::IdentifyFlags )
257 #endif
258 #endif // QGSPROJUTILS_H
259