QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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"
25 
26 #if PROJ_VERSION_MAJOR>=6
27 #include <proj.h>
28 #include <mutex>
29 #endif
30 
31 Q_GLOBAL_STATIC( QReadWriteLock, sEllipsoidCacheLock )
32 typedef QHash< QString, QgsEllipsoidUtils::EllipsoidParameters > EllipsoidParamCache;
33 Q_GLOBAL_STATIC( EllipsoidParamCache, sEllipsoidCache )
34 
35 Q_GLOBAL_STATIC( QReadWriteLock, sDefinitionCacheLock );
36 typedef QList< QgsEllipsoidUtils::EllipsoidDefinition > EllipsoidDefinitionCache;
37 Q_GLOBAL_STATIC( EllipsoidDefinitionCache, sDefinitionCache )
38 
39 static bool sDisableCache = false;
40 
41 QgsEllipsoidUtils::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 #if PROJ_VERSION_MAJOR >= 6
175  // ensure ellipsoid database is populated when first called
176  static std::once_flag initialized;
177  std::call_once( initialized, [ = ]
178  {
179  QgsScopedRuntimeProfile profile( QObject::tr( "Initialize ellipsoids" ) );
180  ( void )definitions();
181  } );
182 
183  ellipsoid = sProj6EllipsoidAcronymMap.value( ellipsoid, ellipsoid ); // silently upgrade older QGIS acronyms to proj acronyms
184 #else
185  ( void )sProj6EllipsoidAcronymMap;
186 #endif
187 
188  // check cache
189  {
190  QgsReadWriteLocker locker( *sEllipsoidCacheLock(), QgsReadWriteLocker::Read );
191  if ( !sDisableCache )
192  {
193  QHash< QString, EllipsoidParameters >::const_iterator cacheIt = sEllipsoidCache()->constFind( ellipsoid );
194  if ( cacheIt != sEllipsoidCache()->constEnd() )
195  {
196  // found a match in the cache
197  QgsEllipsoidUtils::EllipsoidParameters params = cacheIt.value();
198  return params;
199  }
200  }
201  }
202 
203  EllipsoidParameters params;
204 
205  // Check if we have a custom projection, and set from text string.
206  // Format is "PARAMETER:<semi-major axis>:<semi minor axis>
207  // Numbers must be with (optional) decimal point and no other separators (C locale)
208  // Distances in meters. Flattening is calculated.
209  if ( ellipsoid.startsWith( QLatin1String( "PARAMETER" ) ) )
210  {
211  QStringList paramList = ellipsoid.split( ':' );
212  bool semiMajorOk, semiMinorOk;
213  double semiMajor = paramList[1].toDouble( & semiMajorOk );
214  double semiMinor = paramList[2].toDouble( & semiMinorOk );
215  if ( semiMajorOk && semiMinorOk )
216  {
217  params.semiMajor = semiMajor;
218  params.semiMinor = semiMinor;
219  params.inverseFlattening = semiMajor / ( semiMajor - semiMinor );
220  params.useCustomParameters = true;
221  }
222  else
223  {
224  params.valid = false;
225  }
226 
227  QgsReadWriteLocker locker( *sEllipsoidCacheLock(), QgsReadWriteLocker::Write );
228  if ( !sDisableCache )
229  {
230  sEllipsoidCache()->insert( ellipsoid, params );
231  }
232  return params;
233  }
234 
235 #if PROJ_VERSION_MAJOR< 6
236  // cache miss - get from database
237  // NOT REQUIRED FOR PROJ >= 6 -- we populate known types once by calling definitions() above
238 
239  QString radius, parameter2;
240  //
241  // SQLITE3 stuff - get parameters for selected ellipsoid
242  //
245  // Continue with PROJ list of ellipsoids.
246 
247  //check the db is available
248  int result = database.open_v2( QgsApplication::srsDatabaseFilePath(), SQLITE_OPEN_READONLY, nullptr );
249  if ( result )
250  {
251  QgsMessageLog::logMessage( QObject::tr( "Can not open srs database (%1): %2" ).arg( QgsApplication::srsDatabaseFilePath(), database.errorMessage() ) );
252  // XXX This will likely never happen since on open, sqlite creates the
253  // database if it does not exist.
254  return params;
255  }
256  // Set up the query to retrieve the projection information needed to populate the ELLIPSOID list
257  QString sql = "select radius, parameter2 from tbl_ellipsoid where acronym='" + ellipsoid + '\'';
258  statement = database.prepare( sql, result );
259  // XXX Need to free memory from the error msg if one is set
260  if ( result == SQLITE_OK )
261  {
262  if ( statement.step() == SQLITE_ROW )
263  {
264  radius = statement.columnAsText( 0 );
265  parameter2 = statement.columnAsText( 1 );
266  }
267  }
268  // row for this ellipsoid wasn't found?
269  if ( radius.isEmpty() || parameter2.isEmpty() )
270  {
271  QgsDebugMsg( QStringLiteral( "setEllipsoid: no row in tbl_ellipsoid for acronym '%1'" ).arg( ellipsoid ) );
272  params.valid = false;
273  sEllipsoidCacheLock()->lockForWrite();
274  if ( !sDisableCache )
275  {
276  sEllipsoidCache()->insert( ellipsoid, params );
277  }
278  sEllipsoidCacheLock()->unlock();
279  return params;
280  }
281 
282  // get major semiaxis
283  if ( radius.left( 2 ) == QLatin1String( "a=" ) )
284  params.semiMajor = radius.midRef( 2 ).toDouble();
285  else
286  {
287  QgsDebugMsg( QStringLiteral( "setEllipsoid: wrong format of radius field: '%1'" ).arg( radius ) );
288  params.valid = false;
289  sEllipsoidCacheLock()->lockForWrite();
290  if ( !sDisableCache )
291  {
292  sEllipsoidCache()->insert( ellipsoid, params );
293  }
294  sEllipsoidCacheLock()->unlock();
295  return params;
296  }
297 
298  // get second parameter
299  // one of values 'b' or 'f' is in field parameter2
300  // second one must be computed using formula: invf = a/(a-b)
301  if ( parameter2.left( 2 ) == QLatin1String( "b=" ) )
302  {
303  params.semiMinor = parameter2.midRef( 2 ).toDouble();
304  params.inverseFlattening = params.semiMajor / ( params.semiMajor - params.semiMinor );
305  }
306  else if ( parameter2.left( 3 ) == QLatin1String( "rf=" ) )
307  {
308  params.inverseFlattening = parameter2.midRef( 3 ).toDouble();
309  params.semiMinor = params.semiMajor - ( params.semiMajor / params.inverseFlattening );
310  }
311  else
312  {
313  QgsDebugMsg( QStringLiteral( "setEllipsoid: wrong format of parameter2 field: '%1'" ).arg( parameter2 ) );
314  params.valid = false;
315  sEllipsoidCacheLock()->lockForWrite();
316  if ( !sDisableCache )
317  {
318  sEllipsoidCache()->insert( ellipsoid, params );
319  }
320  sEllipsoidCacheLock()->unlock();
321  return params;
322  }
323 
324  QgsDebugMsgLevel( QStringLiteral( "setEllipsoid: a=%1, b=%2, 1/f=%3" ).arg( params.semiMajor ).arg( params.semiMinor ).arg( params.inverseFlattening ), 4 );
325 
326 
327  // get spatial ref system for ellipsoid
328  QString proj4 = "+proj=longlat +ellps=" + ellipsoid + " +no_defs";
330  //TODO: createFromProj used to save to the user database any new CRS
331  // this behavior was changed in order to separate creation and saving.
332  // Not sure if it necessary to save it here, should be checked by someone
333  // familiar with the code (should also give a more descriptive name to the generated CRS)
334  if ( destCRS.srsid() == 0 )
335  {
336  QString name = QStringLiteral( " * %1 (%2)" )
337  .arg( QObject::tr( "Generated CRS", "A CRS automatically generated from layer info get this prefix for description" ),
338  destCRS.toProj() );
339  destCRS.saveAsUserCrs( name );
340  }
341  //
342 
343  // set transformation from project CRS to ellipsoid coordinates
344  params.crs = destCRS;
345 
346  sEllipsoidCacheLock()->lockForWrite();
347  if ( !sDisableCache )
348  {
349  sEllipsoidCache()->insert( ellipsoid, params );
350  }
351  sEllipsoidCacheLock()->unlock();
352  return params;
353 #else
354  params.valid = false;
355 
356  QgsReadWriteLocker l( *sEllipsoidCacheLock(), QgsReadWriteLocker::Write );
357  if ( !sDisableCache )
358  {
359  sEllipsoidCache()->insert( ellipsoid, params );
360  }
361 
362  return params;
363 #endif
364 }
365 
366 QList<QgsEllipsoidUtils::EllipsoidDefinition> QgsEllipsoidUtils::definitions()
367 {
368  QgsReadWriteLocker defLocker( *sDefinitionCacheLock(), QgsReadWriteLocker::Read );
369  if ( !sDefinitionCache()->isEmpty() )
370  {
371  return *sDefinitionCache();
372  }
374 
375  QList<QgsEllipsoidUtils::EllipsoidDefinition> defs;
376 
377 #if PROJ_VERSION_MAJOR>=6
378  QgsReadWriteLocker locker( *sEllipsoidCacheLock(), QgsReadWriteLocker::Write );
379 
380  PJ_CONTEXT *context = QgsProjContext::get();
381  if ( PROJ_STRING_LIST authorities = proj_get_authorities_from_database( context ) )
382  {
383  PROJ_STRING_LIST authoritiesIt = authorities;
384  while ( char *authority = *authoritiesIt )
385  {
386  if ( PROJ_STRING_LIST codes = proj_get_codes_from_database( context, authority, PJ_TYPE_ELLIPSOID, 0 ) )
387  {
388  PROJ_STRING_LIST codesIt = codes;
389  while ( char *code = *codesIt )
390  {
391  QgsProjUtils::proj_pj_unique_ptr ellipsoid( proj_create_from_database( context, authority, code, PJ_CATEGORY_ELLIPSOID, 0, nullptr ) );
392  if ( ellipsoid.get() )
393  {
395  QString name = QString( proj_get_name( ellipsoid.get() ) );
396  def.acronym = QStringLiteral( "%1:%2" ).arg( authority, code );
397  name.replace( '_', ' ' );
398  def.description = QStringLiteral( "%1 (%2:%3)" ).arg( name, authority, code );
399 
400  double semiMajor, semiMinor, invFlattening;
401  int semiMinorComputed = 0;
402  if ( proj_ellipsoid_get_parameters( context, ellipsoid.get(), &semiMajor, &semiMinor, &semiMinorComputed, &invFlattening ) )
403  {
404  def.parameters.semiMajor = semiMajor;
405  def.parameters.semiMinor = semiMinor;
406  def.parameters.inverseFlattening = invFlattening;
407  if ( !semiMinorComputed )
408  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 );
409  else if ( !qgsDoubleNear( def.parameters.inverseFlattening, 0.0 ) )
410  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 );
411  else
412  def.parameters.crs.createFromProj( QStringLiteral( "+proj=longlat +a=%1 +no_defs +type=crs" ).arg( def.parameters.semiMajor, 0, 'g', 17 ), false );
413  }
414  else
415  {
416  def.parameters.valid = false;
417  }
418 
419  defs << def;
420  if ( !sDisableCache )
421  {
422  sEllipsoidCache()->insert( def.acronym, def.parameters );
423  }
424  }
425 
426  codesIt++;
427  }
428  proj_string_list_destroy( codes );
429  }
430 
431  authoritiesIt++;
432  }
433  proj_string_list_destroy( authorities );
434  }
435  locker.unlock();
436 
437 #else
440  int result;
441 
442  //check the db is available
443  result = database.open_v2( QgsApplication::srsDatabaseFilePath(), SQLITE_OPEN_READONLY, nullptr );
444  if ( result )
445  {
446  QgsDebugMsg( QStringLiteral( "Can't open database: %1" ).arg( database.errorMessage() ) );
447  // XXX This will likely never happen since on open, sqlite creates the
448  // database if it does not exist.
449  Q_ASSERT( result == 0 );
450  }
451 
452  // Set up the query to retrieve the projection information needed to populate the ELLIPSOID list
453  QString sql = QStringLiteral( "select acronym, name from tbl_ellipsoid order by name" );
454  statement = database.prepare( sql, result );
455 
456  if ( result == SQLITE_OK )
457  {
458  while ( statement.step() == SQLITE_ROW )
459  {
461  def.acronym = statement.columnAsText( 0 );
462  def.description = statement.columnAsText( 1 );
463 
464  // use ellipsoidParameters so that result is cached
466 
467  defs << def;
468  }
469  }
470 
471 #endif
472 
473  QCollator collator;
474  collator.setCaseSensitivity( Qt::CaseInsensitive );
475  std::sort( defs.begin(), defs.end(), [&collator]( const EllipsoidDefinition & a, const EllipsoidDefinition & b )
476  {
477  return collator.compare( a.description, b.description ) < 0;
478  } );
479  if ( !sDisableCache )
480  {
481  *sDefinitionCache() = defs;
482  }
483 
484  return defs;
485 }
486 
488 {
489  QStringList result;
490  const QList<QgsEllipsoidUtils::EllipsoidDefinition> defs = definitions();
491  result.reserve( defs.size() );
492  for ( const QgsEllipsoidUtils::EllipsoidDefinition &def : defs )
493  {
494  result << def.acronym;
495  }
496  return result;
497 }
498 
499 void QgsEllipsoidUtils::invalidateCache( bool disableCache )
500 {
501  QgsReadWriteLocker locker1( *sEllipsoidCacheLock(), QgsReadWriteLocker::Write );
502  QgsReadWriteLocker locker2( *sDefinitionCacheLock(), QgsReadWriteLocker::Write );
503 
504  if ( !sDisableCache )
505  {
506  if ( disableCache )
507  sDisableCache = true;
508  sEllipsoidCache()->clear();
509  sDefinitionCache()->clear();
510  }
511 }
static QString srsDatabaseFilePath()
Returns the path to the srs.db file.
This class represents a coordinate reference system (CRS).
QString toProj() const
Returns a Proj string representation of this CRS.
bool createFromProj(const QString &projString, bool identify=true)
Sets this CRS by passing it a PROJ style formatted string.
static QgsCoordinateReferenceSystem fromProj(const QString &proj)
Creates a CRS from a proj style formatted string.
long saveAsUserCrs(const QString &name, Format nativeFormat=FormatWkt)
Saves the CRS as a new custom ("USER") CRS.
long srsid() const
Returns the internal CRS ID, if available.
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 void invalidateCache(bool disableCache=false)
Clears the internal cache used.
static EllipsoidParameters ellipsoidParameters(const QString &ellipsoid)
Returns the parameters for the specified ellipsoid.
static QStringList acronyms()
Returns a list of all known ellipsoid acronyms from the internal ellipsoid database.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
static PJ_CONTEXT * get()
Returns a thread local instance of a proj context, safe for use in the current thread.
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.
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
QString errorMessage() const
Returns the most recent error message encountered by the database.
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
QString columnAsText(int column) const
Returns the column value from the current statement row as a string.
int step()
Steps to the next record in the statement, returning the sqlite3 result code.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:316
QList< QgsEllipsoidUtils::EllipsoidDefinition > EllipsoidDefinitionCache
QHash< QString, QgsEllipsoidUtils::EllipsoidParameters > EllipsoidParamCache
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
void PJ_CONTEXT
Definition: qgsprojutils.h:151
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 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)