QGIS API Documentation 3.41.0-Master (88383c3d16f)
Loading...
Searching...
No Matches
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 def.celestialBodyName = proj_get_celestial_body_name( context, ellipsoid.get() );
275
276 double semiMajor, semiMinor, invFlattening;
277 int semiMinorComputed = 0;
278 if ( proj_ellipsoid_get_parameters( context, ellipsoid.get(), &semiMajor, &semiMinor, &semiMinorComputed, &invFlattening ) )
279 {
280 def.parameters.semiMajor = semiMajor;
281 def.parameters.semiMinor = semiMinor;
282 def.parameters.inverseFlattening = invFlattening;
283 if ( !semiMinorComputed )
284 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 );
285 else if ( !qgsDoubleNear( def.parameters.inverseFlattening, 0.0 ) )
286 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 );
287 else
288 def.parameters.crs.createFromProj( QStringLiteral( "+proj=longlat +a=%1 +no_defs +type=crs" ).arg( def.parameters.semiMajor, 0, 'g', 17 ), false );
289 }
290 else
291 {
292 def.parameters.valid = false;
293 }
294
295 defs << def;
296 if ( !sDisableCache )
297 {
298 sEllipsoidCache()->insert( def.acronym, def.parameters );
299 }
300 }
301
302 codesIt++;
303 }
304 proj_string_list_destroy( codes );
305 }
306
307 authoritiesIt++;
308 }
309 proj_string_list_destroy( authorities );
310 }
311 locker.unlock();
312
313 QCollator collator;
314 collator.setCaseSensitivity( Qt::CaseInsensitive );
315 std::sort( defs.begin(), defs.end(), [&collator]( const EllipsoidDefinition & a, const EllipsoidDefinition & b )
316 {
317 return collator.compare( a.description, b.description ) < 0;
318 } );
319 if ( !sDisableCache )
320 {
321 *sDefinitionCache() = defs;
322 }
323
324 return defs;
325}
326
328{
329 QStringList result;
330 const QList<QgsEllipsoidUtils::EllipsoidDefinition> defs = definitions();
331 result.reserve( defs.size() );
332 for ( const QgsEllipsoidUtils::EllipsoidDefinition &def : defs )
333 {
334 result << def.acronym;
335 }
336 return result;
337}
338
343
344void QgsEllipsoidUtils::invalidateCache( bool disableCache )
345{
346 const QgsReadWriteLocker locker1( *sEllipsoidCacheLock(), QgsReadWriteLocker::Write );
347 const QgsReadWriteLocker locker2( *sDefinitionCacheLock(), QgsReadWriteLocker::Write );
348
349 if ( !sDisableCache )
350 {
351 if ( disableCache )
352 sDisableCache = true;
353 sEllipsoidCache()->clear();
354 sDefinitionCache()->clear();
355 }
356}
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.
The QgsReadWriteLocker class is a convenience class that simplifies locking and unlocking QReadWriteL...
@ Write
Lock for write.
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:6125
struct pj_ctx 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)