QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgscoordinatereferencesystem.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscoordinatereferencesystem.cpp
3 
4  -------------------
5  begin : 2007
6  copyright : (C) 2007 by Gary E. Sherman
7  email : [email protected]
8 ***************************************************************************/
9 
10 /***************************************************************************
11  * *
12  * This program is free software; you can redistribute it and/or modify *
13  * it under the terms of the GNU General Public License as published by *
14  * the Free Software Foundation; either version 2 of the License, or *
15  * (at your option) any later version. *
16  * *
17  ***************************************************************************/
20 
23 #include "qgsreadwritelocker.h"
24 
25 #include <cmath>
26 
27 #include <QDir>
28 #include <QDomNode>
29 #include <QDomElement>
30 #include <QFileInfo>
31 #include <QRegularExpression>
32 #include <QTextStream>
33 #include <QFile>
34 
35 #include "qgsapplication.h"
36 #include "qgslogger.h"
37 #include "qgsmessagelog.h"
38 #include "qgis.h" //const vals declared here
39 #include "qgslocalec.h"
40 #include "qgssettings.h"
41 #include "qgsogrutils.h"
42 #include "qgsdatums.h"
43 #include "qgsprojectionfactors.h"
44 #include "qgsprojoperation.h"
45 
46 #include <sqlite3.h>
47 #include "qgsprojutils.h"
48 #include <proj.h>
49 #include <proj_experimental.h>
50 
51 //gdal and ogr includes (needed for == operator)
52 #include <ogr_srs_api.h>
53 #include <cpl_error.h>
54 #include <cpl_conv.h>
55 #include <cpl_csv.h>
56 
57 CUSTOM_CRS_VALIDATION QgsCoordinateReferenceSystem::sCustomSrsValidation = nullptr;
58 
59 typedef QHash< long, QgsCoordinateReferenceSystem > SrIdCrsCacheHash;
60 typedef QHash< QString, QgsCoordinateReferenceSystem > StringCrsCacheHash;
61 
62 Q_GLOBAL_STATIC( QReadWriteLock, sSrIdCacheLock )
63 Q_GLOBAL_STATIC( SrIdCrsCacheHash, sSrIdCache )
64 bool QgsCoordinateReferenceSystem::sDisableSrIdCache = false;
65 
66 Q_GLOBAL_STATIC( QReadWriteLock, sOgcLock )
68 bool QgsCoordinateReferenceSystem::sDisableOgcCache = false;
69 
70 Q_GLOBAL_STATIC( QReadWriteLock, sProj4CacheLock )
71 Q_GLOBAL_STATIC( StringCrsCacheHash, sProj4Cache )
72 bool QgsCoordinateReferenceSystem::sDisableProjCache = false;
73 
74 Q_GLOBAL_STATIC( QReadWriteLock, sCRSWktLock )
76 bool QgsCoordinateReferenceSystem::sDisableWktCache = false;
77 
78 Q_GLOBAL_STATIC( QReadWriteLock, sCRSSrsIdLock )
79 Q_GLOBAL_STATIC( SrIdCrsCacheHash, sSrsIdCache )
80 bool QgsCoordinateReferenceSystem::sDisableSrsIdCache = false;
81 
82 Q_GLOBAL_STATIC( QReadWriteLock, sCrsStringLock )
83 Q_GLOBAL_STATIC( StringCrsCacheHash, sStringCache )
84 bool QgsCoordinateReferenceSystem::sDisableStringCache = false;
85 
86 QString getFullProjString( PJ *obj )
87 {
88  // see https://lists.osgeo.org/pipermail/proj/2019-May/008565.html, it's not sufficient to just
89  // use proj_as_proj_string
90  QgsProjUtils::proj_pj_unique_ptr boundCrs( proj_crs_create_bound_crs_to_WGS84( QgsProjContext::get(), obj, nullptr ) );
91  if ( boundCrs )
92  {
93  if ( const char *proj4src = proj_as_proj_string( QgsProjContext::get(), boundCrs.get(), PJ_PROJ_4, nullptr ) )
94  {
95  return QString( proj4src );
96  }
97  }
98 
99  return QString( proj_as_proj_string( QgsProjContext::get(), obj, PJ_PROJ_4, nullptr ) );
100 }
101 //--------------------------
102 
104 {
105  static QgsCoordinateReferenceSystem nullCrs = QgsCoordinateReferenceSystem( QString() );
106 
107  d = nullCrs.d;
108 }
109 
111 {
112  d = new QgsCoordinateReferenceSystemPrivate();
113  createFromString( definition );
114 }
115 
117 {
118  d = new QgsCoordinateReferenceSystemPrivate();
120  createFromId( id, type );
122 }
123 
125  : d( srs.d )
126  , mValidationHint( srs.mValidationHint )
127  , mNativeFormat( srs.mNativeFormat )
128 {
129 }
130 
132 {
133  d = srs.d;
134  mValidationHint = srs.mValidationHint;
135  mNativeFormat = srs.mNativeFormat;
136  return *this;
137 }
138 
140 {
141  QList<long> results;
142  // check both standard & user defined projection databases
143  QStringList dbs = QStringList() << QgsApplication::srsDatabaseFilePath() << QgsApplication::qgisUserDatabaseFilePath();
144 
145  const auto constDbs = dbs;
146  for ( const QString &db : constDbs )
147  {
148  QFileInfo myInfo( db );
149  if ( !myInfo.exists() )
150  {
151  QgsDebugMsg( "failed : " + db + " does not exist!" );
152  continue;
153  }
154 
157 
158  //check the db is available
159  int result = openDatabase( db, database );
160  if ( result != SQLITE_OK )
161  {
162  QgsDebugMsg( "failed : " + db + " could not be opened!" );
163  continue;
164  }
165 
166  QString sql = QStringLiteral( "select srs_id from tbl_srs" );
167  int rc;
168  statement = database.prepare( sql, rc );
169  while ( true )
170  {
171  // this one is an infinitive loop, intended to fetch any row
172  int ret = statement.step();
173 
174  if ( ret == SQLITE_DONE )
175  {
176  // there are no more rows to fetch - we can stop looping
177  break;
178  }
179 
180  if ( ret == SQLITE_ROW )
181  {
182  results.append( statement.columnAsInt64( 0 ) );
183  }
184  else
185  {
186  QgsMessageLog::logMessage( QObject::tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr( "SpatiaLite" ) );
187  break;
188  }
189  }
190  }
191  std::sort( results.begin(), results.end() );
192  return results;
193 }
194 
196 {
198  crs.createFromOgcWmsCrs( ogcCrs );
199  return crs;
200 }
201 
203 {
204  QgsCoordinateReferenceSystem res = fromOgcWmsCrs( "EPSG:" + QString::number( epsg ) );
205  if ( res.isValid() )
206  return res;
207 
208  // pre proj6 builds allowed use of ESRI: codes here (e.g. 54030), so we need to keep compatibility
209  res = fromOgcWmsCrs( "ESRI:" + QString::number( epsg ) );
210  if ( res.isValid() )
211  return res;
212 
214 }
215 
217 {
218  return fromProj( proj4 );
219 }
220 
222 {
224  crs.createFromProj( proj );
225  return crs;
226 }
227 
229 {
231  crs.createFromWkt( wkt );
232  return crs;
233 }
234 
236 {
238  crs.createFromSrsId( srsId );
239  return crs;
240 }
241 
243 {
244 }
245 
247 {
248  bool result = false;
249  switch ( type )
250  {
251  case InternalCrsId:
252  result = createFromSrsId( id );
253  break;
254  case PostgisCrsId:
256  result = createFromSrid( id );
258  break;
259  case EpsgCrsId:
260  result = createFromOgcWmsCrs( QStringLiteral( "EPSG:%1" ).arg( id ) );
261  break;
262  default:
263  //THIS IS BAD...THIS PART OF CODE SHOULD NEVER BE REACHED...
264  QgsDebugMsg( QStringLiteral( "Unexpected case reached!" ) );
265  };
266  return result;
267 }
268 
269 bool QgsCoordinateReferenceSystem::createFromString( const QString &definition )
270 {
271  if ( definition.isEmpty() )
272  return false;
273 
274  QgsReadWriteLocker locker( *sCrsStringLock(), QgsReadWriteLocker::Read );
275  if ( !sDisableStringCache )
276  {
277  QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sStringCache()->constFind( definition );
278  if ( crsIt != sStringCache()->constEnd() )
279  {
280  // found a match in the cache
281  *this = crsIt.value();
282  return d->mIsValid;
283  }
284  }
285  locker.unlock();
286 
287  bool result = false;
288  const thread_local QRegularExpression reCrsId( QStringLiteral( "^(epsg|esri|osgeo|ignf|ogc|nkg|zangi|iau_2015|iau2000|postgis|internal|user)\\:(\\w+)$" ), QRegularExpression::CaseInsensitiveOption );
289  QRegularExpressionMatch match = reCrsId.match( definition );
290  if ( match.capturedStart() == 0 )
291  {
292  QString authName = match.captured( 1 ).toLower();
293  if ( authName == QLatin1String( "epsg" ) )
294  {
295  result = createFromOgcWmsCrs( definition );
296  }
297  else if ( authName == QLatin1String( "postgis" ) )
298  {
299  const long id = match.captured( 2 ).toLong();
301  result = createFromSrid( id );
303  }
304  else if ( authName == QLatin1String( "esri" )
305  || authName == QLatin1String( "osgeo" )
306  || authName == QLatin1String( "ignf" )
307  || authName == QLatin1String( "zangi" )
308  || authName == QLatin1String( "iau2000" )
309  || authName == QLatin1String( "ogc" )
310  || authName == QLatin1String( "nkg" )
311  || authName == QLatin1String( "iau_2015" )
312  )
313  {
314  result = createFromOgcWmsCrs( definition );
315  }
316  else
317  {
318  const long id = match.captured( 2 ).toLong();
320  result = createFromId( id, InternalCrsId );
322  }
323  }
324  else
325  {
326  const thread_local QRegularExpression reCrsStr( QStringLiteral( "^(?:(wkt|proj4|proj)\\:)?(.+)$" ), QRegularExpression::CaseInsensitiveOption );
327  match = reCrsStr.match( definition );
328  if ( match.capturedStart() == 0 )
329  {
330  if ( match.captured( 1 ).startsWith( QLatin1String( "proj" ), Qt::CaseInsensitive ) )
331  {
332  result = createFromProj( match.captured( 2 ) );
333  }
334  else
335  {
336  result = createFromWkt( match.captured( 2 ) );
337  }
338  }
339  }
340 
342  if ( !sDisableStringCache )
343  sStringCache()->insert( definition, *this );
344  return result;
345 }
346 
347 bool QgsCoordinateReferenceSystem::createFromUserInput( const QString &definition )
348 {
349  if ( definition.isEmpty() )
350  return false;
351 
352  QString userWkt;
353  OGRSpatialReferenceH crs = OSRNewSpatialReference( nullptr );
354 
355  if ( OSRSetFromUserInput( crs, definition.toLocal8Bit().constData() ) == OGRERR_NONE )
356  {
358  OSRDestroySpatialReference( crs );
359  }
360  //QgsDebugMsg( "definition: " + definition + " wkt = " + wkt );
361  return createFromWkt( userWkt );
362 }
363 
365 {
366  // make sure towgs84 parameter is loaded if gdal >= 1.9
367  // this requires setting GDAL_FIX_ESRI_WKT=GEOGCS (see qgis bug #5598 and gdal bug #4673)
368  const char *configOld = CPLGetConfigOption( "GDAL_FIX_ESRI_WKT", "" );
369  const char *configNew = "GEOGCS";
370  // only set if it was not set, to let user change the value if needed
371  if ( strcmp( configOld, "" ) == 0 )
372  {
373  CPLSetConfigOption( "GDAL_FIX_ESRI_WKT", configNew );
374  if ( strcmp( configNew, CPLGetConfigOption( "GDAL_FIX_ESRI_WKT", "" ) ) != 0 )
375  QgsLogger::warning( QStringLiteral( "GDAL_FIX_ESRI_WKT could not be set to %1 : %2" )
376  .arg( configNew, CPLGetConfigOption( "GDAL_FIX_ESRI_WKT", "" ) ) );
377  QgsDebugMsgLevel( QStringLiteral( "set GDAL_FIX_ESRI_WKT : %1" ).arg( configNew ), 4 );
378  }
379  else
380  {
381  QgsDebugMsgLevel( QStringLiteral( "GDAL_FIX_ESRI_WKT was already set : %1" ).arg( configNew ), 4 );
382  }
383 }
384 
386 {
387  if ( crs.isEmpty() )
388  return false;
389 
390  QgsReadWriteLocker locker( *sOgcLock(), QgsReadWriteLocker::Read );
391  if ( !sDisableOgcCache )
392  {
393  QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sOgcCache()->constFind( crs );
394  if ( crsIt != sOgcCache()->constEnd() )
395  {
396  // found a match in the cache
397  *this = crsIt.value();
398  return d->mIsValid;
399  }
400  }
401  locker.unlock();
402 
403  QString wmsCrs = crs;
404 
405  thread_local const QRegularExpression re_uri( QRegularExpression::anchoredPattern( QStringLiteral( "http://www\\.opengis\\.net/def/crs/([^/]+).+/([^/]+)" ) ), QRegularExpression::CaseInsensitiveOption );
406  QRegularExpressionMatch match = re_uri.match( wmsCrs );
407  if ( match.hasMatch() )
408  {
409  wmsCrs = match.captured( 1 ) + ':' + match.captured( 2 );
410  }
411  else
412  {
413  thread_local const QRegularExpression re_urn( QRegularExpression::anchoredPattern( QStringLiteral( "urn:ogc:def:crs:([^:]+).+(?<=:)([^:]+)" ) ), QRegularExpression::CaseInsensitiveOption );
414  match = re_urn.match( wmsCrs );
415  if ( match.hasMatch() )
416  {
417  wmsCrs = match.captured( 1 ) + ':' + match.captured( 2 );
418  }
419  else
420  {
421  thread_local const QRegularExpression re_urn_custom( QRegularExpression::anchoredPattern( QStringLiteral( "(user|custom|qgis):(\\d+)" ) ), QRegularExpression::CaseInsensitiveOption );
422  match = re_urn_custom.match( wmsCrs );
423  if ( match.hasMatch() && createFromSrsId( match.captured( 2 ).toInt() ) )
424  {
426  if ( !sDisableOgcCache )
427  sOgcCache()->insert( crs, *this );
428  return d->mIsValid;
429  }
430  }
431  }
432 
433  // first chance for proj 6 - scan through legacy systems and try to use authid directly
434  const QString legacyKey = wmsCrs.toLower();
435  for ( auto it = sAuthIdToQgisSrsIdMap.constBegin(); it != sAuthIdToQgisSrsIdMap.constEnd(); ++it )
436  {
437  if ( it.key().compare( legacyKey, Qt::CaseInsensitive ) == 0 )
438  {
439  const QStringList parts = it.key().split( ':' );
440  const QString auth = parts.at( 0 );
441  const QString code = parts.at( 1 );
442  if ( loadFromAuthCode( auth, code ) )
443  {
445  if ( !sDisableOgcCache )
446  sOgcCache()->insert( crs, *this );
447  return d->mIsValid;
448  }
449  }
450  }
451 
452  if ( loadFromDatabase( QgsApplication::srsDatabaseFilePath(), QStringLiteral( "lower(auth_name||':'||auth_id)" ), wmsCrs.toLower() ) )
453  {
455  if ( !sDisableOgcCache )
456  sOgcCache()->insert( crs, *this );
457  return d->mIsValid;
458  }
459 
460  // NAD27
461  if ( wmsCrs.compare( QLatin1String( "CRS:27" ), Qt::CaseInsensitive ) == 0 ||
462  wmsCrs.compare( QLatin1String( "OGC:CRS27" ), Qt::CaseInsensitive ) == 0 )
463  {
464  // TODO: verify same axis orientation
465  return createFromOgcWmsCrs( QStringLiteral( "EPSG:4267" ) );
466  }
467 
468  // NAD83
469  if ( wmsCrs.compare( QLatin1String( "CRS:83" ), Qt::CaseInsensitive ) == 0 ||
470  wmsCrs.compare( QLatin1String( "OGC:CRS83" ), Qt::CaseInsensitive ) == 0 )
471  {
472  // TODO: verify same axis orientation
473  return createFromOgcWmsCrs( QStringLiteral( "EPSG:4269" ) );
474  }
475 
476  // WGS84
477  if ( wmsCrs.compare( QLatin1String( "CRS:84" ), Qt::CaseInsensitive ) == 0 ||
478  wmsCrs.compare( QLatin1String( "OGC:CRS84" ), Qt::CaseInsensitive ) == 0 )
479  {
480  if ( loadFromDatabase( QgsApplication::srsDatabaseFilePath(), QStringLiteral( "lower(auth_name||':'||auth_id)" ), QStringLiteral( "epsg:4326" ) ) )
481  {
482  d->mAxisInverted = false;
483  d->mAxisInvertedDirty = false;
484  }
485 
487  if ( !sDisableOgcCache )
488  sOgcCache()->insert( crs, *this );
489 
490  return d->mIsValid;
491  }
492 
494  if ( !sDisableOgcCache )
495  sOgcCache()->insert( crs, QgsCoordinateReferenceSystem() );
496  return d->mIsValid;
497 }
498 
499 // Misc helper functions -----------------------
500 
501 
503 {
504  if ( d->mIsValid || !sCustomSrsValidation )
505  return;
506 
507  // try to validate using custom validation routines
508  if ( sCustomSrsValidation )
509  sCustomSrsValidation( *this );
510 }
511 
513 {
514  QgsReadWriteLocker locker( *sSrIdCacheLock(), QgsReadWriteLocker::Read );
515  if ( !sDisableSrIdCache )
516  {
517  QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrIdCache()->constFind( id );
518  if ( crsIt != sSrIdCache()->constEnd() )
519  {
520  // found a match in the cache
521  *this = crsIt.value();
522  return d->mIsValid;
523  }
524  }
525  locker.unlock();
526 
527  // first chance for proj 6 - scan through legacy systems and try to use authid directly
528  for ( auto it = sAuthIdToQgisSrsIdMap.constBegin(); it != sAuthIdToQgisSrsIdMap.constEnd(); ++it )
529  {
530  if ( it.value().endsWith( QStringLiteral( ",%1" ).arg( id ) ) )
531  {
532  const QStringList parts = it.key().split( ':' );
533  const QString auth = parts.at( 0 );
534  const QString code = parts.at( 1 );
535  if ( loadFromAuthCode( auth, code ) )
536  {
538  if ( !sDisableSrIdCache )
539  sSrIdCache()->insert( id, *this );
540 
541  return d->mIsValid;
542  }
543  }
544  }
545 
546  bool result = loadFromDatabase( QgsApplication::srsDatabaseFilePath(), QStringLiteral( "srid" ), QString::number( id ) );
547 
549  if ( !sDisableSrIdCache )
550  sSrIdCache()->insert( id, *this );
551 
552  return result;
553 }
554 
556 {
557  QgsReadWriteLocker locker( *sCRSSrsIdLock(), QgsReadWriteLocker::Read );
558  if ( !sDisableSrsIdCache )
559  {
560  QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrsIdCache()->constFind( id );
561  if ( crsIt != sSrsIdCache()->constEnd() )
562  {
563  // found a match in the cache
564  *this = crsIt.value();
565  return d->mIsValid;
566  }
567  }
568  locker.unlock();
569 
570  // first chance for proj 6 - scan through legacy systems and try to use authid directly
571  for ( auto it = sAuthIdToQgisSrsIdMap.constBegin(); it != sAuthIdToQgisSrsIdMap.constEnd(); ++it )
572  {
573  if ( it.value().startsWith( QString::number( id ) + ',' ) )
574  {
575  const QStringList parts = it.key().split( ':' );
576  const QString auth = parts.at( 0 );
577  const QString code = parts.at( 1 );
578  if ( loadFromAuthCode( auth, code ) )
579  {
581  if ( !sDisableSrsIdCache )
582  sSrsIdCache()->insert( id, *this );
583  return d->mIsValid;
584  }
585  }
586  }
587 
588  bool result = loadFromDatabase( id < USER_CRS_START_ID ? QgsApplication::srsDatabaseFilePath() :
590  QStringLiteral( "srs_id" ), QString::number( id ) );
591 
593  if ( !sDisableSrsIdCache )
594  sSrsIdCache()->insert( id, *this );
595  return result;
596 }
597 
598 bool QgsCoordinateReferenceSystem::loadFromDatabase( const QString &db, const QString &expression, const QString &value )
599 {
600  d.detach();
601 
602  QgsDebugMsgLevel( "load CRS from " + db + " where " + expression + " is " + value, 3 );
603  d->mIsValid = false;
604  d->mWktPreferred.clear();
605 
606  QFileInfo myInfo( db );
607  if ( !myInfo.exists() )
608  {
609  QgsDebugMsg( "failed : " + db + " does not exist!" );
610  return d->mIsValid;
611  }
612 
615  int myResult;
616  //check the db is available
617  myResult = openDatabase( db, database );
618  if ( myResult != SQLITE_OK )
619  {
620  return d->mIsValid;
621  }
622 
623  /*
624  srs_id INTEGER PRIMARY KEY,
625  description text NOT NULL,
626  projection_acronym text NOT NULL,
627  ellipsoid_acronym NOT NULL,
628  parameters text NOT NULL,
629  srid integer NOT NULL,
630  auth_name varchar NOT NULL,
631  auth_id integer NOT NULL,
632  is_geo integer NOT NULL);
633  */
634 
635  QString mySql = "select srs_id,description,projection_acronym,"
636  "ellipsoid_acronym,parameters,srid,auth_name||':'||auth_id,is_geo,wkt "
637  "from tbl_srs where " + expression + '=' + QgsSqliteUtils::quotedString( value ) + " order by deprecated";
638  statement = database.prepare( mySql, myResult );
639  QString wkt;
640  // XXX Need to free memory from the error msg if one is set
641  if ( myResult == SQLITE_OK && statement.step() == SQLITE_ROW )
642  {
643  d->mSrsId = statement.columnAsText( 0 ).toLong();
644  d->mDescription = statement.columnAsText( 1 );
645  d->mProjectionAcronym = statement.columnAsText( 2 );
646  d->mEllipsoidAcronym.clear();
647  d->mProj4 = statement.columnAsText( 4 );
648  d->mWktPreferred.clear();
649  d->mSRID = statement.columnAsText( 5 ).toLong();
650  d->mAuthId = statement.columnAsText( 6 );
651  d->mIsGeographic = statement.columnAsText( 7 ).toInt() != 0;
652  wkt = statement.columnAsText( 8 );
653  d->mAxisInvertedDirty = true;
654 
655  if ( d->mSrsId >= USER_CRS_START_ID && ( d->mAuthId.isEmpty() || d->mAuthId == QChar( ':' ) ) )
656  {
657  d->mAuthId = QStringLiteral( "USER:%1" ).arg( d->mSrsId );
658  }
659  else if ( !d->mAuthId.startsWith( QLatin1String( "USER:" ), Qt::CaseInsensitive ) )
660  {
661  QStringList parts = d->mAuthId.split( ':' );
662  QString auth = parts.at( 0 );
663  QString code = parts.at( 1 );
664 
665  {
666  QgsProjUtils::proj_pj_unique_ptr crs( proj_create_from_database( QgsProjContext::get(), auth.toLatin1(), code.toLatin1(), PJ_CATEGORY_CRS, false, nullptr ) );
667  d->setPj( QgsProjUtils::crsToSingleCrs( crs.get() ) );
668  }
669 
670  d->mIsValid = d->hasPj();
671  setMapUnits();
672  }
673 
674  if ( !d->mIsValid )
675  {
676  if ( !wkt.isEmpty() )
677  {
678  setWktString( wkt );
679  // set WKT string resets the description to that description embedded in the WKT, so manually overwrite this back to the
680  // value from the user DB
681  d->mDescription = statement.columnAsText( 1 );
682  }
683  else
684  setProjString( d->mProj4 );
685  }
686  }
687  else
688  {
689  QgsDebugMsgLevel( "failed : " + mySql, 4 );
690  }
691  return d->mIsValid;
692 }
693 
694 void QgsCoordinateReferenceSystem::removeFromCacheObjectsBelongingToCurrentThread( PJ_CONTEXT *pj_context )
695 {
696  // Not completely sure about object order destruction after main() has
697  // exited. So it is safer to check sDisableCache before using sCacheLock
698  // in case sCacheLock would have been destroyed before the current TLS
699  // QgsProjContext object that has called us...
700 
701  if ( !sDisableSrIdCache )
702  {
703  QgsReadWriteLocker locker( *sSrIdCacheLock(), QgsReadWriteLocker::Write );
704  if ( !sDisableSrIdCache )
705  {
706  for ( auto it = sSrIdCache()->begin(); it != sSrIdCache()->end(); )
707  {
708  auto &v = it.value();
709  if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
710  it = sSrIdCache()->erase( it );
711  else
712  ++it;
713  }
714  }
715  }
716  if ( !sDisableOgcCache )
717  {
718  QgsReadWriteLocker locker( *sOgcLock(), QgsReadWriteLocker::Write );
719  if ( !sDisableOgcCache )
720  {
721  for ( auto it = sOgcCache()->begin(); it != sOgcCache()->end(); )
722  {
723  auto &v = it.value();
724  if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
725  it = sOgcCache()->erase( it );
726  else
727  ++it;
728  }
729  }
730  }
731  if ( !sDisableProjCache )
732  {
733  QgsReadWriteLocker locker( *sProj4CacheLock(), QgsReadWriteLocker::Write );
734  if ( !sDisableProjCache )
735  {
736  for ( auto it = sProj4Cache()->begin(); it != sProj4Cache()->end(); )
737  {
738  auto &v = it.value();
739  if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
740  it = sProj4Cache()->erase( it );
741  else
742  ++it;
743  }
744  }
745  }
746  if ( !sDisableWktCache )
747  {
748  QgsReadWriteLocker locker( *sCRSWktLock(), QgsReadWriteLocker::Write );
749  if ( !sDisableWktCache )
750  {
751  for ( auto it = sWktCache()->begin(); it != sWktCache()->end(); )
752  {
753  auto &v = it.value();
754  if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
755  it = sWktCache()->erase( it );
756  else
757  ++it;
758  }
759  }
760  }
761  if ( !sDisableSrsIdCache )
762  {
763  QgsReadWriteLocker locker( *sCRSSrsIdLock(), QgsReadWriteLocker::Write );
764  if ( !sDisableSrsIdCache )
765  {
766  for ( auto it = sSrsIdCache()->begin(); it != sSrsIdCache()->end(); )
767  {
768  auto &v = it.value();
769  if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
770  it = sSrsIdCache()->erase( it );
771  else
772  ++it;
773  }
774  }
775  }
776  if ( !sDisableStringCache )
777  {
778  QgsReadWriteLocker locker( *sCrsStringLock(), QgsReadWriteLocker::Write );
779  if ( !sDisableStringCache )
780  {
781  for ( auto it = sStringCache()->begin(); it != sStringCache()->end(); )
782  {
783  auto &v = it.value();
784  if ( v.d->removeObjectsBelongingToCurrentThread( pj_context ) )
785  it = sStringCache()->erase( it );
786  else
787  ++it;
788  }
789  }
790  }
791 }
792 
794 {
795  if ( d->mAxisInvertedDirty )
796  {
797  d->mAxisInverted = QgsProjUtils::axisOrderIsSwapped( d->threadLocalProjObject() );
798  d->mAxisInvertedDirty = false;
799  }
800 
801  return d->mAxisInverted;
802 }
803 
804 QList<Qgis::CrsAxisDirection> QgsCoordinateReferenceSystem::axisOrdering() const
805 {
806  const PJ *projObject = d->threadLocalProjObject();
807  if ( !projObject )
808  return {};
809 
810  PJ_CONTEXT *context = QgsProjContext::get();
811  QgsProjUtils::proj_pj_unique_ptr pjCs( proj_crs_get_coordinate_system( context, projObject ) );
812  if ( !pjCs )
813  return {};
814 
815  const thread_local QMap< Qgis::CrsAxisDirection, QString > mapping =
816  {
817  { Qgis::CrsAxisDirection::North, QStringLiteral( "north" ) },
818  { Qgis::CrsAxisDirection::NorthNorthEast, QStringLiteral( "northNorthEast" ) },
819  { Qgis::CrsAxisDirection::NorthEast, QStringLiteral( "northEast" ) },
820  { Qgis::CrsAxisDirection::EastNorthEast, QStringLiteral( "eastNorthEast" ) },
821  { Qgis::CrsAxisDirection::East, QStringLiteral( "east" ) },
822  { Qgis::CrsAxisDirection::EastSouthEast, QStringLiteral( "eastSouthEast" ) },
823  { Qgis::CrsAxisDirection::SouthEast, QStringLiteral( "southEast" ) },
824  { Qgis::CrsAxisDirection::SouthSouthEast, QStringLiteral( "southSouthEast" ) },
825  { Qgis::CrsAxisDirection::South, QStringLiteral( "south" ) },
826  { Qgis::CrsAxisDirection::SouthSouthWest, QStringLiteral( "southSouthWest" ) },
827  { Qgis::CrsAxisDirection::SouthWest, QStringLiteral( "southWest" ) },
828  { Qgis::CrsAxisDirection::WestSouthWest, QStringLiteral( "westSouthWest" ) },
829  { Qgis::CrsAxisDirection::West, QStringLiteral( "west" ) },
830  { Qgis::CrsAxisDirection::WestNorthWest, QStringLiteral( "westNorthWest" ) },
831  { Qgis::CrsAxisDirection::NorthWest, QStringLiteral( "northWest" ) },
832  { Qgis::CrsAxisDirection::NorthNorthWest, QStringLiteral( "northNorthWest" ) },
833  { Qgis::CrsAxisDirection::GeocentricX, QStringLiteral( "geocentricX" ) },
834  { Qgis::CrsAxisDirection::GeocentricY, QStringLiteral( "geocentricY" ) },
835  { Qgis::CrsAxisDirection::GeocentricZ, QStringLiteral( "geocentricZ" ) },
836  { Qgis::CrsAxisDirection::Up, QStringLiteral( "up" ) },
837  { Qgis::CrsAxisDirection::Down, QStringLiteral( "down" ) },
838  { Qgis::CrsAxisDirection::Forward, QStringLiteral( "forward" ) },
839  { Qgis::CrsAxisDirection::Aft, QStringLiteral( "aft" ) },
840  { Qgis::CrsAxisDirection::Port, QStringLiteral( "port" ) },
841  { Qgis::CrsAxisDirection::Starboard, QStringLiteral( "starboard" ) },
842  { Qgis::CrsAxisDirection::Clockwise, QStringLiteral( "clockwise" ) },
843  { Qgis::CrsAxisDirection::CounterClockwise, QStringLiteral( "counterClockwise" ) },
844  { Qgis::CrsAxisDirection::ColumnPositive, QStringLiteral( "columnPositive" ) },
845  { Qgis::CrsAxisDirection::ColumnNegative, QStringLiteral( "columnNegative" ) },
846  { Qgis::CrsAxisDirection::RowPositive, QStringLiteral( "rowPositive" ) },
847  { Qgis::CrsAxisDirection::RowNegative, QStringLiteral( "rowNegative" ) },
848  { Qgis::CrsAxisDirection::DisplayRight, QStringLiteral( "displayRight" ) },
849  { Qgis::CrsAxisDirection::DisplayLeft, QStringLiteral( "displayLeft" ) },
850  { Qgis::CrsAxisDirection::DisplayUp, QStringLiteral( "displayUp" ) },
851  { Qgis::CrsAxisDirection::DisplayDown, QStringLiteral( "displayDown" ) },
852  { Qgis::CrsAxisDirection::Future, QStringLiteral( "future" ) },
853  { Qgis::CrsAxisDirection::Past, QStringLiteral( "past" ) },
854  { Qgis::CrsAxisDirection::Towards, QStringLiteral( "towards" ) },
855  { Qgis::CrsAxisDirection::AwayFrom, QStringLiteral( "awayFrom" ) },
856  };
857 
858  QList< Qgis::CrsAxisDirection > res;
859  const int axisCount = proj_cs_get_axis_count( context, pjCs.get() );
860  if ( axisCount > 0 )
861  {
862  res.reserve( axisCount );
863 
864  for ( int i = 0; i < axisCount; ++i )
865  {
866  const char *outDirection = nullptr;
867  proj_cs_get_axis_info( context, pjCs.get(), i,
868  nullptr,
869  nullptr,
870  &outDirection,
871  nullptr,
872  nullptr,
873  nullptr,
874  nullptr
875  );
876  // get first word of direction only
877  const thread_local QRegularExpression rx( QStringLiteral( "([^\\s]+).*" ) );
878  const QRegularExpressionMatch match = rx.match( QString( outDirection ) );
879  if ( !match.hasMatch() )
880  continue;
881 
882  const QString direction = match.captured( 1 );
884  for ( auto it = mapping.constBegin(); it != mapping.constEnd(); ++it )
885  {
886  if ( it.value().compare( direction, Qt::CaseInsensitive ) == 0 )
887  {
888  dir = it.key();
889  break;
890  }
891  }
892 
893  res.append( dir );
894  }
895  }
896  return res;
897 }
898 
900 {
901  return createFromWktInternal( wkt, QString() );
902 }
903 
904 bool QgsCoordinateReferenceSystem::createFromWktInternal( const QString &wkt, const QString &description )
905 {
906  if ( wkt.isEmpty() )
907  return false;
908 
909  d.detach();
910 
911  QgsReadWriteLocker locker( *sCRSWktLock(), QgsReadWriteLocker::Read );
912  if ( !sDisableWktCache )
913  {
914  QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sWktCache()->constFind( wkt );
915  if ( crsIt != sWktCache()->constEnd() )
916  {
917  // found a match in the cache
918  *this = crsIt.value();
919 
920  if ( !description.isEmpty() && d->mDescription.isEmpty() )
921  {
922  // now we have a name for a previously unknown CRS! Update the cached CRS accordingly, so that we use the name from now on...
923  d->mDescription = description;
924  locker.changeMode( QgsReadWriteLocker::Write );
925  sWktCache()->insert( wkt, *this );
926  }
927  return d->mIsValid;
928  }
929  }
930  locker.unlock();
931 
932  d->mIsValid = false;
933  d->mProj4.clear();
934  d->mWktPreferred.clear();
935  if ( wkt.isEmpty() )
936  {
937  QgsDebugMsgLevel( QStringLiteral( "theWkt is uninitialized, operation failed" ), 4 );
938  return d->mIsValid;
939  }
940 
941  // try to match against user crs
942  QgsCoordinateReferenceSystem::RecordMap record = getRecord( "select * from tbl_srs where wkt=" + QgsSqliteUtils::quotedString( wkt ) + " order by deprecated" );
943  if ( !record.empty() )
944  {
945  long srsId = record[QStringLiteral( "srs_id" )].toLong();
946  if ( srsId > 0 )
947  {
948  createFromSrsId( srsId );
949  }
950  }
951  else
952  {
953  setWktString( wkt );
954  if ( !description.isEmpty() )
955  {
956  d->mDescription = description;
957  }
958  if ( d->mSrsId == 0 )
959  {
960  // lastly, try a tolerant match of the created proj object against all user CRSes (allowing differences in parameter order during the comparison)
961  long id = matchToUserCrs();
962  if ( id >= USER_CRS_START_ID )
963  {
964  createFromSrsId( id );
965  }
966  }
967  }
968 
969  locker.changeMode( QgsReadWriteLocker::Write );
970  if ( !sDisableWktCache )
971  sWktCache()->insert( wkt, *this );
972 
973  return d->mIsValid;
974  //setMapunits will be called by createfromproj above
975 }
976 
978 {
979  return d->mIsValid;
980 }
981 
982 bool QgsCoordinateReferenceSystem::createFromProj4( const QString &proj4String )
983 {
984  return createFromProj( proj4String );
985 }
986 
987 bool QgsCoordinateReferenceSystem::createFromProj( const QString &projString, const bool identify )
988 {
989  if ( projString.isEmpty() )
990  return false;
991 
992  d.detach();
993 
994  if ( projString.trimmed().isEmpty() )
995  {
996  d->mIsValid = false;
997  d->mProj4.clear();
998  d->mWktPreferred.clear();
999  return false;
1000  }
1001 
1002  QgsReadWriteLocker locker( *sProj4CacheLock(), QgsReadWriteLocker::Read );
1003  if ( !sDisableProjCache )
1004  {
1005  QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sProj4Cache()->constFind( projString );
1006  if ( crsIt != sProj4Cache()->constEnd() )
1007  {
1008  // found a match in the cache
1009  *this = crsIt.value();
1010  return d->mIsValid;
1011  }
1012  }
1013  locker.unlock();
1014 
1015  //
1016  // Examples:
1017  // +proj=tmerc +lat_0=0 +lon_0=-62 +k=0.999500 +x_0=400000 +y_0=0
1018  // +ellps=clrk80 +towgs84=-255,-15,71,0,0,0,0 +units=m +no_defs
1019  //
1020  // +proj=lcc +lat_1=46.8 +lat_0=46.8 +lon_0=2.337229166666664 +k_0=0.99987742
1021  // +x_0=600000 +y_0=2200000 +a=6378249.2 +b=6356515.000000472 +units=m +no_defs
1022  //
1023  QString myProj4String = projString.trimmed();
1024  myProj4String.remove( QStringLiteral( "+type=crs" ) );
1025  myProj4String = myProj4String.trimmed();
1026 
1027  d->mIsValid = false;
1028  d->mWktPreferred.clear();
1029 
1030  if ( identify )
1031  {
1032  // first, try to use proj to do this for us...
1033  const QString projCrsString = myProj4String + ( myProj4String.contains( QStringLiteral( "+type=crs" ) ) ? QString() : QStringLiteral( " +type=crs" ) );
1034  QgsProjUtils::proj_pj_unique_ptr crs( proj_create( QgsProjContext::get(), projCrsString.toLatin1().constData() ) );
1035  if ( crs )
1036  {
1037  QString authName;
1038  QString authCode;
1040  {
1041  const QString authid = QStringLiteral( "%1:%2" ).arg( authName, authCode );
1042  if ( createFromOgcWmsCrs( authid ) )
1043  {
1045  if ( !sDisableProjCache )
1046  sProj4Cache()->insert( projString, *this );
1047  return d->mIsValid;
1048  }
1049  }
1050  }
1051 
1052  // try a direct match against user crses
1053  QgsCoordinateReferenceSystem::RecordMap myRecord = getRecord( "select * from tbl_srs where parameters=" + QgsSqliteUtils::quotedString( myProj4String ) + " order by deprecated" );
1054  long id = 0;
1055  if ( !myRecord.empty() )
1056  {
1057  id = myRecord[QStringLiteral( "srs_id" )].toLong();
1058  if ( id >= USER_CRS_START_ID )
1059  {
1060  createFromSrsId( id );
1061  }
1062  }
1063  if ( id < USER_CRS_START_ID )
1064  {
1065  // no direct matches, so go ahead and create a new proj object based on the proj string alone.
1066  setProjString( myProj4String );
1067 
1068  // lastly, try a tolerant match of the created proj object against all user CRSes (allowing differences in parameter order during the comparison)
1069  id = matchToUserCrs();
1070  if ( id >= USER_CRS_START_ID )
1071  {
1072  createFromSrsId( id );
1073  }
1074  }
1075  }
1076  else
1077  {
1078  setProjString( myProj4String );
1079  }
1080 
1082  if ( !sDisableProjCache )
1083  sProj4Cache()->insert( projString, *this );
1084 
1085  return d->mIsValid;
1086 }
1087 
1088 //private method meant for internal use by this class only
1089 QgsCoordinateReferenceSystem::RecordMap QgsCoordinateReferenceSystem::getRecord( const QString &sql )
1090 {
1091  QString myDatabaseFileName;
1092  QgsCoordinateReferenceSystem::RecordMap myMap;
1093  QString myFieldName;
1094  QString myFieldValue;
1095  sqlite3_database_unique_ptr database;
1096  sqlite3_statement_unique_ptr statement;
1097  int myResult;
1098 
1099  // Get the full path name to the sqlite3 spatial reference database.
1100  myDatabaseFileName = QgsApplication::srsDatabaseFilePath();
1101  QFileInfo myInfo( myDatabaseFileName );
1102  if ( !myInfo.exists() )
1103  {
1104  QgsDebugMsg( "failed : " + myDatabaseFileName + " does not exist!" );
1105  return myMap;
1106  }
1107 
1108  //check the db is available
1109  myResult = openDatabase( myDatabaseFileName, database );
1110  if ( myResult != SQLITE_OK )
1111  {
1112  return myMap;
1113  }
1114 
1115  statement = database.prepare( sql, myResult );
1116  // XXX Need to free memory from the error msg if one is set
1117  if ( myResult == SQLITE_OK && statement.step() == SQLITE_ROW )
1118  {
1119  int myColumnCount = statement.columnCount();
1120  //loop through each column in the record adding its expression name and value to the map
1121  for ( int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1122  {
1123  myFieldName = statement.columnName( myColNo );
1124  myFieldValue = statement.columnAsText( myColNo );
1125  myMap[myFieldName] = myFieldValue;
1126  }
1127  if ( statement.step() != SQLITE_DONE )
1128  {
1129  QgsDebugMsgLevel( QStringLiteral( "Multiple records found in srs.db" ), 4 );
1130  //be less fussy on proj 6 -- the db has MANY more entries!
1131  }
1132  }
1133  else
1134  {
1135  QgsDebugMsgLevel( "failed : " + sql, 4 );
1136  }
1137 
1138  if ( myMap.empty() )
1139  {
1140  myDatabaseFileName = QgsApplication::qgisUserDatabaseFilePath();
1141  QFileInfo myFileInfo;
1142  myFileInfo.setFile( myDatabaseFileName );
1143  if ( !myFileInfo.exists() )
1144  {
1145  QgsDebugMsg( QStringLiteral( "user qgis.db not found" ) );
1146  return myMap;
1147  }
1148 
1149  //check the db is available
1150  myResult = openDatabase( myDatabaseFileName, database );
1151  if ( myResult != SQLITE_OK )
1152  {
1153  return myMap;
1154  }
1155 
1156  statement = database.prepare( sql, myResult );
1157  // XXX Need to free memory from the error msg if one is set
1158  if ( myResult == SQLITE_OK && statement.step() == SQLITE_ROW )
1159  {
1160  int myColumnCount = statement.columnCount();
1161  //loop through each column in the record adding its field name and value to the map
1162  for ( int myColNo = 0; myColNo < myColumnCount; myColNo++ )
1163  {
1164  myFieldName = statement.columnName( myColNo );
1165  myFieldValue = statement.columnAsText( myColNo );
1166  myMap[myFieldName] = myFieldValue;
1167  }
1168 
1169  if ( statement.step() != SQLITE_DONE )
1170  {
1171  QgsDebugMsgLevel( QStringLiteral( "Multiple records found in srs.db" ), 4 );
1172  myMap.clear();
1173  }
1174  }
1175  else
1176  {
1177  QgsDebugMsgLevel( "failed : " + sql, 4 );
1178  }
1179  }
1180  return myMap;
1181 }
1182 
1183 // Accessors -----------------------------------
1184 
1186 {
1187  return d->mSrsId;
1188 }
1189 
1191 {
1192  return d->mSRID;
1193 }
1194 
1196 {
1197  return d->mAuthId;
1198 }
1199 
1201 {
1202  if ( d->mDescription.isNull() )
1203  {
1204  return QString();
1205  }
1206  else
1207  {
1208  return d->mDescription;
1209  }
1210 }
1211 
1213 {
1214  QString id;
1215  if ( !authid().isEmpty() )
1216  {
1217  if ( type != ShortString && !description().isEmpty() )
1218  id = QStringLiteral( "%1 - %2" ).arg( authid(), description() );
1219  else
1220  id = authid();
1221  }
1222  else if ( !description().isEmpty() )
1223  id = description();
1224  else if ( type == ShortString )
1225  id = isValid() ? QObject::tr( "Custom CRS" ) : QObject::tr( "Unknown CRS" );
1226  else if ( !toWkt( WKT_PREFERRED ).isEmpty() )
1227  id = QObject::tr( "Custom CRS: %1" ).arg(
1228  type == MediumString ? ( toWkt( WKT_PREFERRED ).left( 50 ) + QString( QChar( 0x2026 ) ) )
1229  : toWkt( WKT_PREFERRED ) );
1230  else if ( !toProj().isEmpty() )
1231  id = QObject::tr( "Custom CRS: %1" ).arg( type == MediumString ? ( toProj().left( 50 ) + QString( QChar( 0x2026 ) ) )
1232  : toProj() );
1233  if ( !id.isEmpty() && !std::isnan( d->mCoordinateEpoch ) )
1234  id += QStringLiteral( " @ %1" ).arg( d->mCoordinateEpoch );
1235 
1236  return id;
1237 }
1238 
1240 {
1241  if ( d->mProjectionAcronym.isNull() )
1242  {
1243  return QString();
1244  }
1245  else
1246  {
1247  return d->mProjectionAcronym;
1248  }
1249 }
1250 
1252 {
1253  if ( d->mEllipsoidAcronym.isNull() )
1254  {
1255  if ( PJ *obj = d->threadLocalProjObject() )
1256  {
1257  QgsProjUtils::proj_pj_unique_ptr ellipsoid( proj_get_ellipsoid( QgsProjContext::get(), obj ) );
1258  if ( ellipsoid )
1259  {
1260  const QString ellipsoidAuthName( proj_get_id_auth_name( ellipsoid.get(), 0 ) );
1261  const QString ellipsoidAuthCode( proj_get_id_code( ellipsoid.get(), 0 ) );
1262  if ( !ellipsoidAuthName.isEmpty() && !ellipsoidAuthCode.isEmpty() )
1263  d->mEllipsoidAcronym = QStringLiteral( "%1:%2" ).arg( ellipsoidAuthName, ellipsoidAuthCode );
1264  else
1265  {
1266  double semiMajor, semiMinor, invFlattening;
1267  int semiMinorComputed = 0;
1268  if ( proj_ellipsoid_get_parameters( QgsProjContext::get(), ellipsoid.get(), &semiMajor, &semiMinor, &semiMinorComputed, &invFlattening ) )
1269  {
1270  d->mEllipsoidAcronym = QStringLiteral( "PARAMETER:%1:%2" ).arg( qgsDoubleToString( semiMajor ),
1271  qgsDoubleToString( semiMinor ) );
1272  }
1273  else
1274  {
1275  d->mEllipsoidAcronym.clear();
1276  }
1277  }
1278  }
1279  }
1280  return d->mEllipsoidAcronym;
1281  }
1282  else
1283  {
1284  return d->mEllipsoidAcronym;
1285  }
1286 }
1287 
1289 {
1290  return toProj();
1291 }
1292 
1294 {
1295  if ( !d->mIsValid )
1296  return QString();
1297 
1298  if ( d->mProj4.isEmpty() )
1299  {
1300  if ( PJ *obj = d->threadLocalProjObject() )
1301  {
1302  d->mProj4 = getFullProjString( obj );
1303  }
1304  }
1305  // Stray spaces at the end?
1306  return d->mProj4.trimmed();
1307 }
1308 
1310 {
1311  return d->mIsGeographic;
1312 }
1313 
1315 {
1316  const PJ *pj = projObject();
1317  if ( !pj )
1318  return false;
1319 
1320  return QgsProjUtils::isDynamic( pj );
1321 }
1322 
1324 {
1325  const PJ *pj = projObject();
1326  if ( !pj )
1327  return QString();
1328 
1329 #if PROJ_VERSION_MAJOR>8 || (PROJ_VERSION_MAJOR==8 && PROJ_VERSION_MINOR>=1)
1330  PJ_CONTEXT *context = QgsProjContext::get();
1331 
1332  return QString( proj_get_celestial_body_name( context, pj ) );
1333 #else
1334  throw QgsNotSupportedException( QStringLiteral( "Retrieving celestial body requires a QGIS build based on PROJ 8.1 or later" ) );
1335 #endif
1336 }
1337 
1339 {
1340  if ( d->mCoordinateEpoch == epoch )
1341  return;
1342 
1343  // detaching clears the proj object, so we need to clone the existing one first
1345  d.detach();
1346  d->mCoordinateEpoch = epoch;
1347  d->setPj( std::move( clone ) );
1348 }
1349 
1351 {
1352  return d->mCoordinateEpoch;
1353 }
1354 
1356 {
1357  QgsDatumEnsemble res;
1358  res.mValid = false;
1359 
1360  const PJ *pj = projObject();
1361  if ( !pj )
1362  return res;
1363 
1364 #if PROJ_VERSION_MAJOR>=8
1365  PJ_CONTEXT *context = QgsProjContext::get();
1366 
1368  if ( !ensemble )
1369  return res;
1370 
1371  res.mValid = true;
1372  res.mName = QString( proj_get_name( ensemble.get() ) );
1373  res.mAuthority = QString( proj_get_id_auth_name( ensemble.get(), 0 ) );
1374  res.mCode = QString( proj_get_id_code( ensemble.get(), 0 ) );
1375  res.mRemarks = QString( proj_get_remarks( ensemble.get() ) );
1376  res.mScope = QString( proj_get_scope( ensemble.get() ) );
1377  res.mAccuracy = proj_datum_ensemble_get_accuracy( context, ensemble.get() );
1378 
1379  const int memberCount = proj_datum_ensemble_get_member_count( context, ensemble.get() );
1380  for ( int i = 0; i < memberCount; ++i )
1381  {
1382  QgsProjUtils::proj_pj_unique_ptr member( proj_datum_ensemble_get_member( context, ensemble.get(), i ) );
1383  if ( !member )
1384  continue;
1385 
1386  QgsDatumEnsembleMember details;
1387  details.mName = QString( proj_get_name( member.get() ) );
1388  details.mAuthority = QString( proj_get_id_auth_name( member.get(), 0 ) );
1389  details.mCode = QString( proj_get_id_code( member.get(), 0 ) );
1390  details.mRemarks = QString( proj_get_remarks( member.get() ) );
1391  details.mScope = QString( proj_get_scope( member.get() ) );
1392 
1393  res.mMembers << details;
1394  }
1395  return res;
1396 #else
1397  throw QgsNotSupportedException( QStringLiteral( "Calculating datum ensembles requires a QGIS build based on PROJ 8.0 or later" ) );
1398 #endif
1399 }
1400 
1402 {
1404 
1405  // we have to make a transformation object corresponding to the crs
1406  QString projString = toProj();
1407  projString.replace( QLatin1String( "+type=crs" ), QString() );
1408 
1409  QgsProjUtils::proj_pj_unique_ptr transformation( proj_create( QgsProjContext::get(), projString.toUtf8().constData() ) );
1410  if ( !transformation )
1411  return res;
1412 
1413  PJ_COORD coord = proj_coord( 0, 0, 0, HUGE_VAL );
1414  coord.uv.u = point.x() * M_PI / 180.0;
1415  coord.uv.v = point.y() * M_PI / 180.0;
1416 
1417  proj_errno_reset( transformation.get() );
1418  const PJ_FACTORS pjFactors = proj_factors( transformation.get(), coord );
1419  if ( proj_errno( transformation.get() ) )
1420  {
1421  return res;
1422  }
1423 
1424  res.mIsValid = true;
1425  res.mMeridionalScale = pjFactors.meridional_scale;
1426  res.mParallelScale = pjFactors.parallel_scale;
1427  res.mArealScale = pjFactors.areal_scale;
1428  res.mAngularDistortion = pjFactors.angular_distortion;
1429  res.mMeridianParallelAngle = pjFactors.meridian_parallel_angle * 180 / M_PI;
1430  res.mMeridianConvergence = pjFactors.meridian_convergence * 180 / M_PI;
1431  res.mTissotSemimajor = pjFactors.tissot_semimajor;
1432  res.mTissotSemiminor = pjFactors.tissot_semiminor;
1433  res.mDxDlam = pjFactors.dx_dlam;
1434  res.mDxDphi = pjFactors.dx_dphi;
1435  res.mDyDlam = pjFactors.dy_dlam;
1436  res.mDyDphi = pjFactors.dy_dphi;
1437  return res;
1438 }
1439 
1441 {
1442  QgsProjOperation res;
1443 
1444  // we have to make a transformation object corresponding to the crs
1445  QString projString = toProj();
1446  projString.replace( QLatin1String( "+type=crs" ), QString() );
1447 
1448  QgsProjUtils::proj_pj_unique_ptr transformation( proj_create( QgsProjContext::get(), projString.toUtf8().constData() ) );
1449  if ( !transformation )
1450  return res;
1451 
1452  PJ_PROJ_INFO info = proj_pj_info( transformation.get() );
1453 
1454  if ( info.id )
1455  {
1456  return QgsApplication::coordinateReferenceSystemRegistry()->projOperations().value( QString( info.id ) );
1457  }
1458 
1459  return res;
1460 }
1461 
1463 {
1464  if ( !d->mIsValid )
1466 
1467  return d->mMapUnits;
1468 }
1469 
1471 {
1472  if ( !d->mIsValid )
1473  return QgsRectangle();
1474 
1475  PJ *obj = d->threadLocalProjObject();
1476  if ( !obj )
1477  return QgsRectangle();
1478 
1479  double westLon = 0;
1480  double southLat = 0;
1481  double eastLon = 0;
1482  double northLat = 0;
1483 
1484  if ( !proj_get_area_of_use( QgsProjContext::get(), obj,
1485  &westLon, &southLat, &eastLon, &northLat, nullptr ) )
1486  return QgsRectangle();
1487 
1488 
1489  // don't use the constructor which normalizes!
1490  QgsRectangle rect;
1491  rect.setXMinimum( westLon );
1492  rect.setYMinimum( southLat );
1493  rect.setXMaximum( eastLon );
1494  rect.setYMaximum( northLat );
1495  return rect;
1496 }
1497 
1499 {
1500  if ( !d->mIsValid )
1501  return;
1502 
1503  if ( d->mSrsId >= USER_CRS_START_ID )
1504  {
1505  // user CRS, so update to new definition
1506  createFromSrsId( d->mSrsId );
1507  }
1508  else
1509  {
1510  // nothing to do -- only user CRS definitions can be changed
1511  }
1512 }
1513 
1514 void QgsCoordinateReferenceSystem::setProjString( const QString &proj4String )
1515 {
1516  d.detach();
1517  d->mProj4 = proj4String;
1518  d->mWktPreferred.clear();
1519 
1520  QgsLocaleNumC l;
1521  QString trimmed = proj4String.trimmed();
1522 
1523  trimmed += QLatin1String( " +type=crs" );
1525 
1526  {
1527  d->setPj( QgsProjUtils::proj_pj_unique_ptr( proj_create( ctx, trimmed.toLatin1().constData() ) ) );
1528  }
1529 
1530  if ( !d->hasPj() )
1531  {
1532 #ifdef QGISDEBUG
1533  const int errNo = proj_context_errno( ctx );
1534  QgsDebugMsg( QStringLiteral( "proj string rejected: %1" ).arg( proj_errno_string( errNo ) ) );
1535 #endif
1536  d->mIsValid = false;
1537  }
1538  else
1539  {
1540  d->mEllipsoidAcronym.clear();
1541  d->mIsValid = true;
1542  }
1543 
1544  setMapUnits();
1545 }
1546 
1547 bool QgsCoordinateReferenceSystem::setWktString( const QString &wkt )
1548 {
1549  bool res = false;
1550  d->mIsValid = false;
1551  d->mWktPreferred.clear();
1552 
1553  PROJ_STRING_LIST warnings = nullptr;
1554  PROJ_STRING_LIST grammerErrors = nullptr;
1555  {
1556  d->setPj( QgsProjUtils::proj_pj_unique_ptr( proj_create_from_wkt( QgsProjContext::get(), wkt.toLatin1().constData(), nullptr, &warnings, &grammerErrors ) ) );
1557  }
1558 
1559  res = d->hasPj();
1560  if ( !res )
1561  {
1562  QgsDebugMsg( QStringLiteral( "\n---------------------------------------------------------------" ) );
1563  QgsDebugMsg( QStringLiteral( "This CRS could *** NOT *** be set from the supplied Wkt " ) );
1564  QgsDebugMsg( "INPUT: " + wkt );
1565  for ( auto iter = warnings; iter && *iter; ++iter )
1566  QgsDebugMsg( *iter );
1567  for ( auto iter = grammerErrors; iter && *iter; ++iter )
1568  QgsDebugMsg( *iter );
1569  QgsDebugMsg( QStringLiteral( "---------------------------------------------------------------\n" ) );
1570  }
1571  proj_string_list_destroy( warnings );
1572  proj_string_list_destroy( grammerErrors );
1573 
1574  QgsReadWriteLocker locker( *sProj4CacheLock(), QgsReadWriteLocker::Unlocked );
1575  if ( !res )
1576  {
1577  locker.changeMode( QgsReadWriteLocker::Write );
1578  if ( !sDisableWktCache )
1579  sWktCache()->insert( wkt, *this );
1580  return d->mIsValid;
1581  }
1582 
1583  if ( d->hasPj() )
1584  {
1585  // try 1 - maybe we can directly grab the auth name and code from the crs already?
1586  QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
1587  QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
1588 
1589  if ( authName.isEmpty() || authCode.isEmpty() )
1590  {
1591  // try 2, use proj's identify method and see if there's a nice candidate we can use
1592  QgsProjUtils::identifyCrs( d->threadLocalProjObject(), authName, authCode );
1593  }
1594 
1595  if ( !authName.isEmpty() && !authCode.isEmpty() )
1596  {
1597  if ( loadFromAuthCode( authName, authCode ) )
1598  {
1599  locker.changeMode( QgsReadWriteLocker::Write );
1600  if ( !sDisableWktCache )
1601  sWktCache()->insert( wkt, *this );
1602  return d->mIsValid;
1603  }
1604  }
1605  else
1606  {
1607  // Still a valid CRS, just not a known one
1608  d->mIsValid = true;
1609  d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
1610  }
1611  setMapUnits();
1612  }
1613 
1614  return d->mIsValid;
1615 }
1616 
1617 void QgsCoordinateReferenceSystem::setMapUnits()
1618 {
1619  if ( !d->mIsValid )
1620  {
1621  d->mMapUnits = QgsUnitTypes::DistanceUnknownUnit;
1622  return;
1623  }
1624 
1625  if ( !d->hasPj() )
1626  {
1627  d->mMapUnits = QgsUnitTypes::DistanceUnknownUnit;
1628  return;
1629  }
1630 
1631  PJ_CONTEXT *context = QgsProjContext::get();
1632  QgsProjUtils::proj_pj_unique_ptr crs( QgsProjUtils::crsToSingleCrs( d->threadLocalProjObject() ) );
1633  QgsProjUtils::proj_pj_unique_ptr coordinateSystem( proj_crs_get_coordinate_system( context, crs.get() ) );
1634  if ( !coordinateSystem )
1635  {
1636  d->mMapUnits = QgsUnitTypes::DistanceUnknownUnit;
1637  return;
1638  }
1639 
1640  const int axisCount = proj_cs_get_axis_count( context, coordinateSystem.get() );
1641  if ( axisCount > 0 )
1642  {
1643  const char *outUnitName = nullptr;
1644  // Read only first axis
1645  proj_cs_get_axis_info( context, coordinateSystem.get(), 0,
1646  nullptr,
1647  nullptr,
1648  nullptr,
1649  nullptr,
1650  &outUnitName,
1651  nullptr,
1652  nullptr );
1653 
1654  const QString unitName( outUnitName );
1655 
1656  // proj unit names are freeform -- they differ from authority to authority :(
1657  // see https://lists.osgeo.org/pipermail/proj/2019-April/008444.html
1658  if ( unitName.compare( QLatin1String( "degree" ), Qt::CaseInsensitive ) == 0 ||
1659  unitName.compare( QLatin1String( "degree minute second" ), Qt::CaseInsensitive ) == 0 ||
1660  unitName.compare( QLatin1String( "degree minute second hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1661  unitName.compare( QLatin1String( "degree minute" ), Qt::CaseInsensitive ) == 0 ||
1662  unitName.compare( QLatin1String( "degree hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1663  unitName.compare( QLatin1String( "degree minute hemisphere" ), Qt::CaseInsensitive ) == 0 ||
1664  unitName.compare( QLatin1String( "hemisphere degree" ), Qt::CaseInsensitive ) == 0 ||
1665  unitName.compare( QLatin1String( "hemisphere degree minute" ), Qt::CaseInsensitive ) == 0 ||
1666  unitName.compare( QLatin1String( "hemisphere degree minute second" ), Qt::CaseInsensitive ) == 0 ||
1667  unitName.compare( QLatin1String( "degree (supplier to define representation)" ), Qt::CaseInsensitive ) == 0 )
1668  d->mMapUnits = QgsUnitTypes::DistanceDegrees;
1669  else if ( unitName.compare( QLatin1String( "metre" ), Qt::CaseInsensitive ) == 0
1670  || unitName.compare( QLatin1String( "m" ), Qt::CaseInsensitive ) == 0
1671  || unitName.compare( QLatin1String( "meter" ), Qt::CaseInsensitive ) == 0 )
1672  d->mMapUnits = QgsUnitTypes::DistanceMeters;
1673  // we don't differentiate between these, suck it imperial users!
1674  else if ( unitName.compare( QLatin1String( "US survey foot" ), Qt::CaseInsensitive ) == 0 ||
1675  unitName.compare( QLatin1String( "foot" ), Qt::CaseInsensitive ) == 0 )
1676  d->mMapUnits = QgsUnitTypes::DistanceFeet;
1677  else if ( unitName.compare( QLatin1String( "kilometre" ), Qt::CaseInsensitive ) == 0 ) //#spellok
1678  d->mMapUnits = QgsUnitTypes::DistanceKilometers;
1679  else if ( unitName.compare( QLatin1String( "centimetre" ), Qt::CaseInsensitive ) == 0 ) //#spellok
1680  d->mMapUnits = QgsUnitTypes::DistanceCentimeters;
1681  else if ( unitName.compare( QLatin1String( "millimetre" ), Qt::CaseInsensitive ) == 0 ) //#spellok
1682  d->mMapUnits = QgsUnitTypes::DistanceMillimeters;
1683  else if ( unitName.compare( QLatin1String( "Statute mile" ), Qt::CaseInsensitive ) == 0 )
1684  d->mMapUnits = QgsUnitTypes::DistanceMiles;
1685  else if ( unitName.compare( QLatin1String( "nautical mile" ), Qt::CaseInsensitive ) == 0 )
1686  d->mMapUnits = QgsUnitTypes::DistanceNauticalMiles;
1687  else if ( unitName.compare( QLatin1String( "yard" ), Qt::CaseInsensitive ) == 0 )
1688  d->mMapUnits = QgsUnitTypes::DistanceYards;
1689  // TODO - maybe more values to handle here?
1690  else
1691  d->mMapUnits = QgsUnitTypes::DistanceUnknownUnit;
1692  return;
1693  }
1694  else
1695  {
1696  d->mMapUnits = QgsUnitTypes::DistanceUnknownUnit;
1697  return;
1698  }
1699 }
1700 
1701 
1703 {
1704  if ( d->mEllipsoidAcronym.isNull() || d->mProjectionAcronym.isNull()
1705  || !d->mIsValid )
1706  {
1707  QgsDebugMsgLevel( "QgsCoordinateReferenceSystem::findMatchingProj will only "
1708  "work if prj acr ellipsoid acr and proj4string are set"
1709  " and the current projection is valid!", 4 );
1710  return 0;
1711  }
1712 
1713  sqlite3_database_unique_ptr database;
1714  sqlite3_statement_unique_ptr statement;
1715  int myResult;
1716 
1717  // Set up the query to retrieve the projection information
1718  // needed to populate the list
1719  QString mySql = QString( "select srs_id,parameters from tbl_srs where "
1720  "projection_acronym=%1 and ellipsoid_acronym=%2 order by deprecated" )
1721  .arg( QgsSqliteUtils::quotedString( d->mProjectionAcronym ),
1722  QgsSqliteUtils::quotedString( d->mEllipsoidAcronym ) );
1723  // Get the full path name to the sqlite3 spatial reference database.
1724  QString myDatabaseFileName = QgsApplication::srsDatabaseFilePath();
1725 
1726  //check the db is available
1727  myResult = openDatabase( myDatabaseFileName, database );
1728  if ( myResult != SQLITE_OK )
1729  {
1730  return 0;
1731  }
1732 
1733  statement = database.prepare( mySql, myResult );
1734  if ( myResult == SQLITE_OK )
1735  {
1736 
1737  while ( statement.step() == SQLITE_ROW )
1738  {
1739  QString mySrsId = statement.columnAsText( 0 );
1740  QString myProj4String = statement.columnAsText( 1 );
1741  if ( toProj() == myProj4String.trimmed() )
1742  {
1743  return mySrsId.toLong();
1744  }
1745  }
1746  }
1747 
1748  //
1749  // Try the users db now
1750  //
1751 
1752  myDatabaseFileName = QgsApplication::qgisUserDatabaseFilePath();
1753  //check the db is available
1754  myResult = openDatabase( myDatabaseFileName, database );
1755  if ( myResult != SQLITE_OK )
1756  {
1757  return 0;
1758  }
1759 
1760  statement = database.prepare( mySql, myResult );
1761 
1762  if ( myResult == SQLITE_OK )
1763  {
1764  while ( statement.step() == SQLITE_ROW )
1765  {
1766  QString mySrsId = statement.columnAsText( 0 );
1767  QString myProj4String = statement.columnAsText( 1 );
1768  if ( toProj() == myProj4String.trimmed() )
1769  {
1770  return mySrsId.toLong();
1771  }
1772  }
1773  }
1774 
1775  return 0;
1776 }
1777 
1779 {
1780  // shortcut
1781  if ( d == srs.d )
1782  return true;
1783 
1784  if ( !d->mIsValid && !srs.d->mIsValid )
1785  return true;
1786 
1787  if ( !d->mIsValid || !srs.d->mIsValid )
1788  return false;
1789 
1790  if ( !qgsNanCompatibleEquals( d->mCoordinateEpoch, srs.d->mCoordinateEpoch ) )
1791  return false;
1792 
1793  const bool isUser = d->mSrsId >= USER_CRS_START_ID;
1794  const bool otherIsUser = srs.d->mSrsId >= USER_CRS_START_ID;
1795  if ( isUser != otherIsUser )
1796  return false;
1797 
1798  // we can't directly compare authid for user crses -- the actual definition of these may have changed
1799  if ( !isUser && ( !d->mAuthId.isEmpty() || !srs.d->mAuthId.isEmpty() ) )
1800  return d->mAuthId == srs.d->mAuthId;
1801 
1802  return toWkt( WKT_PREFERRED ) == srs.toWkt( WKT_PREFERRED );
1803 }
1804 
1806 {
1807  return !( *this == srs );
1808 }
1809 
1810 QString QgsCoordinateReferenceSystem::toWkt( WktVariant variant, bool multiline, int indentationWidth ) const
1811 {
1812  if ( PJ *obj = d->threadLocalProjObject() )
1813  {
1814  const bool isDefaultPreferredFormat = variant == WKT_PREFERRED && !multiline;
1815  if ( isDefaultPreferredFormat && !d->mWktPreferred.isEmpty() )
1816  {
1817  // can use cached value
1818  return d->mWktPreferred;
1819  }
1820 
1821  PJ_WKT_TYPE type = PJ_WKT1_GDAL;
1822  switch ( variant )
1823  {
1824  case WKT1_GDAL:
1825  type = PJ_WKT1_GDAL;
1826  break;
1827  case WKT1_ESRI:
1828  type = PJ_WKT1_ESRI;
1829  break;
1830  case WKT2_2015:
1831  type = PJ_WKT2_2015;
1832  break;
1833  case WKT2_2015_SIMPLIFIED:
1834  type = PJ_WKT2_2015_SIMPLIFIED;
1835  break;
1836  case WKT2_2019:
1837  type = PJ_WKT2_2019;
1838  break;
1839  case WKT2_2019_SIMPLIFIED:
1840  type = PJ_WKT2_2019_SIMPLIFIED;
1841  break;
1842  }
1843 
1844  const QByteArray multiLineOption = QStringLiteral( "MULTILINE=%1" ).arg( multiline ? QStringLiteral( "YES" ) : QStringLiteral( "NO" ) ).toLocal8Bit();
1845  const QByteArray indentatationWidthOption = QStringLiteral( "INDENTATION_WIDTH=%1" ).arg( multiline ? QString::number( indentationWidth ) : QStringLiteral( "0" ) ).toLocal8Bit();
1846  const char *const options[] = {multiLineOption.constData(), indentatationWidthOption.constData(), nullptr};
1847  QString res = proj_as_wkt( QgsProjContext::get(), obj, type, options );
1848 
1849  if ( isDefaultPreferredFormat )
1850  {
1851  // cache result for later use
1852  d->mWktPreferred = res;
1853  }
1854 
1855  return res;
1856  }
1857  return QString();
1858 }
1859 
1860 bool QgsCoordinateReferenceSystem::readXml( const QDomNode &node )
1861 {
1862  d.detach();
1863  bool result = true;
1864  QDomNode srsNode = node.namedItem( QStringLiteral( "spatialrefsys" ) );
1865 
1866  if ( ! srsNode.isNull() )
1867  {
1868  bool initialized = false;
1869 
1870  bool ok = false;
1871  long srsid = srsNode.namedItem( QStringLiteral( "srsid" ) ).toElement().text().toLong( &ok );
1872 
1873  QDomNode node;
1874 
1875  if ( ok && srsid > 0 && srsid < USER_CRS_START_ID )
1876  {
1877  node = srsNode.namedItem( QStringLiteral( "authid" ) );
1878  if ( !node.isNull() )
1879  {
1880  createFromOgcWmsCrs( node.toElement().text() );
1881  if ( isValid() )
1882  {
1883  initialized = true;
1884  }
1885  }
1886 
1887  if ( !initialized )
1888  {
1889  node = srsNode.namedItem( QStringLiteral( "epsg" ) );
1890  if ( !node.isNull() )
1891  {
1892  operator=( QgsCoordinateReferenceSystem::fromEpsgId( node.toElement().text().toLong() ) );
1893  if ( isValid() )
1894  {
1895  initialized = true;
1896  }
1897  }
1898  }
1899  }
1900 
1901  // if wkt is present, prefer that since it's lossless (unlike proj4 strings)
1902  if ( !initialized )
1903  {
1904  // before doing anything, we grab and set the stored CRS name (description).
1905  // this way if the stored CRS doesn't match anything available locally (i.e. from Proj's db
1906  // or the user's custom CRS list), then we will correctly show the CRS with its original
1907  // name (instead of just "custom crs")
1908  const QString description = srsNode.namedItem( QStringLiteral( "description" ) ).toElement().text();
1909 
1910  const QString wkt = srsNode.namedItem( QStringLiteral( "wkt" ) ).toElement().text();
1911  initialized = createFromWktInternal( wkt, description );
1912  }
1913 
1914  if ( !initialized )
1915  {
1916  node = srsNode.namedItem( QStringLiteral( "proj4" ) );
1917  const QString proj4 = node.toElement().text();
1918  initialized = createFromProj( proj4 );
1919  }
1920 
1921  if ( !initialized )
1922  {
1923  // Setting from elements one by one
1924  node = srsNode.namedItem( QStringLiteral( "proj4" ) );
1925  const QString proj4 = node.toElement().text();
1926  if ( !proj4.trimmed().isEmpty() )
1927  setProjString( node.toElement().text() );
1928 
1929  node = srsNode.namedItem( QStringLiteral( "srsid" ) );
1930  d->mSrsId = node.toElement().text().toLong();
1931 
1932  node = srsNode.namedItem( QStringLiteral( "srid" ) );
1933  d->mSRID = node.toElement().text().toLong();
1934 
1935  node = srsNode.namedItem( QStringLiteral( "authid" ) );
1936  d->mAuthId = node.toElement().text();
1937 
1938  node = srsNode.namedItem( QStringLiteral( "description" ) );
1939  d->mDescription = node.toElement().text();
1940 
1941  node = srsNode.namedItem( QStringLiteral( "projectionacronym" ) );
1942  d->mProjectionAcronym = node.toElement().text();
1943 
1944  node = srsNode.namedItem( QStringLiteral( "ellipsoidacronym" ) );
1945  d->mEllipsoidAcronym = node.toElement().text();
1946 
1947  node = srsNode.namedItem( QStringLiteral( "geographicflag" ) );
1948  d->mIsGeographic = node.toElement().text() == QLatin1String( "true" );
1949 
1950  d->mWktPreferred.clear();
1951 
1952  //make sure the map units have been set
1953  setMapUnits();
1954  }
1955 
1956  const QString epoch = srsNode.toElement().attribute( QStringLiteral( "coordinateEpoch" ) );
1957  if ( !epoch.isEmpty() )
1958  {
1959  bool epochOk = false;
1960  d->mCoordinateEpoch = epoch.toDouble( &epochOk );
1961  if ( !epochOk )
1962  d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
1963  }
1964  else
1965  {
1966  d->mCoordinateEpoch = std::numeric_limits< double >::quiet_NaN();
1967  }
1968 
1969  mNativeFormat = qgsEnumKeyToValue<Qgis::CrsDefinitionFormat>( srsNode.toElement().attribute( QStringLiteral( "nativeFormat" ) ), Qgis::CrsDefinitionFormat::Wkt );
1970  }
1971  else
1972  {
1973  // Return empty CRS if none was found in the XML.
1974  d = new QgsCoordinateReferenceSystemPrivate();
1975  result = false;
1976  }
1977  return result;
1978 }
1979 
1980 bool QgsCoordinateReferenceSystem::writeXml( QDomNode &node, QDomDocument &doc ) const
1981 {
1982  QDomElement layerNode = node.toElement();
1983  QDomElement srsElement = doc.createElement( QStringLiteral( "spatialrefsys" ) );
1984 
1985  srsElement.setAttribute( QStringLiteral( "nativeFormat" ), qgsEnumValueToKey<Qgis::CrsDefinitionFormat>( mNativeFormat ) );
1986 
1987  if ( std::isfinite( d->mCoordinateEpoch ) )
1988  {
1989  srsElement.setAttribute( QStringLiteral( "coordinateEpoch" ), d->mCoordinateEpoch );
1990  }
1991 
1992  QDomElement wktElement = doc.createElement( QStringLiteral( "wkt" ) );
1993  wktElement.appendChild( doc.createTextNode( toWkt( WKT_PREFERRED ) ) );
1994  srsElement.appendChild( wktElement );
1995 
1996  QDomElement proj4Element = doc.createElement( QStringLiteral( "proj4" ) );
1997  proj4Element.appendChild( doc.createTextNode( toProj() ) );
1998  srsElement.appendChild( proj4Element );
1999 
2000  QDomElement srsIdElement = doc.createElement( QStringLiteral( "srsid" ) );
2001  srsIdElement.appendChild( doc.createTextNode( QString::number( srsid() ) ) );
2002  srsElement.appendChild( srsIdElement );
2003 
2004  QDomElement sridElement = doc.createElement( QStringLiteral( "srid" ) );
2005  sridElement.appendChild( doc.createTextNode( QString::number( postgisSrid() ) ) );
2006  srsElement.appendChild( sridElement );
2007 
2008  QDomElement authidElement = doc.createElement( QStringLiteral( "authid" ) );
2009  authidElement.appendChild( doc.createTextNode( authid() ) );
2010  srsElement.appendChild( authidElement );
2011 
2012  QDomElement descriptionElement = doc.createElement( QStringLiteral( "description" ) );
2013  descriptionElement.appendChild( doc.createTextNode( description() ) );
2014  srsElement.appendChild( descriptionElement );
2015 
2016  QDomElement projectionAcronymElement = doc.createElement( QStringLiteral( "projectionacronym" ) );
2017  projectionAcronymElement.appendChild( doc.createTextNode( projectionAcronym() ) );
2018  srsElement.appendChild( projectionAcronymElement );
2019 
2020  QDomElement ellipsoidAcronymElement = doc.createElement( QStringLiteral( "ellipsoidacronym" ) );
2021  ellipsoidAcronymElement.appendChild( doc.createTextNode( ellipsoidAcronym() ) );
2022  srsElement.appendChild( ellipsoidAcronymElement );
2023 
2024  QDomElement geographicFlagElement = doc.createElement( QStringLiteral( "geographicflag" ) );
2025  QString geoFlagText = QStringLiteral( "false" );
2026  if ( isGeographic() )
2027  {
2028  geoFlagText = QStringLiteral( "true" );
2029  }
2030 
2031  geographicFlagElement.appendChild( doc.createTextNode( geoFlagText ) );
2032  srsElement.appendChild( geographicFlagElement );
2033 
2034  layerNode.appendChild( srsElement );
2035 
2036  return true;
2037 }
2038 
2039 //
2040 // Static helper methods below this point only please!
2041 //
2042 
2043 
2044 // Returns the whole proj4 string for the selected srsid
2045 //this is a static method! NOTE I've made it private for now to reduce API clutter TS
2046 QString QgsCoordinateReferenceSystem::projFromSrsId( const int srsId )
2047 {
2048  QString myDatabaseFileName;
2049  QString myProjString;
2050  QString mySql = QStringLiteral( "select parameters from tbl_srs where srs_id = %1 order by deprecated" ).arg( srsId );
2051 
2052  //
2053  // Determine if this is a user projection or a system on
2054  // user projection defs all have srs_id >= 100000
2055  //
2056  if ( srsId >= USER_CRS_START_ID )
2057  {
2058  myDatabaseFileName = QgsApplication::qgisUserDatabaseFilePath();
2059  QFileInfo myFileInfo;
2060  myFileInfo.setFile( myDatabaseFileName );
2061  if ( !myFileInfo.exists() ) //its unlikely that this condition will ever be reached
2062  {
2063  QgsDebugMsg( QStringLiteral( "users qgis.db not found" ) );
2064  return QString();
2065  }
2066  }
2067  else //must be a system projection then
2068  {
2069  myDatabaseFileName = QgsApplication::srsDatabaseFilePath();
2070  }
2071 
2072  sqlite3_database_unique_ptr database;
2073  sqlite3_statement_unique_ptr statement;
2074 
2075  int rc;
2076  rc = openDatabase( myDatabaseFileName, database );
2077  if ( rc )
2078  {
2079  return QString();
2080  }
2081 
2082  statement = database.prepare( mySql, rc );
2083 
2084  if ( rc == SQLITE_OK )
2085  {
2086  if ( statement.step() == SQLITE_ROW )
2087  {
2088  myProjString = statement.columnAsText( 0 );
2089  }
2090  }
2091 
2092  return myProjString;
2093 }
2094 
2095 int QgsCoordinateReferenceSystem::openDatabase( const QString &path, sqlite3_database_unique_ptr &database, bool readonly )
2096 {
2097  int myResult;
2098  if ( readonly )
2099  myResult = database.open_v2( path, SQLITE_OPEN_READONLY, nullptr );
2100  else
2101  myResult = database.open( path );
2102 
2103  if ( myResult != SQLITE_OK )
2104  {
2105  QgsDebugMsg( "Can't open database: " + database.errorMessage() );
2106  // XXX This will likely never happen since on open, sqlite creates the
2107  // database if it does not exist.
2108  // ... unfortunately it happens on Windows
2109  QgsMessageLog::logMessage( QObject::tr( "Could not open CRS database %1\nError(%2): %3" )
2110  .arg( path )
2111  .arg( myResult )
2112  .arg( database.errorMessage() ), QObject::tr( "CRS" ) );
2113  }
2114  return myResult;
2115 }
2116 
2118 {
2119  sCustomSrsValidation = f;
2120 }
2121 
2123 {
2124  return sCustomSrsValidation;
2125 }
2126 
2127 void QgsCoordinateReferenceSystem::debugPrint()
2128 {
2129  QgsDebugMsg( QStringLiteral( "***SpatialRefSystem***" ) );
2130  QgsDebugMsg( "* Valid : " + ( d->mIsValid ? QString( "true" ) : QString( "false" ) ) );
2131  QgsDebugMsg( "* SrsId : " + QString::number( d->mSrsId ) );
2132  QgsDebugMsg( "* Proj4 : " + toProj() );
2133  QgsDebugMsg( "* WKT : " + toWkt( WKT_PREFERRED ) );
2134  QgsDebugMsg( "* Desc. : " + d->mDescription );
2136  {
2137  QgsDebugMsg( QStringLiteral( "* Units : meters" ) );
2138  }
2139  else if ( mapUnits() == QgsUnitTypes::DistanceFeet )
2140  {
2141  QgsDebugMsg( QStringLiteral( "* Units : feet" ) );
2142  }
2143  else if ( mapUnits() == QgsUnitTypes::DistanceDegrees )
2144  {
2145  QgsDebugMsg( QStringLiteral( "* Units : degrees" ) );
2146  }
2147 }
2148 
2150 {
2151  mValidationHint = html;
2152 }
2153 
2155 {
2156  return mValidationHint;
2157 }
2158 
2160 {
2162 }
2163 
2165 {
2166  mNativeFormat = format;
2167 }
2168 
2170 {
2171  return mNativeFormat;
2172 }
2173 
2174 long QgsCoordinateReferenceSystem::getRecordCount()
2175 {
2176  sqlite3_database_unique_ptr database;
2177  sqlite3_statement_unique_ptr statement;
2178  int myResult;
2179  long myRecordCount = 0;
2180  //check the db is available
2181  myResult = database.open_v2( QgsApplication::qgisUserDatabaseFilePath(), SQLITE_OPEN_READONLY, nullptr );
2182  if ( myResult != SQLITE_OK )
2183  {
2184  QgsDebugMsg( QStringLiteral( "Can't open database: %1" ).arg( database.errorMessage() ) );
2185  return 0;
2186  }
2187  // Set up the query to retrieve the projection information needed to populate the ELLIPSOID list
2188  QString mySql = QStringLiteral( "select count(*) from tbl_srs" );
2189  statement = database.prepare( mySql, myResult );
2190  if ( myResult == SQLITE_OK )
2191  {
2192  if ( statement.step() == SQLITE_ROW )
2193  {
2194  QString myRecordCountString = statement.columnAsText( 0 );
2195  myRecordCount = myRecordCountString.toLong();
2196  }
2197  }
2198  return myRecordCount;
2199 }
2200 
2202 {
2203  PJ_CONTEXT *pjContext = QgsProjContext::get();
2204  bool isGeographic = false;
2205  QgsProjUtils::proj_pj_unique_ptr coordinateSystem( proj_crs_get_coordinate_system( pjContext, crs ) );
2206  if ( coordinateSystem )
2207  {
2208  const int axisCount = proj_cs_get_axis_count( pjContext, coordinateSystem.get() );
2209  if ( axisCount > 0 )
2210  {
2211  const char *outUnitAuthName = nullptr;
2212  const char *outUnitAuthCode = nullptr;
2213  // Read only first axis
2214  proj_cs_get_axis_info( pjContext, coordinateSystem.get(), 0,
2215  nullptr,
2216  nullptr,
2217  nullptr,
2218  nullptr,
2219  nullptr,
2220  &outUnitAuthName,
2221  &outUnitAuthCode );
2222 
2223  if ( outUnitAuthName && outUnitAuthCode )
2224  {
2225  const char *unitCategory = nullptr;
2226  if ( proj_uom_get_info_from_database( pjContext, outUnitAuthName, outUnitAuthCode, nullptr, nullptr, &unitCategory ) )
2227  {
2228  isGeographic = QString( unitCategory ).compare( QLatin1String( "angular" ), Qt::CaseInsensitive ) == 0;
2229  }
2230  }
2231  }
2232  }
2233  return isGeographic;
2234 }
2235 
2236 void getOperationAndEllipsoidFromProjString( const QString &proj, QString &operation, QString &ellipsoid )
2237 {
2238  thread_local const QRegularExpression projRegExp( QStringLiteral( "\\+proj=(\\S+)" ) );
2239  const QRegularExpressionMatch projMatch = projRegExp.match( proj );
2240  if ( !projMatch.hasMatch() )
2241  {
2242  QgsDebugMsgLevel( QStringLiteral( "no +proj argument found [%2]" ).arg( proj ), 2 );
2243  return;
2244  }
2245  operation = projMatch.captured( 1 );
2246 
2247  const QRegularExpressionMatch ellipseMatch = projRegExp.match( proj );
2248  if ( ellipseMatch.hasMatch() )
2249  {
2250  ellipsoid = ellipseMatch.captured( 1 );
2251  }
2252  else
2253  {
2254  // satisfy not null constraint on ellipsoid_acronym field
2255  // possibly we should drop the constraint, yet the crses with missing ellipsoid_acronym are malformed
2256  // and will result in oddities within other areas of QGIS (e.g. project ellipsoid won't be correctly
2257  // set for these CRSes). Better just hack around and make the constraint happy for now,
2258  // and hope that the definitions get corrected in future.
2259  ellipsoid = "";
2260  }
2261 }
2262 
2263 
2264 bool QgsCoordinateReferenceSystem::loadFromAuthCode( const QString &auth, const QString &code )
2265 {
2266  d.detach();
2267  d->mIsValid = false;
2268  d->mWktPreferred.clear();
2269 
2270  PJ_CONTEXT *pjContext = QgsProjContext::get();
2271  QgsProjUtils::proj_pj_unique_ptr crs( proj_create_from_database( pjContext, auth.toUtf8().constData(), code.toUtf8().constData(), PJ_CATEGORY_CRS, false, nullptr ) );
2272  if ( !crs )
2273  {
2274  return false;
2275  }
2276 
2277  switch ( proj_get_type( crs.get() ) )
2278  {
2279  case PJ_TYPE_VERTICAL_CRS:
2280  return false;
2281 
2282  default:
2283  break;
2284  }
2285 
2287 
2288  QString proj4 = getFullProjString( crs.get() );
2289  proj4.replace( QLatin1String( "+type=crs" ), QString() );
2290  proj4 = proj4.trimmed();
2291 
2292  d->mIsValid = true;
2293  d->mProj4 = proj4;
2294  d->mWktPreferred.clear();
2295  d->mDescription = QString( proj_get_name( crs.get() ) );
2296  d->mAuthId = QStringLiteral( "%1:%2" ).arg( auth, code );
2297  d->mIsGeographic = testIsGeographic( crs.get() );
2298  d->mAxisInvertedDirty = true;
2299  QString operation;
2300  QString ellipsoid;
2302  d->mProjectionAcronym = operation;
2303  d->mEllipsoidAcronym.clear();
2304  d->setPj( std::move( crs ) );
2305 
2306  const QString dbVals = sAuthIdToQgisSrsIdMap.value( QStringLiteral( "%1:%2" ).arg( auth, code ).toUpper() );
2307  if ( !dbVals.isEmpty() )
2308  {
2309  const QStringList parts = dbVals.split( ',' );
2310  d->mSrsId = parts.at( 0 ).toInt();
2311  d->mSRID = parts.at( 1 ).toInt();
2312  }
2313 
2314  setMapUnits();
2315 
2316  return true;
2317 }
2318 
2319 QList<long> QgsCoordinateReferenceSystem::userSrsIds()
2320 {
2321  QList<long> results;
2322  // check user defined projection database
2323  const QString db = QgsApplication::qgisUserDatabaseFilePath();
2324 
2325  QFileInfo myInfo( db );
2326  if ( !myInfo.exists() )
2327  {
2328  QgsDebugMsg( "failed : " + db + " does not exist!" );
2329  return results;
2330  }
2331 
2332  sqlite3_database_unique_ptr database;
2333  sqlite3_statement_unique_ptr statement;
2334 
2335  //check the db is available
2336  int result = openDatabase( db, database );
2337  if ( result != SQLITE_OK )
2338  {
2339  QgsDebugMsg( "failed : " + db + " could not be opened!" );
2340  return results;
2341  }
2342 
2343  QString sql = QStringLiteral( "select srs_id from tbl_srs where srs_id >= %1" ).arg( USER_CRS_START_ID );
2344  int rc;
2345  statement = database.prepare( sql, rc );
2346  while ( true )
2347  {
2348  int ret = statement.step();
2349 
2350  if ( ret == SQLITE_DONE )
2351  {
2352  // there are no more rows to fetch - we can stop looping
2353  break;
2354  }
2355 
2356  if ( ret == SQLITE_ROW )
2357  {
2358  results.append( statement.columnAsInt64( 0 ) );
2359  }
2360  else
2361  {
2362  QgsMessageLog::logMessage( QObject::tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database.get() ) ), QObject::tr( "SpatiaLite" ) );
2363  break;
2364  }
2365  }
2366 
2367  return results;
2368 }
2369 
2370 long QgsCoordinateReferenceSystem::matchToUserCrs() const
2371 {
2372  PJ *obj = d->threadLocalProjObject();
2373  if ( !obj )
2374  return 0;
2375 
2376  const QList< long > ids = userSrsIds();
2377  for ( long id : ids )
2378  {
2380  if ( candidate.projObject() && proj_is_equivalent_to( obj, candidate.projObject(), PJ_COMP_EQUIVALENT ) )
2381  {
2382  return id;
2383  }
2384  }
2385  return 0;
2386 }
2387 
2388 static void sync_db_proj_logger( void * /* user_data */, int level, const char *message )
2389 {
2390 #ifndef QGISDEBUG
2391  Q_UNUSED( message )
2392 #endif
2393  if ( level == PJ_LOG_ERROR )
2394  {
2395  QgsDebugMsgLevel( QStringLiteral( "PROJ: %1" ).arg( message ), 2 );
2396  }
2397  else if ( level == PJ_LOG_DEBUG )
2398  {
2399  QgsDebugMsgLevel( QStringLiteral( "PROJ: %1" ).arg( message ), 3 );
2400  }
2401 }
2402 
2404 {
2405  setlocale( LC_ALL, "C" );
2406  QString dbFilePath = QgsApplication::srsDatabaseFilePath();
2407 
2408  int inserted = 0, updated = 0, deleted = 0, errors = 0;
2409 
2410  QgsDebugMsgLevel( QStringLiteral( "Load srs db from: %1" ).arg( QgsApplication::srsDatabaseFilePath().toLocal8Bit().constData() ), 4 );
2411 
2412  sqlite3_database_unique_ptr database;
2413  if ( database.open( dbFilePath ) != SQLITE_OK )
2414  {
2415  QgsDebugMsg( QStringLiteral( "Could not open database: %1 (%2)\n" ).arg( QgsApplication::srsDatabaseFilePath(), database.errorMessage() ) );
2416  return -1;
2417  }
2418 
2419  if ( sqlite3_exec( database.get(), "BEGIN TRANSACTION", nullptr, nullptr, nullptr ) != SQLITE_OK )
2420  {
2421  QgsDebugMsg( QStringLiteral( "Could not begin transaction: %1 (%2)\n" ).arg( QgsApplication::srsDatabaseFilePath(), database.errorMessage() ) );
2422  return -1;
2423  }
2424 
2425  sqlite3_statement_unique_ptr statement;
2426  int result;
2427  char *errMsg = nullptr;
2428 
2429  if ( sqlite3_exec( database.get(), "create table tbl_info (proj_major INT, proj_minor INT, proj_patch INT)", nullptr, nullptr, nullptr ) == SQLITE_OK )
2430  {
2431  QString sql = QStringLiteral( "INSERT INTO tbl_info(proj_major, proj_minor, proj_patch) VALUES (%1, %2,%3)" )
2432  .arg( QString::number( PROJ_VERSION_MAJOR ),
2433  QString::number( PROJ_VERSION_MINOR ),
2434  QString::number( PROJ_VERSION_PATCH ) );
2435  if ( sqlite3_exec( database.get(), sql.toUtf8(), nullptr, nullptr, &errMsg ) != SQLITE_OK )
2436  {
2437  QgsDebugMsg( QStringLiteral( "Could not execute: %1 [%2/%3]\n" ).arg(
2438  sql,
2439  database.errorMessage(),
2440  errMsg ? errMsg : "(unknown error)" ) );
2441  if ( errMsg )
2442  sqlite3_free( errMsg );
2443  return -1;
2444  }
2445  }
2446  else
2447  {
2448  // retrieve last update details
2449  QString sql = QStringLiteral( "SELECT proj_major, proj_minor, proj_patch FROM tbl_info" );
2450  statement = database.prepare( sql, result );
2451  if ( result != SQLITE_OK )
2452  {
2453  QgsDebugMsg( QStringLiteral( "Could not prepare: %1 [%2]\n" ).arg( sql, database.errorMessage() ) );
2454  return -1;
2455  }
2456  if ( statement.step() == SQLITE_ROW )
2457  {
2458  int major = statement.columnAsInt64( 0 );
2459  int minor = statement.columnAsInt64( 1 );
2460  int patch = statement.columnAsInt64( 2 );
2461  if ( major == PROJ_VERSION_MAJOR && minor == PROJ_VERSION_MINOR && patch == PROJ_VERSION_PATCH )
2462  // yay, nothing to do!
2463  return 0;
2464  }
2465  else
2466  {
2467  QgsDebugMsg( QStringLiteral( "Could not retrieve previous CRS sync PROJ version number" ) );
2468  return -1;
2469  }
2470  }
2471 
2472  PJ_CONTEXT *pjContext = QgsProjContext::get();
2473  // silence proj warnings
2474  proj_log_func( pjContext, nullptr, sync_db_proj_logger );
2475 
2476  PROJ_STRING_LIST authorities = proj_get_authorities_from_database( pjContext );
2477 
2478  int nextSrsId = 63561;
2479  int nextSrId = 520003561;
2480  for ( auto authIter = authorities; authIter && *authIter; ++authIter )
2481  {
2482  const QString authority( *authIter );
2483  QgsDebugMsgLevel( QStringLiteral( "Loading authority '%1'" ).arg( authority ), 2 );
2484  PROJ_STRING_LIST codes = proj_get_codes_from_database( pjContext, *authIter, PJ_TYPE_CRS, true );
2485 
2486  QStringList allCodes;
2487 
2488  for ( auto codesIter = codes; codesIter && *codesIter; ++codesIter )
2489  {
2490  const QString code( *codesIter );
2491  allCodes << QgsSqliteUtils::quotedString( code );
2492  QgsDebugMsgLevel( QStringLiteral( "Loading code '%1'" ).arg( code ), 4 );
2493  QgsProjUtils::proj_pj_unique_ptr crs( proj_create_from_database( pjContext, *authIter, *codesIter, PJ_CATEGORY_CRS, false, nullptr ) );
2494  if ( !crs )
2495  {
2496  QgsDebugMsg( QStringLiteral( "Could not load '%1:%2'" ).arg( authority, code ) );
2497  continue;
2498  }
2499 
2500  switch ( proj_get_type( crs.get() ) )
2501  {
2502  case PJ_TYPE_VERTICAL_CRS: // don't need these in the CRS db
2503  continue;
2504 
2505  default:
2506  break;
2507  }
2508 
2510 
2511  QString proj4 = getFullProjString( crs.get() );
2512  proj4.replace( QLatin1String( "+type=crs" ), QString() );
2513  proj4 = proj4.trimmed();
2514 
2515  if ( proj4.isEmpty() )
2516  {
2517  QgsDebugMsgLevel( QStringLiteral( "No proj4 for '%1:%2'" ).arg( authority, code ), 2 );
2518  // satisfy not null constraint
2519  proj4 = "";
2520  }
2521 
2522  const bool deprecated = proj_is_deprecated( crs.get() );
2523  const QString name( proj_get_name( crs.get() ) );
2524 
2525  QString sql = QStringLiteral( "SELECT parameters,description,deprecated FROM tbl_srs WHERE auth_name='%1' AND auth_id='%2'" ).arg( authority, code );
2526  statement = database.prepare( sql, result );
2527  if ( result != SQLITE_OK )
2528  {
2529  QgsDebugMsg( QStringLiteral( "Could not prepare: %1 [%2]\n" ).arg( sql, database.errorMessage() ) );
2530  continue;
2531  }
2532 
2533  QString srsProj4;
2534  QString srsDesc;
2535  bool srsDeprecated = deprecated;
2536  if ( statement.step() == SQLITE_ROW )
2537  {
2538  srsProj4 = statement.columnAsText( 0 );
2539  srsDesc = statement.columnAsText( 1 );
2540  srsDeprecated = statement.columnAsText( 2 ).toInt() != 0;
2541  }
2542 
2543  if ( !srsProj4.isEmpty() || !srsDesc.isEmpty() )
2544  {
2545  if ( proj4 != srsProj4 || name != srsDesc || deprecated != srsDeprecated )
2546  {
2547  errMsg = nullptr;
2548  sql = QStringLiteral( "UPDATE tbl_srs SET parameters=%1,description=%2,deprecated=%3 WHERE auth_name=%4 AND auth_id=%5" )
2549  .arg( QgsSqliteUtils::quotedString( proj4 ) )
2550  .arg( QgsSqliteUtils::quotedString( name ) )
2551  .arg( deprecated ? 1 : 0 )
2552  .arg( QgsSqliteUtils::quotedString( authority ), QgsSqliteUtils::quotedString( code ) );
2553 
2554  if ( sqlite3_exec( database.get(), sql.toUtf8(), nullptr, nullptr, &errMsg ) != SQLITE_OK )
2555  {
2556  QgsDebugMsg( QStringLiteral( "Could not execute: %1 [%2/%3]\n" ).arg(
2557  sql,
2558  database.errorMessage(),
2559  errMsg ? errMsg : "(unknown error)" ) );
2560  if ( errMsg )
2561  sqlite3_free( errMsg );
2562  errors++;
2563  }
2564  else
2565  {
2566  updated++;
2567  }
2568  }
2569  }
2570  else
2571  {
2572  // there's a not-null contraint on these columns, so we must use empty strings instead
2573  QString operation = "";
2574  QString ellps = "";
2576  const bool isGeographic = testIsGeographic( crs.get() );
2577 
2578  // work out srid and srsid
2579  const QString dbVals = sAuthIdToQgisSrsIdMap.value( QStringLiteral( "%1:%2" ).arg( authority, code ) );
2580  QString srsId;
2581  QString srId;
2582  if ( !dbVals.isEmpty() )
2583  {
2584  const QStringList parts = dbVals.split( ',' );
2585  srsId = parts.at( 0 );
2586  srId = parts.at( 1 );
2587  }
2588  if ( srId.isEmpty() )
2589  {
2590  srId = QString::number( nextSrId );
2591  nextSrId++;
2592  }
2593  if ( srsId.isEmpty() )
2594  {
2595  srsId = QString::number( nextSrsId );
2596  nextSrsId++;
2597  }
2598 
2599  if ( !srsId.isEmpty() )
2600  {
2601  sql = QStringLiteral( "INSERT INTO tbl_srs(srs_id, description,projection_acronym,ellipsoid_acronym,parameters,srid,auth_name,auth_id,is_geo,deprecated) VALUES (%1, %2,%3,%4,%5,%6,%7,%8,%9,%10)" )
2602  .arg( srsId )
2603  .arg( QgsSqliteUtils::quotedString( name ),
2606  QgsSqliteUtils::quotedString( proj4 ) )
2607  .arg( srId )
2608  .arg( QgsSqliteUtils::quotedString( authority ) )
2609  .arg( QgsSqliteUtils::quotedString( code ) )
2610  .arg( isGeographic ? 1 : 0 )
2611  .arg( deprecated ? 1 : 0 );
2612  }
2613  else
2614  {
2615  sql = QStringLiteral( "INSERT INTO tbl_srs(description,projection_acronym,ellipsoid_acronym,parameters,srid,auth_name,auth_id,is_geo,deprecated) VALUES (%2,%3,%4,%5,%6,%7,%8,%9,%10)" )
2616  .arg( QgsSqliteUtils::quotedString( name ),
2619  QgsSqliteUtils::quotedString( proj4 ) )
2620  .arg( srId )
2621  .arg( QgsSqliteUtils::quotedString( authority ) )
2622  .arg( QgsSqliteUtils::quotedString( code ) )
2623  .arg( isGeographic ? 1 : 0 )
2624  .arg( deprecated ? 1 : 0 );
2625  }
2626 
2627  errMsg = nullptr;
2628  if ( sqlite3_exec( database.get(), sql.toUtf8(), nullptr, nullptr, &errMsg ) == SQLITE_OK )
2629  {
2630  inserted++;
2631  }
2632  else
2633  {
2634  qCritical( "Could not execute: %s [%s/%s]\n",
2635  sql.toLocal8Bit().constData(),
2636  sqlite3_errmsg( database.get() ),
2637  errMsg ? errMsg : "(unknown error)" );
2638  errors++;
2639 
2640  if ( errMsg )
2641  sqlite3_free( errMsg );
2642  }
2643  }
2644  }
2645 
2646  proj_string_list_destroy( codes );
2647 
2648  const QString sql = QStringLiteral( "DELETE FROM tbl_srs WHERE auth_name='%1' AND NOT auth_id IN (%2)" ).arg( authority, allCodes.join( ',' ) );
2649  if ( sqlite3_exec( database.get(), sql.toUtf8(), nullptr, nullptr, nullptr ) == SQLITE_OK )
2650  {
2651  deleted = sqlite3_changes( database.get() );
2652  }
2653  else
2654  {
2655  errors++;
2656  qCritical( "Could not execute: %s [%s]\n",
2657  sql.toLocal8Bit().constData(),
2658  sqlite3_errmsg( database.get() ) );
2659  }
2660 
2661  }
2662  proj_string_list_destroy( authorities );
2663 
2664  QString sql = QStringLiteral( "UPDATE tbl_info set proj_major=%1,proj_minor=%2,proj_patch=%3" )
2665  .arg( QString::number( PROJ_VERSION_MAJOR ),
2666  QString::number( PROJ_VERSION_MINOR ),
2667  QString::number( PROJ_VERSION_PATCH ) );
2668  if ( sqlite3_exec( database.get(), sql.toUtf8(), nullptr, nullptr, &errMsg ) != SQLITE_OK )
2669  {
2670  QgsDebugMsg( QStringLiteral( "Could not execute: %1 [%2/%3]\n" ).arg(
2671  sql,
2672  database.errorMessage(),
2673  errMsg ? errMsg : "(unknown error)" ) );
2674  if ( errMsg )
2675  sqlite3_free( errMsg );
2676  return -1;
2677  }
2678 
2679  if ( sqlite3_exec( database.get(), "COMMIT", nullptr, nullptr, nullptr ) != SQLITE_OK )
2680  {
2681  QgsDebugMsg( QStringLiteral( "Could not commit transaction: %1 [%2]\n" ).arg(
2683  sqlite3_errmsg( database.get() ) )
2684  );
2685  return -1;
2686  }
2687 
2688 #ifdef QGISDEBUG
2689  QgsDebugMsgLevel( QStringLiteral( "CRS update (inserted:%1 updated:%2 deleted:%3 errors:%4)" ).arg( inserted ).arg( updated ).arg( deleted ).arg( errors ), 4 );
2690 #else
2691  Q_UNUSED( deleted )
2692 #endif
2693 
2694  if ( errors > 0 )
2695  return -errors;
2696  else
2697  return updated + inserted;
2698 }
2699 
2700 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::stringCache()
2701 {
2702  return *sStringCache();
2703 }
2704 
2705 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::projCache()
2706 {
2707  return *sProj4Cache();
2708 }
2709 
2710 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::ogcCache()
2711 {
2712  return *sOgcCache();
2713 }
2714 
2715 const QHash<QString, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::wktCache()
2716 {
2717  return *sWktCache();
2718 }
2719 
2720 const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srIdCache()
2721 {
2722  return *sSrIdCache();
2723 }
2724 
2725 const QHash<long, QgsCoordinateReferenceSystem> &QgsCoordinateReferenceSystem::srsIdCache()
2726 {
2727  return *sSrsIdCache();
2728 }
2729 
2731 {
2732  if ( isGeographic() )
2733  {
2734  return *this;
2735  }
2736 
2737  if ( PJ *obj = d->threadLocalProjObject() )
2738  {
2739  PJ_CONTEXT *pjContext = QgsProjContext::get();
2740  QgsProjUtils::proj_pj_unique_ptr geoCrs( proj_crs_get_geodetic_crs( pjContext, obj ) );
2741  if ( !geoCrs )
2743 
2744  if ( !testIsGeographic( geoCrs.get() ) )
2746 
2747  QgsProjUtils::proj_pj_unique_ptr normalized( proj_normalize_for_visualization( pjContext, geoCrs.get() ) );
2748  if ( !normalized )
2750 
2751  return QgsCoordinateReferenceSystem::fromProjObject( normalized.get() );
2752  }
2753  else
2754  {
2756  }
2757 }
2758 
2760 {
2761  if ( isGeographic() )
2762  {
2763  return d->mAuthId;
2764  }
2765  else if ( PJ *obj = d->threadLocalProjObject() )
2766  {
2767  QgsProjUtils::proj_pj_unique_ptr geoCrs( proj_crs_get_geodetic_crs( QgsProjContext::get(), obj ) );
2768  return geoCrs ? QStringLiteral( "%1:%2" ).arg( proj_get_id_auth_name( geoCrs.get(), 0 ), proj_get_id_code( geoCrs.get(), 0 ) ) : QString();
2769  }
2770  else
2771  {
2772  return QString();
2773  }
2774 }
2775 
2777 {
2778  return d->threadLocalProjObject();
2779 }
2780 
2782 {
2784  crs.createFromProjObject( object );
2785  return crs;
2786 }
2787 
2789 {
2790  d.detach();
2791  d->mIsValid = false;
2792  d->mProj4.clear();
2793  d->mWktPreferred.clear();
2794 
2795  if ( !object )
2796  {
2797  return false;
2798  }
2799 
2800  switch ( proj_get_type( object ) )
2801  {
2802  case PJ_TYPE_GEODETIC_CRS:
2803  case PJ_TYPE_GEOCENTRIC_CRS:
2804  case PJ_TYPE_GEOGRAPHIC_CRS:
2805  case PJ_TYPE_GEOGRAPHIC_2D_CRS:
2806  case PJ_TYPE_GEOGRAPHIC_3D_CRS:
2807  case PJ_TYPE_VERTICAL_CRS:
2808  case PJ_TYPE_PROJECTED_CRS:
2809  case PJ_TYPE_COMPOUND_CRS:
2810  case PJ_TYPE_TEMPORAL_CRS:
2811  case PJ_TYPE_ENGINEERING_CRS:
2812  case PJ_TYPE_BOUND_CRS:
2813  case PJ_TYPE_OTHER_CRS:
2814  break;
2815 
2816  default:
2817  return false;
2818  }
2819 
2820  d->setPj( QgsProjUtils::crsToSingleCrs( object ) );
2821 
2822  if ( !d->hasPj() )
2823  {
2824  return d->mIsValid;
2825  }
2826  else
2827  {
2828  // maybe we can directly grab the auth name and code from the crs
2829  const QString authName( proj_get_id_auth_name( d->threadLocalProjObject(), 0 ) );
2830  const QString authCode( proj_get_id_code( d->threadLocalProjObject(), 0 ) );
2831  if ( !authName.isEmpty() && !authCode.isEmpty() && loadFromAuthCode( authName, authCode ) )
2832  {
2833  return d->mIsValid;
2834  }
2835  else
2836  {
2837  // Still a valid CRS, just not a known one
2838  d->mIsValid = true;
2839  d->mDescription = QString( proj_get_name( d->threadLocalProjObject() ) );
2840  setMapUnits();
2841  }
2842  }
2843 
2844  return d->mIsValid;
2845 }
2846 
2848 {
2849  QStringList projections;
2850  const QList<QgsCoordinateReferenceSystem> res = recentCoordinateReferenceSystems();
2851  projections.reserve( res.size() );
2852  for ( const QgsCoordinateReferenceSystem &crs : res )
2853  {
2854  projections << QString::number( crs.srsid() );
2855  }
2856  return projections;
2857 }
2858 
2860 {
2861  QList<QgsCoordinateReferenceSystem> res;
2862 
2863  // Read settings from persistent storage
2864  QgsSettings settings;
2865  QStringList projectionsProj4 = settings.value( QStringLiteral( "UI/recentProjectionsProj4" ) ).toStringList();
2866  QStringList projectionsWkt = settings.value( QStringLiteral( "UI/recentProjectionsWkt" ) ).toStringList();
2867  QStringList projectionsAuthId = settings.value( QStringLiteral( "UI/recentProjectionsAuthId" ) ).toStringList();
2868  int max = std::max( projectionsAuthId.size(), std::max( projectionsProj4.size(), projectionsWkt.size() ) );
2869  res.reserve( max );
2870  for ( int i = 0; i < max; ++i )
2871  {
2872  const QString proj = projectionsProj4.value( i );
2873  const QString wkt = projectionsWkt.value( i );
2874  const QString authid = projectionsAuthId.value( i );
2875 
2877  if ( !authid.isEmpty() )
2879  if ( !crs.isValid() && !wkt.isEmpty() )
2880  crs.createFromWkt( wkt );
2881  if ( !crs.isValid() && !proj.isEmpty() )
2882  crs.createFromProj( wkt );
2883 
2884  if ( crs.isValid() )
2885  res << crs;
2886  }
2887  return res;
2888 }
2889 
2891 {
2892  // we only want saved and standard CRSes in the recent list
2893  if ( crs.srsid() == 0 || !crs.isValid() )
2894  return;
2895 
2896  QList<QgsCoordinateReferenceSystem> recent = recentCoordinateReferenceSystems();
2897  recent.removeAll( crs );
2898  recent.insert( 0, crs );
2899 
2900  // trim to max 30 items
2901  recent = recent.mid( 0, 30 );
2902  QStringList authids;
2903  authids.reserve( recent.size() );
2904  QStringList proj;
2905  proj.reserve( recent.size() );
2906  QStringList wkt;
2907  wkt.reserve( recent.size() );
2908  for ( const QgsCoordinateReferenceSystem &c : std::as_const( recent ) )
2909  {
2910  authids << c.authid();
2911  proj << c.toProj();
2912  wkt << c.toWkt( WKT_PREFERRED );
2913  }
2914 
2915  QgsSettings settings;
2916  settings.setValue( QStringLiteral( "UI/recentProjectionsAuthId" ), authids );
2917  settings.setValue( QStringLiteral( "UI/recentProjectionsWkt" ), wkt );
2918  settings.setValue( QStringLiteral( "UI/recentProjectionsProj4" ), proj );
2919 }
2920 
2922 {
2923  sSrIdCacheLock()->lockForWrite();
2924  if ( !sDisableSrIdCache )
2925  {
2926  if ( disableCache )
2927  sDisableSrIdCache = true;
2928  sSrIdCache()->clear();
2929  }
2930  sSrIdCacheLock()->unlock();
2931 
2932  sOgcLock()->lockForWrite();
2933  if ( !sDisableOgcCache )
2934  {
2935  if ( disableCache )
2936  sDisableOgcCache = true;
2937  sOgcCache()->clear();
2938  }
2939  sOgcLock()->unlock();
2940 
2941  sProj4CacheLock()->lockForWrite();
2942  if ( !sDisableProjCache )
2943  {
2944  if ( disableCache )
2945  sDisableProjCache = true;
2946  sProj4Cache()->clear();
2947  }
2948  sProj4CacheLock()->unlock();
2949 
2950  sCRSWktLock()->lockForWrite();
2951  if ( !sDisableWktCache )
2952  {
2953  if ( disableCache )
2954  sDisableWktCache = true;
2955  sWktCache()->clear();
2956  }
2957  sCRSWktLock()->unlock();
2958 
2959  sCRSSrsIdLock()->lockForWrite();
2960  if ( !sDisableSrsIdCache )
2961  {
2962  if ( disableCache )
2963  sDisableSrsIdCache = true;
2964  sSrsIdCache()->clear();
2965  }
2966  sCRSSrsIdLock()->unlock();
2967 
2968  sCrsStringLock()->lockForWrite();
2969  if ( !sDisableStringCache )
2970  {
2971  if ( disableCache )
2972  sDisableStringCache = true;
2973  sStringCache()->clear();
2974  }
2975  sCrsStringLock()->unlock();
2976 }
2977 
2978 // invalid < regular < user
2980 {
2981  if ( c1.d == c2.d )
2982  return false;
2983 
2984  if ( !c1.d->mIsValid && !c2.d->mIsValid )
2985  return false;
2986 
2987  if ( !c1.d->mIsValid && c2.d->mIsValid )
2988  return false;
2989 
2990  if ( c1.d->mIsValid && !c2.d->mIsValid )
2991  return true;
2992 
2993  const bool c1IsUser = c1.d->mSrsId >= USER_CRS_START_ID;
2994  const bool c2IsUser = c2.d->mSrsId >= USER_CRS_START_ID;
2995 
2996  if ( c1IsUser && !c2IsUser )
2997  return true;
2998 
2999  if ( !c1IsUser && c2IsUser )
3000  return false;
3001 
3002  if ( !c1IsUser && !c2IsUser )
3003  {
3004  if ( c1.d->mAuthId != c2.d->mAuthId )
3005  return c1.d->mAuthId > c2.d->mAuthId;
3006  }
3007  else
3008  {
3009  const QString wkt1 = c1.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED );
3010  const QString wkt2 = c2.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED );
3011  if ( wkt1 != wkt2 )
3012  return wkt1 > wkt2;
3013  }
3014 
3015  if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
3016  return false;
3017 
3018  if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3019  return false;
3020 
3021  if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
3022  return false;
3023 
3024  if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3025  return true;
3026 
3027  return c1.d->mCoordinateEpoch > c2.d->mCoordinateEpoch;
3028 }
3029 
3031 {
3032  if ( c1.d == c2.d )
3033  return false;
3034 
3035  if ( !c1.d->mIsValid && !c2.d->mIsValid )
3036  return false;
3037 
3038  if ( c1.d->mIsValid && !c2.d->mIsValid )
3039  return false;
3040 
3041  if ( !c1.d->mIsValid && c2.d->mIsValid )
3042  return true;
3043 
3044  const bool c1IsUser = c1.d->mSrsId >= USER_CRS_START_ID;
3045  const bool c2IsUser = c2.d->mSrsId >= USER_CRS_START_ID;
3046 
3047  if ( !c1IsUser && c2IsUser )
3048  return true;
3049 
3050  if ( c1IsUser && !c2IsUser )
3051  return false;
3052 
3053  if ( !c1IsUser && !c2IsUser )
3054  {
3055  if ( c1.d->mAuthId != c2.d->mAuthId )
3056  return c1.d->mAuthId < c2.d->mAuthId;
3057  }
3058  else
3059  {
3060  const QString wkt1 = c1.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED );
3061  const QString wkt2 = c2.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED );
3062  if ( wkt1 != wkt2 )
3063  return wkt1 < wkt2;
3064  }
3065 
3066  if ( c1.d->mCoordinateEpoch == c2.d->mCoordinateEpoch )
3067  return false;
3068 
3069  if ( std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3070  return false;
3071 
3072  if ( !std::isnan( c1.d->mCoordinateEpoch ) && std::isnan( c2.d->mCoordinateEpoch ) )
3073  return false;
3074 
3075  if ( std::isnan( c1.d->mCoordinateEpoch ) && !std::isnan( c2.d->mCoordinateEpoch ) )
3076  return true;
3077 
3078  return c1.d->mCoordinateEpoch < c2.d->mCoordinateEpoch;
3079 }
3080 
3082 {
3083  return !( c1 < c2 );
3084 }
3086 {
3087  return !( c1 > c2 );
3088 }
QgsCoordinateReferenceSystem::recentProjections
static Q_DECL_DEPRECATED QStringList recentProjections()
Returns a list of recently used projections.
Definition: qgscoordinatereferencesystem.cpp:2847
Qgis::CrsAxisDirection::Towards
@ Towards
Towards.
QgsProjContext::get
static PJ_CONTEXT * get()
Returns a thread local instance of a proj context, safe for use in the current thread.
Definition: qgsprojutils.cpp:48
QgsReadWriteLocker::changeMode
void changeMode(Mode mode)
Change the mode of the lock to mode.
Definition: qgsreadwritelocker.cpp:30
QgsCoordinateReferenceSystem::findMatchingProj
Q_DECL_DEPRECATED long findMatchingProj()
Walks the CRS databases (both system and user database) trying to match stored PROJ string to a datab...
Definition: qgscoordinatereferencesystem.cpp:1702
QgsCoordinateReferenceSystem::InternalCrsId
@ InternalCrsId
Internal ID used by QGIS in the local SQLite database.
Definition: qgscoordinatereferencesystem.h:225
Qgis::CrsAxisDirection::WestNorthWest
@ WestNorthWest
West North West.
QgsProjUtils::axisOrderIsSwapped
static bool axisOrderIsSwapped(const PJ *crs)
Returns true if the given proj coordinate system uses requires y/x coordinate order instead of x/y.
Definition: qgsprojutils.cpp:111
qgsreadwritelocker.h
QgsCoordinateReferenceSystem::createFromWkt
bool createFromWkt(const QString &wkt)
Sets this CRS using a WKT definition.
Definition: qgscoordinatereferencesystem.cpp:899
qgsprojectionfactors.h
QgsReadWriteLocker::Read
@ Read
Lock for read.
Definition: qgsreadwritelocker.h:75
QgsDatumEnsembleMember
Contains information about a member of a datum ensemble.
Definition: qgsdatums.h:34
QgsCoordinateReferenceSystem::~QgsCoordinateReferenceSystem
~QgsCoordinateReferenceSystem()
Definition: qgscoordinatereferencesystem.cpp:242
QgsCoordinateReferenceSystem::pushRecentCoordinateReferenceSystem
static void pushRecentCoordinateReferenceSystem(const QgsCoordinateReferenceSystem &crs)
Pushes a recently used CRS to the top of the recent CRS list.
Definition: qgscoordinatereferencesystem.cpp:2890
QgsCoordinateReferenceSystem::isDynamic
bool isDynamic() const
Returns true if the CRS is a dynamic CRS.
Definition: qgscoordinatereferencesystem.cpp:1314
QgsCoordinateReferenceSystem::factors
QgsProjectionFactors factors(const QgsPoint &point) const
Calculate various cartographic properties, such as scale factors, angular distortion and meridian con...
Definition: qgscoordinatereferencesystem.cpp:1401
sqlite3_database_unique_ptr::open
int open(const QString &path)
Opens the database at the specified file path.
Definition: qgssqliteutils.cpp:78
Qgis::CrsDefinitionFormat
CrsDefinitionFormat
CRS definition formats.
Definition: qgis.h:1687
QgsCoordinateReferenceSystem::saveAsUserCrs
long saveAsUserCrs(const QString &name, Qgis::CrsDefinitionFormat nativeFormat=Qgis::CrsDefinitionFormat::Wkt)
Saves the CRS as a new custom ("USER") CRS.
Definition: qgscoordinatereferencesystem.cpp:2159
USER_CRS_START_ID
const int USER_CRS_START_ID
Magick number that determines whether a projection crsid is a system (srs.db) or user (~/....
Definition: qgis.h:2768
QgsCoordinateReferenceSystem::customCrsValidation
static CUSTOM_CRS_VALIDATION customCrsValidation()
Gets custom function.
Definition: qgscoordinatereferencesystem.cpp:2122
Qgis::CrsAxisDirection::SouthWest
@ SouthWest
South West.
QgsSettings::value
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Definition: qgssettings.cpp:161
Qgis::CrsAxisDirection::Unspecified
@ Unspecified
Unspecified.
QgsCoordinateReferenceSystem::projectionAcronym
QString projectionAcronym() const
Returns the projection acronym for the projection used by the CRS.
Definition: qgscoordinatereferencesystem.cpp:1239
Qgis::CrsAxisDirection::DisplayDown
@ DisplayDown
Display down.
QgsPoint
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:48
QgsCoordinateReferenceSystem::userFriendlyIdentifier
QString userFriendlyIdentifier(IdentifierType type=MediumString) const
Returns a user friendly identifier for the CRS.
Definition: qgscoordinatereferencesystem.cpp:1212
QgsUnitTypes::DistanceUnknownUnit
@ DistanceUnknownUnit
Unknown distance unit.
Definition: qgsunittypes.h:78
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
qgsNanCompatibleEquals
bool qgsNanCompatibleEquals(double a, double b)
Compare two doubles, treating nan values as equal.
Definition: qgis.h:2249
sqlite3_database_unique_ptr::prepare
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
Definition: qgssqliteutils.cpp:99
Qgis::CrsAxisDirection::West
@ West
West.
QgsCoordinateReferenceSystem::WKT_PREFERRED
@ WKT_PREFERRED
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
Definition: qgscoordinatereferencesystem.h:680
Qgis::CrsAxisDirection::Future
@ Future
Future.
QgsCoordinateReferenceSystem::CrsType
CrsType
Enumeration of types of IDs accepted in createFromId() method.
Definition: qgscoordinatereferencesystem.h:223
QgsCoordinateReferenceSystem::createFromSrsId
bool createFromSrsId(long srsId)
Sets this CRS by lookup of internal QGIS CRS ID in the CRS database.
Definition: qgscoordinatereferencesystem.cpp:555
Qgis::CrsAxisDirection::DisplayUp
@ DisplayUp
Display up.
crs
const QgsCoordinateReferenceSystem & crs
Definition: qgswfsgetfeature.cpp:105
QgsCoordinateReferenceSystem::fromOgcWmsCrs
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
Definition: qgscoordinatereferencesystem.cpp:195
QgsCoordinateReferenceSystem::fromProj
static QgsCoordinateReferenceSystem fromProj(const QString &proj)
Creates a CRS from a proj style formatted string.
Definition: qgscoordinatereferencesystem.cpp:221
Qgis::CrsAxisDirection::SouthSouthEast
@ SouthSouthEast
South South East.
Qgis::CrsAxisDirection::RowNegative
@ RowNegative
Row negative.
QgsCoordinateReferenceSystem::description
QString description
Definition: qgscoordinatereferencesystem.h:218
QgsProjUtils::crsToDatumEnsemble
static proj_pj_unique_ptr crsToDatumEnsemble(const PJ *crs)
Given a PROJ crs, attempt to retrieve the datum ensemble from it.
Definition: qgsprojutils.cpp:219
Qgis::CrsAxisDirection::NorthNorthWest
@ NorthNorthWest
North North West.
PJ_CONTEXT
struct projCtx_t PJ_CONTEXT
Definition: qgscoordinatereferencesystem.h:56
QgsCoordinateReferenceSystem::axisOrdering
QList< Qgis::CrsAxisDirection > axisOrdering() const
Returns an ordered list of the axis directions reflecting the native axis order for the CRS.
Definition: qgscoordinatereferencesystem.cpp:804
QgsCoordinateReferenceSystem::WKT2_2019_SIMPLIFIED
@ WKT2_2019_SIMPLIFIED
WKT2_2019 with the simplification rule of WKT2_SIMPLIFIED.
Definition: qgscoordinatereferencesystem.h:678
qgis.h
qgsogrutils.h
qgsprojoperation.h
QgsSettings
This class is a composition of two QSettings instances:
Definition: qgssettings.h:61
QgsProjUtils::proj_pj_unique_ptr
std::unique_ptr< PJ, ProjPJDeleter > proj_pj_unique_ptr
Scoped Proj PJ object.
Definition: qgsprojutils.h:141
QgsCoordinateReferenceSystem::hasAxisInverted
bool hasAxisInverted() const
Returns whether axis is inverted (e.g., for WMS 1.3) for the CRS.
Definition: qgscoordinatereferencesystem.cpp:793
QgsSqliteUtils::quotedString
static QString quotedString(const QString &value)
Returns a quoted string value, surround by ' characters and with special characters correctly escaped...
Definition: qgssqliteutils.cpp:249
QgsOgrUtils::OGRSpatialReferenceToWkt
static QString OGRSpatialReferenceToWkt(OGRSpatialReferenceH srs)
Returns a WKT string corresponding to the specified OGR srs object.
Definition: qgsogrutils.cpp:1009
QgsCoordinateReferenceSystemRegistry::projOperations
QMap< QString, QgsProjOperation > projOperations() const
Returns a map of all valid PROJ operations.
Definition: qgscoordinatereferencesystemregistry.cpp:349
sqlite3_statement_unique_ptr::columnAsInt64
qlonglong columnAsInt64(int column) const
Gets column value from the current statement row as a long long integer (64 bits).
Definition: qgssqliteutils.cpp:73
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsCoordinateReferenceSystem::createFromUserInput
bool createFromUserInput(const QString &definition)
Set up this CRS from various text formats.
Definition: qgscoordinatereferencesystem.cpp:347
QgsUnitTypes::DistanceUnit
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:67
qgslocalec.h
QgsCoordinateReferenceSystem::readXml
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
Definition: qgscoordinatereferencesystem.cpp:1860
QgsCoordinateReferenceSystem::WKT1_ESRI
@ WKT1_ESRI
WKT1 as traditionally output by ESRI software, deriving from OGC 99-049.
Definition: qgscoordinatereferencesystem.h:672
sqlite3_database_unique_ptr::errorMessage
QString errorMessage() const
Returns the most recent error message encountered by the database.
Definition: qgssqliteutils.cpp:94
QgsUnitTypes::DistanceKilometers
@ DistanceKilometers
Kilometers.
Definition: qgsunittypes.h:70
Q_GLOBAL_STATIC
Q_GLOBAL_STATIC(QReadWriteLock, sDefinitionCacheLock)
QgsProjOperation
Contains information about a PROJ operation.
Definition: qgsprojoperation.h:30
QgsReadWriteLocker::unlock
void unlock()
Unlocks the lock.
Definition: qgsreadwritelocker.cpp:45
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:41
qgsDoubleToString
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:2204
sqlite3_statement_unique_ptr::step
int step()
Steps to the next record in the statement, returning the sqlite3 result code.
Definition: qgssqliteutils.cpp:41
QgsReadWriteLocker
The QgsReadWriteLocker class is a convenience class that simplifies locking and unlocking QReadWriteL...
Definition: qgsreadwritelocker.h:40
QgsProjUtils::crsToSingleCrs
static proj_pj_unique_ptr crsToSingleCrs(const PJ *crs)
Given a PROJ crs (which may be a compound or bound crs, or some other type), extract a single crs fro...
Definition: qgsprojutils.cpp:185
QgsCoordinateReferenceSystem::bounds
QgsRectangle bounds() const
Returns the approximate bounds for the region the CRS is usable within.
Definition: qgscoordinatereferencesystem.cpp:1470
QgsCoordinateReferenceSystem::createFromProj4
Q_DECL_DEPRECATED bool createFromProj4(const QString &projString)
Sets this CRS by passing it a PROJ style formatted string.
Definition: qgscoordinatereferencesystem.cpp:982
qgsapplication.h
OGRSpatialReferenceH
void * OGRSpatialReferenceH
Definition: qgscoordinatereferencesystem.h:66
Qgis::CrsAxisDirection::GeocentricY
@ GeocentricY
Geocentric (Y)
QgsApplication::srsDatabaseFilePath
static QString srsDatabaseFilePath()
Returns the path to the srs.db file.
Definition: qgsapplication.cpp:1124
QgsCoordinateReferenceSystem::operator==
bool operator==(const QgsCoordinateReferenceSystem &srs) const
Overloaded == operator used to compare to CRS's.
Definition: qgscoordinatereferencesystem.cpp:1778
Q_NOWARN_DEPRECATED_POP
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:2820
QgsPoint::y
double y
Definition: qgspoint.h:70
QgsCoordinateReferenceSystem::syncDatabase
static int syncDatabase()
Update proj.4 parameters in our database from proj.4.
Definition: qgscoordinatereferencesystem.cpp:2403
Qgis::CrsAxisDirection::GeocentricZ
@ GeocentricZ
Geocentric (Z)
QgsCoordinateReferenceSystem::WktVariant
WktVariant
WKT formatting variants, only used for builds based on Proj >= 6.
Definition: qgscoordinatereferencesystem.h:669
QgsCoordinateReferenceSystem::isGeographic
bool isGeographic
Definition: qgscoordinatereferencesystem.h:216
Qgis::CrsAxisDirection::East
@ East
East.
getFullProjString
QString getFullProjString(PJ *obj)
Definition: qgscoordinatereferencesystem.cpp:86
QgsCoordinateReferenceSystem::fromSrsId
static QgsCoordinateReferenceSystem fromSrsId(long srsId)
Creates a CRS from a specified QGIS SRS ID.
Definition: qgscoordinatereferencesystem.cpp:235
Qgis::CrsAxisDirection::Past
@ Past
Past.
Qgis::CrsAxisDirection::CounterClockwise
@ CounterClockwise
Counter clockwise.
QgsCoordinateReferenceSystem::srsid
long srsid() const
Returns the internal CRS ID, if available.
Definition: qgscoordinatereferencesystem.cpp:1185
getOperationAndEllipsoidFromProjString
void getOperationAndEllipsoidFromProjString(const QString &proj, QString &operation, QString &ellipsoid)
Definition: qgscoordinatereferencesystem.cpp:2236
Qgis::CrsAxisDirection::SouthSouthWest
@ SouthSouthWest
South South West.
QgsUnitTypes::DistanceDegrees
@ DistanceDegrees
Degrees, for planar geographic CRS distance measurements.
Definition: qgsunittypes.h:75
QgsCoordinateReferenceSystem::writeXml
bool writeXml(QDomNode &node, QDomDocument &doc) const
Stores state to the given Dom node in the given document.
Definition: qgscoordinatereferencesystem.cpp:1980
Qgis::CrsAxisDirection::DisplayLeft
@ DisplayLeft
Display left.
QgsLocaleNumC
Definition: qgslocalec.h:30
qgsdatums.h
QgsProjUtils::isDynamic
static bool isDynamic(const PJ *crs)
Returns true if the given proj coordinate system is a dynamic CRS.
Definition: qgsprojutils.cpp:142
QgsCoordinateReferenceSystem::celestialBodyName
QString celestialBodyName() const SIP_THROW(QgsNotSupportedException)
Attempts to retrieve the name of the celestial body associated with the CRS (e.g.
Definition: qgscoordinatereferencesystem.cpp:1323
QgsCoordinateReferenceSystem::validationHint
QString validationHint() const
Gets user hint for validation.
Definition: qgscoordinatereferencesystem.cpp:2154
QgsCoordinateReferenceSystem::createFromId
Q_DECL_DEPRECATED bool createFromId(long id, CrsType type=PostgisCrsId)
Sets this CRS by lookup of the given ID in the CRS database.
Definition: qgscoordinatereferencesystem.cpp:246
QgsCoordinateReferenceSystem::createFromProjObject
bool createFromProjObject(PJ *object)
Sets this CRS by passing it a PROJ PJ object, corresponding to a PROJ CRS object.
Definition: qgscoordinatereferencesystem.cpp:2788
QgsApplication::coordinateReferenceSystemRegistry
static QgsCoordinateReferenceSystemRegistry * coordinateReferenceSystemRegistry()
Returns the application's coordinate reference system (CRS) registry, which handles known CRS definit...
Definition: qgsapplication.cpp:2355
Qgis::CrsAxisDirection::South
@ South
South.
sAuthIdToQgisSrsIdMap
const QMap< QString, QString > sAuthIdToQgisSrsIdMap
Definition: qgscoordinatereferencesystem_legacy_p.h:24
Qgis::CrsAxisDirection::AwayFrom
@ AwayFrom
Away from.
CUSTOM_CRS_VALIDATION
void(* CUSTOM_CRS_VALIDATION)(QgsCoordinateReferenceSystem &)
Definition: qgscoordinatereferencesystem.h:70
QgsCoordinateReferenceSystem::setupESRIWktFix
static Q_DECL_DEPRECATED void setupESRIWktFix()
Make sure that ESRI WKT import is done properly.
Definition: qgscoordinatereferencesystem.cpp:364
QgsReadWriteLocker::Write
@ Write
Lock for write.
Definition: qgsreadwritelocker.h:76
QgsCoordinateReferenceSystem::nativeFormat
Qgis::CrsDefinitionFormat nativeFormat() const
Returns the native format for the CRS definition.
Definition: qgscoordinatereferencesystem.cpp:2169
Qgis::CrsAxisDirection::DisplayRight
@ DisplayRight
Display right.
Qgis::CrsAxisDirection::EastSouthEast
@ EastSouthEast
East South East.
testIsGeographic
bool testIsGeographic(PJ *crs)
Definition: qgscoordinatereferencesystem.cpp:2201
QgsCoordinateReferenceSystem::toWkt
QString toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
Definition: qgscoordinatereferencesystem.cpp:1810
sqlite3_statement_unique_ptr::columnCount
int columnCount() const
Gets the number of columns that this statement returns.
Definition: qgssqliteutils.cpp:56
QgsUnitTypes::DistanceFeet
@ DistanceFeet
Imperial feet.
Definition: qgsunittypes.h:71
QgsRectangle::setXMinimum
void setXMinimum(double x) SIP_HOLDGIL
Set the minimum x value.
Definition: qgsrectangle.h:151
QgsMessageLog::logMessage
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Definition: qgsmessagelog.cpp:27
QgsCoordinateReferenceSystem::validSrsIds
static QList< long > validSrsIds()
Returns a list of all valid SRS IDs present in the CRS database.
Definition: qgscoordinatereferencesystem.cpp:139
QgsCoordinateReferenceSystem::isValid
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Definition: qgscoordinatereferencesystem.cpp:977
Qgis::CrsAxisDirection::RowPositive
@ RowPositive
Row positive.
QgsLogger::warning
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:122
QgsUnitTypes::DistanceMeters
@ DistanceMeters
Meters.
Definition: qgsunittypes.h:69
QgsCoordinateReferenceSystem::QgsCoordinateReferenceSystem
QgsCoordinateReferenceSystem()
Constructs an invalid CRS object.
Definition: qgscoordinatereferencesystem.cpp:103
QgsCoordinateReferenceSystem::geographicCrsAuthId
QString geographicCrsAuthId() const
Returns auth id of related geographic CRS.
Definition: qgscoordinatereferencesystem.cpp:2759
QgsCoordinateReferenceSystem::operator!=
bool operator!=(const QgsCoordinateReferenceSystem &srs) const
Overloaded != operator used to compare to CRS's.
Definition: qgscoordinatereferencesystem.cpp:1805
Qgis::CrsAxisDirection::NorthWest
@ NorthWest
North West.
Qgis::CrsAxisDirection::Aft
@ Aft
Aft.
Qgis::CrsAxisDirection::GeocentricX
@ GeocentricX
Geocentric (X)
QgsSettings::setValue
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Definition: qgssettings.cpp:279
QgsCoordinateReferenceSystem::projObject
PJ * projObject() const
Returns the underlying PROJ PJ object corresponding to the CRS, or nullptr if the CRS is invalid.
Definition: qgscoordinatereferencesystem.cpp:2776
QgsCoordinateReferenceSystem::setNativeFormat
void setNativeFormat(Qgis::CrsDefinitionFormat format)
Sets the native format for the CRS definition.
Definition: qgscoordinatereferencesystem.cpp:2164
QgsProjUtils::FlagMatchBoundCrsToUnderlyingSourceCrs
@ FlagMatchBoundCrsToUnderlyingSourceCrs
Allow matching a BoundCRS object to its underlying SourceCRS.
Definition: qgsprojutils.h:121
StringCrsCacheHash
QHash< QString, QgsCoordinateReferenceSystem > StringCrsCacheHash
Definition: qgscoordinatereferencesystem.cpp:60
QgsCoordinateReferenceSystem::toProj
QString toProj() const
Returns a Proj string representation of this CRS.
Definition: qgscoordinatereferencesystem.cpp:1293
QgsCoordinateReferenceSystem::validate
void validate()
Perform some validation on this CRS.
Definition: qgscoordinatereferencesystem.cpp:502
QgsCoordinateReferenceSystem::fromProj4
static Q_DECL_DEPRECATED QgsCoordinateReferenceSystem fromProj4(const QString &proj4)
Creates a CRS from a proj style formatted string.
Definition: qgscoordinatereferencesystem.cpp:216
QgsCoordinateReferenceSystem::operation
QgsProjOperation operation() const
Returns information about the PROJ operation associated with the coordinate reference system,...
Definition: qgscoordinatereferencesystem.cpp:1440
sqlite3_database_unique_ptr::open_v2
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
Definition: qgssqliteutils.cpp:86
QgsCoordinateReferenceSystem
This class represents a coordinate reference system (CRS).
Definition: qgscoordinatereferencesystem.h:211
QgsRectangle::setXMaximum
void setXMaximum(double x) SIP_HOLDGIL
Set the maximum x value.
Definition: qgsrectangle.h:156
QgsCoordinateReferenceSystemRegistry::addUserCrs
long addUserCrs(const QgsCoordinateReferenceSystem &crs, const QString &name, Qgis::CrsDefinitionFormat nativeFormat=Qgis::CrsDefinitionFormat::Wkt)
Adds a new crs definition as a custom ("USER") CRS.
Definition: qgscoordinatereferencesystemregistry.cpp:82
QgsCoordinateReferenceSystem::setCustomCrsValidation
static void setCustomCrsValidation(CUSTOM_CRS_VALIDATION f)
Sets custom function to force valid CRS.
Definition: qgscoordinatereferencesystem.cpp:2117
QgsProjectionFactors
contains various cartographic properties, such as scale factors, angular distortion and meridian conv...
Definition: qgsprojectionfactors.h:30
sqlite3_statement_unique_ptr::columnAsText
QString columnAsText(int column) const
Returns the column value from the current statement row as a string.
Definition: qgssqliteutils.cpp:61
QgsCoordinateReferenceSystem::createFromString
bool createFromString(const QString &definition)
Set up this CRS from a string definition.
Definition: qgscoordinatereferencesystem.cpp:269
QgsCoordinateReferenceSystem::invalidateCache
static void invalidateCache(bool disableCache=false)
Clears the internal cache used to initialize QgsCoordinateReferenceSystem objects.
Definition: qgscoordinatereferencesystem.cpp:2921
QgsRectangle::setYMaximum
void setYMaximum(double y) SIP_HOLDGIL
Set the maximum y value.
Definition: qgsrectangle.h:166
QgsCoordinateReferenceSystem::WKT2_2015_SIMPLIFIED
@ WKT2_2015_SIMPLIFIED
Same as WKT2_2015 with the following exceptions: UNIT keyword used. ID node only on top element....
Definition: qgscoordinatereferencesystem.h:674
QgsCoordinateReferenceSystem::ShortString
@ ShortString
A heavily abbreviated string, for use when a compact representation is required.
Definition: qgscoordinatereferencesystem.h:632
Qgis::CrsAxisDirection::NorthNorthEast
@ NorthNorthEast
North North East.
QgsCoordinateReferenceSystem::createFromProj
bool createFromProj(const QString &projString, bool identify=true)
Sets this CRS by passing it a PROJ style formatted string.
Definition: qgscoordinatereferencesystem.cpp:987
QgsCoordinateReferenceSystem::mapUnits
QgsUnitTypes::DistanceUnit mapUnits
Definition: qgscoordinatereferencesystem.h:215
operator>
bool operator>(const QgsCoordinateReferenceSystem &c1, const QgsCoordinateReferenceSystem &c2)
Definition: qgscoordinatereferencesystem.cpp:2979
QgsRectangle::setYMinimum
void setYMinimum(double y) SIP_HOLDGIL
Set the minimum y value.
Definition: qgsrectangle.h:161
c
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
Definition: porting_processing.dox:1
QgsUnitTypes::DistanceMillimeters
@ DistanceMillimeters
Millimeters.
Definition: qgsunittypes.h:77
QgsCoordinateReferenceSystem::WKT1_GDAL
@ WKT1_GDAL
WKT1 as traditionally output by GDAL, deriving from OGC 01-009. A notable departure from WKT1_GDAL wi...
Definition: qgscoordinatereferencesystem.h:671
QgsCoordinateReferenceSystem::fromProjObject
static QgsCoordinateReferenceSystem fromProjObject(PJ *object)
Constructs a QgsCoordinateReferenceSystem from a PROJ PJ object.
Definition: qgscoordinatereferencesystem.cpp:2781
QgsUnitTypes::DistanceCentimeters
@ DistanceCentimeters
Centimeters.
Definition: qgsunittypes.h:76
QgsCoordinateReferenceSystem::datumEnsemble
QgsDatumEnsemble datumEnsemble() const SIP_THROW(QgsNotSupportedException)
Attempts to retrieve datum ensemble details from the CRS.
Definition: qgscoordinatereferencesystem.cpp:1355
qgsprojutils.h
QgsCoordinateReferenceSystem::WKT2_2019
@ WKT2_2019
Full WKT2 string, conforming to ISO 19162:2019 / OGC 18-010, with all possible nodes and new keyword ...
Definition: qgscoordinatereferencesystem.h:677
QgsCoordinateReferenceSystem::toProj4
Q_DECL_DEPRECATED QString toProj4() const
Returns a Proj string representation of this CRS.
Definition: qgscoordinatereferencesystem.cpp:1288
qgssettings.h
sqlite3_statement_unique_ptr::columnName
QString columnName(int column) const
Returns the name of column.
Definition: qgssqliteutils.cpp:46
QgsApplication::qgisUserDatabaseFilePath
static QString qgisUserDatabaseFilePath()
Returns the path to the user qgis.db file.
Definition: qgsapplication.cpp:1104
QgsUnitTypes::DistanceYards
@ DistanceYards
Imperial yards.
Definition: qgsunittypes.h:73
QgsCoordinateReferenceSystem::createFromOgcWmsCrs
bool createFromOgcWmsCrs(const QString &crs)
Sets this CRS to the given OGC WMS-format Coordinate Reference Systems.
Definition: qgscoordinatereferencesystem.cpp:385
Qgis::CrsAxisDirection::ColumnNegative
@ ColumnNegative
Column negative.
QgsCoordinateReferenceSystem::authid
QString authid
Definition: qgscoordinatereferencesystem.h:217
QgsUnitTypes::DistanceMiles
@ DistanceMiles
Terrestrial miles.
Definition: qgsunittypes.h:74
QgsReadWriteLocker::Unlocked
@ Unlocked
Unlocked.
Definition: qgsreadwritelocker.h:77
QgsCoordinateReferenceSystem::fromEpsgId
static Q_INVOKABLE QgsCoordinateReferenceSystem fromEpsgId(long epsg)
Creates a CRS from a given EPSG ID.
Definition: qgscoordinatereferencesystem.cpp:202
QgsCoordinateReferenceSystem::ellipsoidAcronym
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the CRS.
Definition: qgscoordinatereferencesystem.cpp:1251
QgsUnitTypes::DistanceNauticalMiles
@ DistanceNauticalMiles
Nautical miles.
Definition: qgsunittypes.h:72
qgscoordinatereferencesystem_p.h
QgsCoordinateReferenceSystem::setValidationHint
void setValidationHint(const QString &html)
Set user hint for validation.
Definition: qgscoordinatereferencesystem.cpp:2149
QgsNotSupportedException
Custom exception class which is raised when an operation is not supported.
Definition: qgsexception.h:117
Qgis::CrsAxisDirection
CrsAxisDirection
Data provider flags.
Definition: qgis.h:1051
QgsCoordinateReferenceSystem::coordinateEpoch
double coordinateEpoch() const
Returns the coordinate epoch, as a decimal year.
Definition: qgscoordinatereferencesystem.cpp:1350
Qgis::CrsAxisDirection::Up
@ Up
Up.
qgslogger.h
Qgis::CrsAxisDirection::WestSouthWest
@ WestSouthWest
West South West.
Qgis::CrsAxisDirection::EastNorthEast
@ EastNorthEast
East North East.
Qgis::CrsAxisDirection::ColumnPositive
@ ColumnPositive
Column positive.
operator>=
bool operator>=(const QgsCoordinateReferenceSystem &c1, const QgsCoordinateReferenceSystem &c2)
Definition: qgscoordinatereferencesystem.cpp:3081
QgsCoordinateReferenceSystem::createFromSrid
Q_DECL_DEPRECATED bool createFromSrid(long srid)
Sets this CRS by lookup of the given PostGIS SRID in the CRS database.
Definition: qgscoordinatereferencesystem.cpp:512
QgsCoordinateReferenceSystem::EpsgCrsId
@ EpsgCrsId
EPSG code.
Definition: qgscoordinatereferencesystem.h:227
QgsCoordinateReferenceSystem::updateDefinition
void updateDefinition()
Updates the definition and parameters of the coordinate reference system to their latest values.
Definition: qgscoordinatereferencesystem.cpp:1498
QgsCoordinateReferenceSystem::setCoordinateEpoch
void setCoordinateEpoch(double epoch)
Sets the coordinate epoch, as a decimal year.
Definition: qgscoordinatereferencesystem.cpp:1338
sqlite3_database_unique_ptr
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
Definition: qgssqliteutils.h:118
QgsCoordinateReferenceSystem::postgisSrid
long postgisSrid() const
Returns PostGIS SRID for the CRS.
Definition: qgscoordinatereferencesystem.cpp:1190
Qgis::CrsAxisDirection::Port
@ Port
Port.
Q_NOWARN_DEPRECATED_PUSH
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:2819
QgsProjUtils::identifyCrs
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...
Definition: qgsprojutils.cpp:236
operator<=
bool operator<=(const QgsCoordinateReferenceSystem &c1, const QgsCoordinateReferenceSystem &c2)
Definition: qgscoordinatereferencesystem.cpp:3085
qgscoordinatereferencesystem.h
QgsCoordinateReferenceSystem::MediumString
@ MediumString
A medium-length string, recommended for general purpose use.
Definition: qgscoordinatereferencesystem.h:633
qgscoordinatereferencesystemregistry.h
QgsCoordinateReferenceSystem::WKT2_2015
@ WKT2_2015
Full WKT2 string, conforming to ISO 19162:2015(E) / OGC 12-063r5 with all possible nodes and new keyw...
Definition: qgscoordinatereferencesystem.h:673
QgsCoordinateReferenceSystem::fromWkt
static QgsCoordinateReferenceSystem fromWkt(const QString &wkt)
Creates a CRS from a WKT spatial ref sys definition string.
Definition: qgscoordinatereferencesystem.cpp:228
operator<
bool operator<(const QgsCoordinateReferenceSystem &c1, const QgsCoordinateReferenceSystem &c2)
Definition: qgscoordinatereferencesystem.cpp:3030
QgsPoint::x
double x
Definition: qgspoint.h:69
Qgis::CrsAxisDirection::Forward
@ Forward
Forward.
Qgis::CrsAxisDirection::Down
@ Down
Down.
Qgis::CrsAxisDirection::North
@ North
North.
sqlite3_statement_unique_ptr
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
Definition: qgssqliteutils.h:69
Qgis::CrsAxisDirection::Clockwise
@ Clockwise
Clockwise.
QgsCoordinateReferenceSystem::PostgisCrsId
@ PostgisCrsId
SRID used in PostGIS. DEPRECATED – DO NOT USE.
Definition: qgscoordinatereferencesystem.h:226
SrIdCrsCacheHash
QHash< long, QgsCoordinateReferenceSystem > SrIdCrsCacheHash
Definition: qgscoordinatereferencesystem.cpp:59
QgsDatumEnsemble
Contains information about a datum ensemble.
Definition: qgsdatums.h:94
Qgis::CrsAxisDirection::NorthEast
@ NorthEast
North East.
QgsCoordinateReferenceSystem::operator=
QgsCoordinateReferenceSystem & operator=(const QgsCoordinateReferenceSystem &srs)
Assignment operator.
Definition: qgscoordinatereferencesystem.cpp:131
Qgis::CrsAxisDirection::SouthEast
@ SouthEast
South East.
QgsCoordinateReferenceSystem::toGeographicCrs
QgsCoordinateReferenceSystem toGeographicCrs() const
Returns the geographic CRS associated with this CRS object.
Definition: qgscoordinatereferencesystem.cpp:2730
PJ
struct PJconsts PJ
Definition: qgscoordinatereferencesystem.h:49
QgsCoordinateReferenceSystem::recentCoordinateReferenceSystems
static QList< QgsCoordinateReferenceSystem > recentCoordinateReferenceSystems()
Returns a list of recently used CRS.
Definition: qgscoordinatereferencesystem.cpp:2859
QgsCoordinateReferenceSystem::IdentifierType
IdentifierType
Type of identifier string to create.
Definition: qgscoordinatereferencesystem.h:630
qgsmessagelog.h
qgscoordinatereferencesystem_legacy_p.h
Qgis::CrsAxisDirection::Starboard
@ Starboard
Starboard.