QGIS API Documentation 3.27.0-Master (f261cc1f8b)
qgsellipsoidutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsellipsoidutils.cpp
3 ----------------------
4 Date : April 2017
5 Copyright : (C) 2017 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgsellipsoidutils.h"
17#include "qgsapplication.h"
18#include "qgslogger.h"
19#include "qgsmessagelog.h"
20#include <sqlite3.h>
21#include <QCollator>
22#include "qgsprojutils.h"
23#include "qgsreadwritelocker.h"
24#include "qgsruntimeprofiler.h"
26#include "qgscelestialbody.h"
27
28#include <proj.h>
29#include <mutex>
30
31Q_GLOBAL_STATIC( QReadWriteLock, sEllipsoidCacheLock )
32typedef QHash< QString, QgsEllipsoidUtils::EllipsoidParameters > EllipsoidParamCache;
33Q_GLOBAL_STATIC( EllipsoidParamCache, sEllipsoidCache )
34
35Q_GLOBAL_STATIC( QReadWriteLock, sDefinitionCacheLock );
36typedef QList< QgsEllipsoidUtils::EllipsoidDefinition > EllipsoidDefinitionCache;
38
39static bool sDisableCache = false;
40
41QgsEllipsoidUtils::EllipsoidParameters QgsEllipsoidUtils::ellipsoidParameters( const QString &e )
42{
43// maps older QGIS ellipsoid acronyms to proj acronyms/names
44 static const QMap< QString, QString > sProj6EllipsoidAcronymMap
45 {
46 { "clrk80", "clrk80ign" },
47 {"Adrastea2000", "ESRI:107909"},
48 {"Amalthea2000", "ESRI:107910"},
49 {"Ananke2000", "ESRI:107911"},
50 {"Ariel2000", "ESRI:107945"},
51 {"Atlas2000", "ESRI:107926"},
52 {"Belinda2000", "ESRI:107946"},
53 {"Bianca2000", "ESRI:107947"},
54 {"Callisto2000", "ESRI:107912"},
55 {"Calypso2000", "ESRI:107927"},
56 {"Carme2000", "ESRI:107913"},
57 {"Charon2000", "ESRI:107970"},
58 {"Cordelia2000", "ESRI:107948"},
59 {"Cressida2000", "ESRI:107949"},
60 {"Deimos2000", "ESRI:107906"},
61 {"Desdemona2000", "ESRI:107950"},
62 {"Despina2000", "ESRI:107961"},
63 {"Dione2000", "ESRI:107928"},
64 {"Elara2000", "ESRI:107914"},
65 {"Enceladus2000", "ESRI:107929"},
66 {"Epimetheus2000", "ESRI:107930"},
67 {"Europa2000", "ESRI:107915"},
68 {"Galatea2000", "ESRI:107962"},
69 {"Ganymede2000", "ESRI:107916"},
70 {"Helene2000", "ESRI:107931"},
71 {"Himalia2000", "ESRI:107917"},
72 {"Hyperion2000", "ESRI:107932"},
73 {"Iapetus2000", "ESRI:107933"},
74 {"Io2000", "ESRI:107918"},
75 {"Janus2000", "ESRI:107934"},
76 {"Juliet2000", "ESRI:107951"},
77 {"Jupiter2000", "ESRI:107908"},
78 {"Larissa2000", "ESRI:107963"},
79 {"Leda2000", "ESRI:107919"},
80 {"Lysithea2000", "ESRI:107920"},
81 {"Mars2000", "ESRI:107905"},
82 {"Mercury2000", "ESRI:107900"},
83 {"Metis2000", "ESRI:107921"},
84 {"Mimas2000", "ESRI:107935"},
85 {"Miranda2000", "ESRI:107952"},
86 {"Moon2000", "ESRI:107903"},
87 {"Naiad2000", "ESRI:107964"},
88 {"Neptune2000", "ESRI:107960"},
89 {"Nereid2000", "ESRI:107965"},
90 {"Oberon2000", "ESRI:107953"},
91 {"Ophelia2000", "ESRI:107954"},
92 {"Pan2000", "ESRI:107936"},
93 {"Pandora2000", "ESRI:107937"},
94 {"Pasiphae2000", "ESRI:107922"},
95 {"Phobos2000", "ESRI:107907"},
96 {"Phoebe2000", "ESRI:107938"},
97 {"Pluto2000", "ESRI:107969"},
98 {"Portia2000", "ESRI:107955"},
99 {"Prometheus2000", "ESRI:107939"},
100 {"Proteus2000", "ESRI:107966"},
101 {"Puck2000", "ESRI:107956"},
102 {"Rhea2000", "ESRI:107940"},
103 {"Rosalind2000", "ESRI:107957"},
104 {"Saturn2000", "ESRI:107925"},
105 {"Sinope2000", "ESRI:107923"},
106 {"Telesto2000", "ESRI:107941"},
107 {"Tethys2000", "ESRI:107942"},
108 {"Thalassa2000", "ESRI:107967"},
109 {"Thebe2000", "ESRI:107924"},
110 {"Titan2000", "ESRI:107943"},
111 {"Titania2000", "ESRI:107958"},
112 {"Triton2000", "ESRI:107968"},
113 {"Umbriel2000", "ESRI:107959"},
114 {"Uranus2000", "ESRI:107944"},
115 {"Venus2000", "ESRI:107902"},
116 {"IGNF:ELG053", "EPSG:7030"},
117 {"IGNF:ELG052", "EPSG:7043"},
118 {"IGNF:ELG102", "EPSG:7043"},
119 {"WGS66", "ESRI:107001"},
120 {"plessis", "EPSG:7027"},
121 {"IGNF:ELG017", "EPSG:7027"},
122 {"mod_airy", "EPSG:7002"},
123 {"IGNF:ELG037", "EPSG:7019"},
124 {"IGNF:ELG108", "EPSG:7036"},
125 {"cape", "EPSG:7034"},
126 {"IGNF:ELG010", "EPSG:7011"},
127 {"IGNF:ELG003", "EPSG:7012"},
128 {"IGNF:ELG004", "EPSG:7008"},
129 {"GSK2011", "EPSG:1025"},
130 {"airy", "EPSG:7001"},
131 {"aust_SA", "EPSG:7003"},
132 {"bessel", "EPSG:7004"},
133 {"clrk66", "EPSG:7008"},
134 {"clrk80ign", "EPSG:7011"},
135 {"evrst30", "EPSG:7015"},
136 {"evrstSS", "EPSG:7016"},
137 {"evrst48", "EPSG:7018"},
138 {"GRS80", "EPSG:7019"},
139 {"helmert", "EPSG:7020"},
140 {"intl", "EPSG:7022"},
141 {"krass", "EPSG:7024"},
142 {"NWL9D", "EPSG:7025"},
143 {"WGS84", "EPSG:7030"},
144 {"GRS67", "EPSG:7036"},
145 {"WGS72", "EPSG:7043"},
146 {"bess_nam", "EPSG:7046"},
147 {"IAU76", "EPSG:7049"},
148 {"sphere", "EPSG:7052"},
149 {"hough", "EPSG:7053"},
150 {"evrst69", "EPSG:7056"},
151 {"fschr60", "ESRI:107002"},
152 {"fschr68", "ESRI:107003"},
153 {"fschr60m", "ESRI:107004"},
154 {"walbeck", "ESRI:107007"},
155 {"IGNF:ELG001", "EPSG:7022"},
156 {"engelis", "EPSG:7054"},
157 {"evrst56", "EPSG:7044"},
158 {"SEasia", "ESRI:107004"},
159 {"SGS85", "EPSG:7054"},
160 {"andrae", "PROJ:ANDRAE"},
161 {"clrk80", "EPSG:7034"},
162 {"CPM", "PROJ:CPM"},
163 {"delmbr", "PROJ:DELMBR"},
164 {"Earth2000", "PROJ:EARTH2000"},
165 {"kaula", "PROJ:KAULA"},
166 {"lerch", "PROJ:LERCH"},
167 {"MERIT", "PROJ:MERIT"},
168 {"mprts", "PROJ:MPRTS"},
169 {"new_intl", "PROJ:NEW_INTL"},
170 {"WGS60", "PROJ:WGS60"}
171 };
172
173 QString ellipsoid = e;
174 // ensure ellipsoid database is populated when first called
175 static std::once_flag initialized;
176 std::call_once( initialized, [ = ]
177 {
178 const QgsScopedRuntimeProfile profile( QObject::tr( "Initialize ellipsoids" ) );
179 ( void )definitions();
180 } );
181
182 ellipsoid = sProj6EllipsoidAcronymMap.value( ellipsoid, ellipsoid ); // silently upgrade older QGIS acronyms to proj acronyms
183
184 // check cache
185 {
186 const QgsReadWriteLocker locker( *sEllipsoidCacheLock(), QgsReadWriteLocker::Read );
187 if ( !sDisableCache )
188 {
189 const QHash< QString, EllipsoidParameters >::const_iterator cacheIt = sEllipsoidCache()->constFind( ellipsoid );
190 if ( cacheIt != sEllipsoidCache()->constEnd() )
191 {
192 // found a match in the cache
193 QgsEllipsoidUtils::EllipsoidParameters params = cacheIt.value();
194 return params;
195 }
196 }
197 }
198
199 EllipsoidParameters params;
200
201 // Check if we have a custom projection, and set from text string.
202 // Format is "PARAMETER:<semi-major axis>:<semi minor axis>
203 // Numbers must be with (optional) decimal point and no other separators (C locale)
204 // Distances in meters. Flattening is calculated.
205 if ( ellipsoid.startsWith( QLatin1String( "PARAMETER" ) ) )
206 {
207 QStringList paramList = ellipsoid.split( ':' );
208 bool semiMajorOk, semiMinorOk;
209 const double semiMajor = paramList[1].toDouble( & semiMajorOk );
210 const double semiMinor = paramList[2].toDouble( & semiMinorOk );
211 if ( semiMajorOk && semiMinorOk )
212 {
213 params.semiMajor = semiMajor;
214 params.semiMinor = semiMinor;
215 params.inverseFlattening = semiMajor / ( semiMajor - semiMinor );
216 params.useCustomParameters = true;
217 }
218 else
219 {
220 params.valid = false;
221 }
222
223 const QgsReadWriteLocker locker( *sEllipsoidCacheLock(), QgsReadWriteLocker::Write );
224 if ( !sDisableCache )
225 {
226 sEllipsoidCache()->insert( ellipsoid, params );
227 }
228 return params;
229 }
230 params.valid = false;
231
232 const QgsReadWriteLocker l( *sEllipsoidCacheLock(), QgsReadWriteLocker::Write );
233 if ( !sDisableCache )
234 {
235 sEllipsoidCache()->insert( ellipsoid, params );
236 }
237
238 return params;
239}
240
241QList<QgsEllipsoidUtils::EllipsoidDefinition> QgsEllipsoidUtils::definitions()
242{
243 QgsReadWriteLocker defLocker( *sDefinitionCacheLock(), QgsReadWriteLocker::Read );
244 if ( !sDefinitionCache()->isEmpty() )
245 {
246 return *sDefinitionCache();
247 }
249
250 QList<QgsEllipsoidUtils::EllipsoidDefinition> defs;
251
252 QgsReadWriteLocker locker( *sEllipsoidCacheLock(), QgsReadWriteLocker::Write );
253
254 PJ_CONTEXT *context = QgsProjContext::get();
255 if ( PROJ_STRING_LIST authorities = proj_get_authorities_from_database( context ) )
256 {
257 PROJ_STRING_LIST authoritiesIt = authorities;
258 while ( char *authority = *authoritiesIt )
259 {
260 if ( PROJ_STRING_LIST codes = proj_get_codes_from_database( context, authority, PJ_TYPE_ELLIPSOID, 0 ) )
261 {
262 PROJ_STRING_LIST codesIt = codes;
263 while ( char *code = *codesIt )
264 {
265 const QgsProjUtils::proj_pj_unique_ptr ellipsoid( proj_create_from_database( context, authority, code, PJ_CATEGORY_ELLIPSOID, 0, nullptr ) );
266 if ( ellipsoid.get() )
267 {
269 QString name = QString( proj_get_name( ellipsoid.get() ) );
270 def.acronym = QStringLiteral( "%1:%2" ).arg( authority, code );
271 name.replace( '_', ' ' );
272 def.description = QStringLiteral( "%1 (%2:%3)" ).arg( name, authority, code );
273
274#if PROJ_VERSION_MAJOR>8 || (PROJ_VERSION_MAJOR==8 && PROJ_VERSION_MINOR>=1)
275 def.celestialBodyName = proj_get_celestial_body_name( context, ellipsoid.get() );
276#endif
277
278 double semiMajor, semiMinor, invFlattening;
279 int semiMinorComputed = 0;
280 if ( proj_ellipsoid_get_parameters( context, ellipsoid.get(), &semiMajor, &semiMinor, &semiMinorComputed, &invFlattening ) )
281 {
282 def.parameters.semiMajor = semiMajor;
283 def.parameters.semiMinor = semiMinor;
284 def.parameters.inverseFlattening = invFlattening;
285 if ( !semiMinorComputed )
286 def.parameters.crs.createFromProj( QStringLiteral( "+proj=longlat +a=%1 +b=%2 +no_defs +type=crs" ).arg( def.parameters.semiMajor, 0, 'g', 17 ).arg( def.parameters.semiMinor, 0, 'g', 17 ), false );
287 else if ( !qgsDoubleNear( def.parameters.inverseFlattening, 0.0 ) )
288 def.parameters.crs.createFromProj( QStringLiteral( "+proj=longlat +a=%1 +rf=%2 +no_defs +type=crs" ).arg( def.parameters.semiMajor, 0, 'g', 17 ).arg( def.parameters.inverseFlattening, 0, 'g', 17 ), false );
289 else
290 def.parameters.crs.createFromProj( QStringLiteral( "+proj=longlat +a=%1 +no_defs +type=crs" ).arg( def.parameters.semiMajor, 0, 'g', 17 ), false );
291 }
292 else
293 {
294 def.parameters.valid = false;
295 }
296
297 defs << def;
298 if ( !sDisableCache )
299 {
300 sEllipsoidCache()->insert( def.acronym, def.parameters );
301 }
302 }
303
304 codesIt++;
305 }
306 proj_string_list_destroy( codes );
307 }
308
309 authoritiesIt++;
310 }
311 proj_string_list_destroy( authorities );
312 }
313 locker.unlock();
314
315 QCollator collator;
316 collator.setCaseSensitivity( Qt::CaseInsensitive );
317 std::sort( defs.begin(), defs.end(), [&collator]( const EllipsoidDefinition & a, const EllipsoidDefinition & b )
318 {
319 return collator.compare( a.description, b.description ) < 0;
320 } );
321 if ( !sDisableCache )
322 {
323 *sDefinitionCache() = defs;
324 }
325
326 return defs;
327}
328
330{
331 QStringList result;
332 const QList<QgsEllipsoidUtils::EllipsoidDefinition> defs = definitions();
333 result.reserve( defs.size() );
334 for ( const QgsEllipsoidUtils::EllipsoidDefinition &def : defs )
335 {
336 result << def.acronym;
337 }
338 return result;
339}
340
341QList<QgsCelestialBody> QgsEllipsoidUtils::celestialBodies()
342{
344}
345
346void QgsEllipsoidUtils::invalidateCache( bool disableCache )
347{
348 const QgsReadWriteLocker locker1( *sEllipsoidCacheLock(), QgsReadWriteLocker::Write );
349 const QgsReadWriteLocker locker2( *sDefinitionCacheLock(), QgsReadWriteLocker::Write );
350
351 if ( !sDisableCache )
352 {
353 if ( disableCache )
354 sDisableCache = true;
355 sEllipsoidCache()->clear();
356 sDefinitionCache()->clear();
357 }
358}
static QgsCoordinateReferenceSystemRegistry * coordinateReferenceSystemRegistry()
Returns the application's coordinate reference system (CRS) registry, which handles known CRS definit...
QList< QgsCelestialBody > celestialBodies() const
Returns a list of all known celestial bodies.
bool createFromProj(const QString &projString, bool identify=true)
Sets this CRS by passing it a PROJ style formatted string.
Contains utility functions for working with ellipsoids and querying the ellipsoid database.
static QList< QgsEllipsoidUtils::EllipsoidDefinition > definitions()
Returns a list of the definitions for all known ellipsoids from the internal ellipsoid database.
static QList< QgsCelestialBody > celestialBodies()
Returns a list of all known celestial bodies.
static void invalidateCache(bool disableCache=false)
Clears the internal cache used.
static QStringList acronyms()
Returns a list of all known ellipsoid acronyms from the internal ellipsoid database.
static PJ_CONTEXT * get()
Returns a thread local instance of a proj context, safe for use in the current thread.
std::unique_ptr< PJ, ProjPJDeleter > proj_pj_unique_ptr
Scoped Proj PJ object.
Definition: qgsprojutils.h:141
The QgsReadWriteLocker class is a convenience class that simplifies locking and unlocking QReadWriteL...
@ Write
Lock for write.
@ Read
Lock for read.
void unlock()
Unlocks the lock.
void changeMode(Mode mode)
Change the mode of the lock to mode.
Scoped object for logging of the runtime for a single operation or group of operations.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2396
struct projCtx_t PJ_CONTEXT
QList< QgsEllipsoidUtils::EllipsoidDefinition > EllipsoidDefinitionCache
QHash< QString, QgsEllipsoidUtils::EllipsoidParameters > EllipsoidParamCache
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
Contains definition of an ellipsoid.
QString acronym
authority:code for QGIS builds with proj version 6 or greater, or custom acronym for ellipsoid for ea...
QString celestialBodyName
Name of the associated celestial body (e.g.
QString description
Description of ellipsoid.
QgsEllipsoidUtils::EllipsoidParameters parameters
Ellipsoid parameters.
Contains parameters for an ellipsoid.
bool valid
Whether ellipsoid parameters are valid.
QgsCoordinateReferenceSystem crs
Associated coordinate reference system.
double inverseFlattening
Inverse flattening.
bool useCustomParameters
Whether custom parameters alone should be used (semiMajor/semiMinor only)