QGIS API Documentation  2.99.0-Master (314842d)
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 
21 #include <cmath>
22 
23 #include <QDir>
24 #include <QTemporaryFile>
25 #include <QDomNode>
26 #include <QDomElement>
27 #include <QFileInfo>
28 #include <QRegExp>
29 #include <QTextStream>
30 #include <QFile>
31 
32 #include "qgsapplication.h"
33 #include "qgslogger.h"
34 #include "qgsmessagelog.h"
35 #include "qgis.h" //const vals declared here
36 #include "qgslocalec.h"
37 #include "qgssettings.h"
38 
39 #include <sqlite3.h>
40 #include <proj_api.h>
41 
42 //gdal and ogr includes (needed for == operator)
43 #include <ogr_srs_api.h>
44 #include <cpl_error.h>
45 #include <cpl_conv.h>
46 #include <cpl_csv.h>
47 
48 CUSTOM_CRS_VALIDATION QgsCoordinateReferenceSystem::mCustomSrsValidation = nullptr;
49 
50 QReadWriteLock QgsCoordinateReferenceSystem::sSrIdCacheLock;
51 QHash< long, QgsCoordinateReferenceSystem > QgsCoordinateReferenceSystem::sSrIdCache;
52 QReadWriteLock QgsCoordinateReferenceSystem::sOgcLock;
53 QHash< QString, QgsCoordinateReferenceSystem > QgsCoordinateReferenceSystem::sOgcCache;
54 QReadWriteLock QgsCoordinateReferenceSystem::sProj4CacheLock;
55 QHash< QString, QgsCoordinateReferenceSystem > QgsCoordinateReferenceSystem::sProj4Cache;
56 QReadWriteLock QgsCoordinateReferenceSystem::sCRSWktLock;
57 QHash< QString, QgsCoordinateReferenceSystem > QgsCoordinateReferenceSystem::sWktCache;
58 QReadWriteLock QgsCoordinateReferenceSystem::sCRSSrsIdLock;
59 QHash< long, QgsCoordinateReferenceSystem > QgsCoordinateReferenceSystem::sSrsIdCache;
60 QReadWriteLock QgsCoordinateReferenceSystem::sCrsStringLock;
61 QHash< QString, QgsCoordinateReferenceSystem > QgsCoordinateReferenceSystem::sStringCache;
62 
63 //--------------------------
64 
66 {
67  d = new QgsCoordinateReferenceSystemPrivate();
68 }
69 
71 {
72  d = new QgsCoordinateReferenceSystemPrivate();
73  createFromString( definition );
74 }
75 
77 {
78  d = new QgsCoordinateReferenceSystemPrivate();
79  createFromId( id, type );
80 }
81 
83  : d( srs.d )
84 {
85 }
86 
88 {
89  d = srs.d;
90  return *this;
91 }
92 
94 {
95  QList<long> results;
96  // check both standard & user defined projection databases
97  QStringList dbs = QStringList() << QgsApplication::srsDatabaseFilePath() << QgsApplication::qgisUserDatabaseFilePath();
98 
99  Q_FOREACH ( const QString &db, dbs )
100  {
101  QFileInfo myInfo( db );
102  if ( !myInfo.exists() )
103  {
104  QgsDebugMsg( "failed : " + db + " does not exist!" );
105  continue;
106  }
107 
108  sqlite3 *database = nullptr;
109  const char *tail = nullptr;
110  sqlite3_stmt *statement = nullptr;
111 
112  //check the db is available
113  int result = openDatabase( db, &database );
114  if ( result != SQLITE_OK )
115  {
116  QgsDebugMsg( "failed : " + db + " could not be opened!" );
117  continue;
118  }
119 
120  QString sql = "select srs_id from tbl_srs";
121  ( void )sqlite3_prepare( database, sql.toUtf8(),
122  sql.toUtf8().length(),
123  &statement, &tail );
124  while ( true )
125  {
126  // this one is an infinitive loop, intended to fetch any row
127  int ret = sqlite3_step( statement );
128 
129  if ( ret == SQLITE_DONE )
130  {
131  // there are no more rows to fetch - we can stop looping
132  break;
133  }
134 
135  if ( ret == SQLITE_ROW )
136  {
137  results.append( sqlite3_column_int( statement, 0 ) );
138  }
139  else
140  {
141  QgsMessageLog::logMessage( QObject::tr( "SQLite error: %2\nSQL: %1" ).arg( sql, sqlite3_errmsg( database ) ), QObject::tr( "SpatiaLite" ) );
142  break;
143  }
144  }
145 
146  sqlite3_finalize( statement );
147  sqlite3_close( database );
148  }
149  std::sort( results.begin(), results.end() );
150  return results;
151 }
152 
154 {
156  crs.createFromOgcWmsCrs( ogcCrs );
157  return crs;
158 }
159 
161 {
162  return fromOgcWmsCrs( "EPSG:" + QString::number( epsg ) );
163 }
164 
166 {
168  crs.createFromProj4( proj4 );
169  return crs;
170 }
171 
173 {
175  crs.createFromWkt( wkt );
176  return crs;
177 }
178 
180 {
182  crs.createFromSrsId( srsId );
183  return crs;
184 }
185 
187 {
188 }
189 
191 {
192  bool result = false;
193  switch ( type )
194  {
195  case InternalCrsId:
196  result = createFromSrsId( id );
197  break;
198  case PostgisCrsId:
199  result = createFromSrid( id );
200  break;
201  case EpsgCrsId:
202  result = createFromOgcWmsCrs( QStringLiteral( "EPSG:%1" ).arg( id ) );
203  break;
204  default:
205  //THIS IS BAD...THIS PART OF CODE SHOULD NEVER BE REACHED...
206  QgsDebugMsg( "Unexpected case reached!" );
207  };
208  return result;
209 }
210 
211 bool QgsCoordinateReferenceSystem::createFromString( const QString &definition )
212 {
213  sCrsStringLock.lockForRead();
214  QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sStringCache.constFind( definition );
215  if ( crsIt != sStringCache.constEnd() )
216  {
217  // found a match in the cache
218  *this = crsIt.value();
219  sCrsStringLock.unlock();
220  return true;
221  }
222  sCrsStringLock.unlock();
223 
224  bool result = false;
225  QRegExp reCrsId( "^(epsg|postgis|internal)\\:(\\d+)$", Qt::CaseInsensitive );
226  if ( reCrsId.indexIn( definition ) == 0 )
227  {
228  QString authName = reCrsId.cap( 1 ).toLower();
229  CrsType type = InternalCrsId;
230  if ( authName == QLatin1String( "epsg" ) )
231  type = EpsgCrsId;
232  if ( authName == QLatin1String( "postgis" ) )
233  type = PostgisCrsId;
234  long id = reCrsId.cap( 2 ).toLong();
235  result = createFromId( id, type );
236  }
237  else
238  {
239  QRegExp reCrsStr( "^(?:(wkt|proj4)\\:)?(.+)$", Qt::CaseInsensitive );
240  if ( reCrsStr.indexIn( definition ) == 0 )
241  {
242  if ( reCrsStr.cap( 1 ).toLower() == QLatin1String( "proj4" ) )
243  {
244  result = createFromProj4( reCrsStr.cap( 2 ) );
245  //TODO: createFromProj4 used to save to the user database any new CRS
246  // this behavior was changed in order to separate creation and saving.
247  // Not sure if it necessary to save it here, should be checked by someone
248  // familiar with the code (should also give a more descriptive name to the generated CRS)
249  if ( srsid() == 0 )
250  {
251  QString myName = QStringLiteral( " * %1 (%2)" )
252  .arg( QObject::tr( "Generated CRS", "A CRS automatically generated from layer info get this prefix for description" ),
253  toProj4() );
254  saveAsUserCrs( myName );
255  }
256  }
257  else
258  {
259  result = createFromWkt( reCrsStr.cap( 2 ) );
260  }
261  }
262  }
263 
264  sCrsStringLock.lockForWrite();
265  sStringCache.insert( definition, *this );
266  sCrsStringLock.unlock();
267  return result;
268 }
269 
270 bool QgsCoordinateReferenceSystem::createFromUserInput( const QString &definition )
271 {
272  QString userWkt;
273  char *wkt = nullptr;
274  OGRSpatialReferenceH crs = OSRNewSpatialReference( nullptr );
275 
276  // make sure towgs84 parameter is loaded if using an ESRI definition and gdal >= 1.9
277  if ( definition.startsWith( QLatin1String( "ESRI::" ) ) )
278  {
279  setupESRIWktFix();
280  }
281 
282  if ( OSRSetFromUserInput( crs, definition.toLocal8Bit().constData() ) == OGRERR_NONE )
283  {
284  if ( OSRExportToWkt( crs, &wkt ) == OGRERR_NONE )
285  {
286  userWkt = wkt;
287  CPLFree( wkt );
288  }
289  OSRDestroySpatialReference( crs );
290  }
291  //QgsDebugMsg( "definition: " + definition + " wkt = " + wkt );
292  return createFromWkt( userWkt );
293 }
294 
296 {
297  // make sure towgs84 parameter is loaded if gdal >= 1.9
298  // this requires setting GDAL_FIX_ESRI_WKT=GEOGCS (see qgis bug #5598 and gdal bug #4673)
299  const char *configOld = CPLGetConfigOption( "GDAL_FIX_ESRI_WKT", "" );
300  const char *configNew = "GEOGCS";
301  // only set if it was not set, to let user change the value if needed
302  if ( strcmp( configOld, "" ) == 0 )
303  {
304  CPLSetConfigOption( "GDAL_FIX_ESRI_WKT", configNew );
305  if ( strcmp( configNew, CPLGetConfigOption( "GDAL_FIX_ESRI_WKT", "" ) ) != 0 )
306  QgsLogger::warning( QStringLiteral( "GDAL_FIX_ESRI_WKT could not be set to %1 : %2" )
307  .arg( configNew, CPLGetConfigOption( "GDAL_FIX_ESRI_WKT", "" ) ) );
308  QgsDebugMsg( QString( "set GDAL_FIX_ESRI_WKT : %1" ).arg( configNew ) );
309  }
310  else
311  {
312  QgsDebugMsg( QString( "GDAL_FIX_ESRI_WKT was already set : %1" ).arg( configNew ) );
313  }
314 }
315 
317 {
318  sOgcLock.lockForRead();
319  QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sOgcCache.constFind( crs );
320  if ( crsIt != sOgcCache.constEnd() )
321  {
322  // found a match in the cache
323  *this = crsIt.value();
324  sOgcLock.unlock();
325  return true;
326  }
327  sOgcLock.unlock();
328 
329  QString wmsCrs = crs;
330 
331  QRegExp re( "urn:ogc:def:crs:([^:]+).+([^:]+)", Qt::CaseInsensitive );
332  if ( re.exactMatch( wmsCrs ) )
333  {
334  wmsCrs = re.cap( 1 ) + ':' + re.cap( 2 );
335  }
336  else
337  {
338  re.setPattern( QStringLiteral( "(user|custom|qgis):(\\d+)" ) );
339  if ( re.exactMatch( wmsCrs ) && createFromSrsId( re.cap( 2 ).toInt() ) )
340  {
341  sOgcLock.lockForWrite();
342  sOgcCache.insert( crs, *this );
343  sOgcLock.unlock();
344  return true;
345  }
346  }
347 
348  if ( loadFromDatabase( QgsApplication::srsDatabaseFilePath(), QStringLiteral( "lower(auth_name||':'||auth_id)" ), wmsCrs.toLower() ) )
349  {
350  sOgcLock.lockForWrite();
351  sOgcCache.insert( crs, *this );
352  sOgcLock.unlock();
353  return true;
354  }
355 
356  // NAD27
357  if ( wmsCrs.compare( QLatin1String( "CRS:27" ), Qt::CaseInsensitive ) == 0 ||
358  wmsCrs.compare( QLatin1String( "OGC:CRS27" ), Qt::CaseInsensitive ) == 0 )
359  {
360  // TODO: verify same axis orientation
361  return createFromOgcWmsCrs( QStringLiteral( "EPSG:4267" ) );
362  }
363 
364  // NAD83
365  if ( wmsCrs.compare( QLatin1String( "CRS:83" ), Qt::CaseInsensitive ) == 0 ||
366  wmsCrs.compare( QLatin1String( "OGC:CRS83" ), Qt::CaseInsensitive ) == 0 )
367  {
368  // TODO: verify same axis orientation
369  return createFromOgcWmsCrs( QStringLiteral( "EPSG:4269" ) );
370  }
371 
372  // WGS84
373  if ( wmsCrs.compare( QLatin1String( "CRS:84" ), Qt::CaseInsensitive ) == 0 ||
374  wmsCrs.compare( QLatin1String( "OGC:CRS84" ), Qt::CaseInsensitive ) == 0 )
375  {
376  createFromOgcWmsCrs( QStringLiteral( "EPSG:4326" ) );
377 
378  d.detach();
379  d->mAxisInverted = false;
380  d->mAxisInvertedDirty = false;
381 
382  sOgcLock.lockForWrite();
383  sOgcCache.insert( crs, *this );
384  sOgcLock.unlock();
385 
386  return d->mIsValid;
387  }
388 
389  sOgcLock.lockForWrite();
390  sOgcCache.insert( crs, QgsCoordinateReferenceSystem() );
391  sOgcLock.unlock();
392  return false;
393 }
394 
395 // Misc helper functions -----------------------
396 
397 
399 {
400  if ( d->mIsValid )
401  return;
402 
403  d.detach();
404 
405  // try to validate using custom validation routines
406  if ( mCustomSrsValidation )
407  mCustomSrsValidation( *this );
408 
409  if ( !d->mIsValid )
410  {
412  }
413 }
414 
416 {
417  sSrIdCacheLock.lockForRead();
418  QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrIdCache.constFind( id );
419  if ( crsIt != sSrIdCache.constEnd() )
420  {
421  // found a match in the cache
422  *this = crsIt.value();
423  sSrIdCacheLock.unlock();
424  return true;
425  }
426  sSrIdCacheLock.unlock();
427 
428  bool result = loadFromDatabase( QgsApplication::srsDatabaseFilePath(), QStringLiteral( "srid" ), QString::number( id ) );
429 
430  sSrIdCacheLock.lockForWrite();
431  sSrIdCache.insert( id, *this );
432  sSrIdCacheLock.unlock();
433 
434  return result;
435 }
436 
438 {
439  sCRSSrsIdLock.lockForRead();
440  QHash< long, QgsCoordinateReferenceSystem >::const_iterator crsIt = sSrsIdCache.constFind( id );
441  if ( crsIt != sSrsIdCache.constEnd() )
442  {
443  // found a match in the cache
444  *this = crsIt.value();
445  sCRSSrsIdLock.unlock();
446  return true;
447  }
448  sCRSSrsIdLock.unlock();
449 
450  bool result = loadFromDatabase( id < USER_CRS_START_ID ? QgsApplication::srsDatabaseFilePath() :
452  QStringLiteral( "srs_id" ), QString::number( id ) );
453 
454  sCRSSrsIdLock.lockForWrite();
455  sSrsIdCache.insert( id, *this );
456  sCRSSrsIdLock.unlock();
457 
458  return result;
459 }
460 
461 bool QgsCoordinateReferenceSystem::loadFromDatabase( const QString &db, const QString &expression, const QString &value )
462 {
463  d.detach();
464 
465  QgsDebugMsgLevel( "load CRS from " + db + " where " + expression + " is " + value, 3 );
466  d->mIsValid = false;
467  d->mWkt.clear();
468 
469  QFileInfo myInfo( db );
470  if ( !myInfo.exists() )
471  {
472  QgsDebugMsg( "failed : " + db + " does not exist!" );
473  return d->mIsValid;
474  }
475 
476  sqlite3 *myDatabase = nullptr;
477  const char *myTail = nullptr;
478  sqlite3_stmt *myPreparedStatement = nullptr;
479  int myResult;
480  //check the db is available
481  myResult = openDatabase( db, &myDatabase );
482  if ( myResult != SQLITE_OK )
483  {
484  QgsDebugMsg( "failed : " + db + " could not be opened!" );
485  return d->mIsValid;
486  }
487 
488  /*
489  srs_id INTEGER PRIMARY KEY,
490  description text NOT NULL,
491  projection_acronym text NOT NULL,
492  ellipsoid_acronym NOT NULL,
493  parameters text NOT NULL,
494  srid integer NOT NULL,
495  auth_name varchar NOT NULL,
496  auth_id integer NOT NULL,
497  is_geo integer NOT NULL);
498  */
499 
500  QString mySql = "select srs_id,description,projection_acronym,"
501  "ellipsoid_acronym,parameters,srid,auth_name||':'||auth_id,is_geo "
502  "from tbl_srs where " + expression + '=' + quotedValue( value ) + " order by deprecated";
503  myResult = sqlite3_prepare( myDatabase, mySql.toUtf8(),
504  mySql.toUtf8().length(),
505  &myPreparedStatement, &myTail );
506  // XXX Need to free memory from the error msg if one is set
507  if ( myResult == SQLITE_OK && sqlite3_step( myPreparedStatement ) == SQLITE_ROW )
508  {
509  d->mSrsId = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text(
510  myPreparedStatement, 0 ) ) ).toLong();
511  d->mDescription = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text(
512  myPreparedStatement, 1 ) ) );
513  d->mProjectionAcronym = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( myPreparedStatement, 2 ) ) );
514  d->mEllipsoidAcronym = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( myPreparedStatement, 3 ) ) );
515  d->mProj4 = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( myPreparedStatement, 4 ) ) );
516  d->mSRID = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( myPreparedStatement, 5 ) ) ).toLong() ;
517  d->mAuthId = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( myPreparedStatement, 6 ) ) );
518  d->mIsGeographic = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( myPreparedStatement, 7 ) ) ).toInt() != 0;
519  d->mAxisInvertedDirty = true;
520 
521  if ( d->mSrsId >= USER_CRS_START_ID && d->mAuthId.isEmpty() )
522  {
523  d->mAuthId = QStringLiteral( "USER:%1" ).arg( d->mSrsId );
524  }
525  else if ( d->mAuthId.startsWith( QLatin1String( "EPSG:" ), Qt::CaseInsensitive ) )
526  {
527  OSRDestroySpatialReference( d->mCRS );
528  d->mCRS = OSRNewSpatialReference( nullptr );
529  d->mIsValid = OSRSetFromUserInput( d->mCRS, d->mAuthId.toLower().toLatin1() ) == OGRERR_NONE;
530  setMapUnits();
531  }
532 
533  if ( !d->mIsValid )
534  {
535  setProj4String( d->mProj4 );
536  }
537  }
538  else
539  {
540  QgsDebugMsg( "failed : " + mySql );
541  }
542  sqlite3_finalize( myPreparedStatement );
543  sqlite3_close( myDatabase );
544  return d->mIsValid;
545 }
546 
548 {
549  if ( d->mAxisInvertedDirty )
550  {
551  OGRAxisOrientation orientation;
552  OSRGetAxis( d->mCRS, OSRIsGeographic( d->mCRS ) ? "GEOGCS" : "PROJCS", 0, &orientation );
553 
554  // If axis orientation is unknown, try again with OSRImportFromEPSGA for EPSG crs
555  if ( orientation == OAO_Other && d->mAuthId.startsWith( QLatin1String( "EPSG:" ), Qt::CaseInsensitive ) )
556  {
557  OGRSpatialReferenceH crs = OSRNewSpatialReference( nullptr );
558 
559  if ( OSRImportFromEPSGA( crs, d->mAuthId.midRef( 5 ).toInt() ) == OGRERR_NONE )
560  {
561  OSRGetAxis( crs, OSRIsGeographic( crs ) ? "GEOGCS" : "PROJCS", 0, &orientation );
562  }
563 
564  OSRDestroySpatialReference( crs );
565  }
566 
567  d->mAxisInverted = orientation == OAO_North;
568  d->mAxisInvertedDirty = false;
569  }
570 
571  return d->mAxisInverted;
572 }
573 
575 {
576  d.detach();
577 
578  sCRSWktLock.lockForRead();
579  QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sWktCache.constFind( wkt );
580  if ( crsIt != sWktCache.constEnd() )
581  {
582  // found a match in the cache
583  *this = crsIt.value();
584  sCRSWktLock.unlock();
585  return true;
586  }
587  sCRSWktLock.unlock();
588 
589  d->mIsValid = false;
590  d->mWkt.clear();
591  d->mProj4.clear();
592 
593  if ( wkt.isEmpty() )
594  {
595  QgsDebugMsg( "theWkt is uninitialized, operation failed" );
596  return d->mIsValid;
597  }
598  QByteArray ba = wkt.toLatin1();
599  const char *pWkt = ba.data();
600 
601  OGRErr myInputResult = OSRImportFromWkt( d->mCRS, const_cast< char ** >( & pWkt ) );
602 
603  if ( myInputResult != OGRERR_NONE )
604  {
605  QgsDebugMsg( "\n---------------------------------------------------------------" );
606  QgsDebugMsg( "This CRS could *** NOT *** be set from the supplied Wkt " );
607  QgsDebugMsg( "INPUT: " + wkt );
608  QgsDebugMsg( QString( "UNUSED WKT: %1" ).arg( pWkt ) );
609  QgsDebugMsg( "---------------------------------------------------------------\n" );
610 
611  sCRSWktLock.lockForWrite();
612  sWktCache.insert( wkt, *this );
613  sCRSWktLock.unlock();
614  return d->mIsValid;
615  }
616 
617  if ( OSRAutoIdentifyEPSG( d->mCRS ) == OGRERR_NONE )
618  {
619  QString authid = QStringLiteral( "%1:%2" )
620  .arg( OSRGetAuthorityName( d->mCRS, nullptr ),
621  OSRGetAuthorityCode( d->mCRS, nullptr ) );
622  bool result = createFromOgcWmsCrs( authid );
623  sCRSWktLock.lockForWrite();
624  sWktCache.insert( wkt, *this );
625  sCRSWktLock.unlock();
626  return result;
627  }
628 
629  // always morph from esri as it doesn't hurt anything
630  // FW: Hey, that's not right! It can screw stuff up! Disable
631  //myOgrSpatialRef.morphFromESRI();
632 
633  // create the proj4 structs needed for transforming
634  char *proj4src = nullptr;
635  OSRExportToProj4( d->mCRS, &proj4src );
636 
637  //now that we have the proj4string, delegate to createFromProj4 so
638  // that we can try to fill in the remaining class members...
639  //create from Proj will set the isValidFlag
640  if ( !createFromProj4( proj4src ) )
641  {
642  CPLFree( proj4src );
643 
644  // try fixed up version
645  OSRFixup( d->mCRS );
646 
647  OSRExportToProj4( d->mCRS, &proj4src );
648 
649  createFromProj4( proj4src );
650  }
651  //TODO: createFromProj4 used to save to the user database any new CRS
652  // this behavior was changed in order to separate creation and saving.
653  // Not sure if it necessary to save it here, should be checked by someone
654  // familiar with the code (should also give a more descriptive name to the generated CRS)
655  if ( d->mSrsId == 0 )
656  {
657  QString myName = QStringLiteral( " * %1 (%2)" )
658  .arg( QObject::tr( "Generated CRS", "A CRS automatically generated from layer info get this prefix for description" ),
659  toProj4() );
660  saveAsUserCrs( myName );
661  }
662 
663  CPLFree( proj4src );
664 
665  sCRSWktLock.lockForWrite();
666  sWktCache.insert( wkt, *this );
667  sCRSWktLock.unlock();
668 
669  return d->mIsValid;
670  //setMapunits will be called by createfromproj above
671 }
672 
674 {
675  return d->mIsValid;
676 }
677 
678 bool QgsCoordinateReferenceSystem::createFromProj4( const QString &proj4String )
679 {
680  d.detach();
681 
682  sProj4CacheLock.lockForRead();
683  QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sProj4Cache.constFind( proj4String );
684  if ( crsIt != sProj4Cache.constEnd() )
685  {
686  // found a match in the cache
687  *this = crsIt.value();
688  sProj4CacheLock.unlock();
689  return true;
690  }
691  sProj4CacheLock.unlock();
692 
693  //
694  // Examples:
695  // +proj=tmerc +lat_0=0 +lon_0=-62 +k=0.999500 +x_0=400000 +y_0=0
696  // +ellps=clrk80 +towgs84=-255,-15,71,0,0,0,0 +units=m +no_defs
697  //
698  // +proj=lcc +lat_1=46.8 +lat_0=46.8 +lon_0=2.337229166666664 +k_0=0.99987742
699  // +x_0=600000 +y_0=2200000 +a=6378249.2 +b=6356515.000000472 +units=m +no_defs
700  //
701  QString myProj4String = proj4String.trimmed();
702  d->mIsValid = false;
703  d->mWkt.clear();
704 
705  QRegExp myProjRegExp( "\\+proj=(\\S+)" );
706  int myStart = myProjRegExp.indexIn( myProj4String );
707  if ( myStart == -1 )
708  {
709  sProj4CacheLock.lockForWrite();
710  sProj4Cache.insert( proj4String, *this );
711  sProj4CacheLock.unlock();
712 
713  return d->mIsValid;
714  }
715 
716  d->mProjectionAcronym = myProjRegExp.cap( 1 );
717 
718  QRegExp myEllipseRegExp( "\\+ellps=(\\S+)" );
719  myStart = myEllipseRegExp.indexIn( myProj4String );
720  if ( myStart == -1 )
721  {
722  d->mEllipsoidAcronym.clear();
723  }
724  else
725  {
726  d->mEllipsoidAcronym = myEllipseRegExp.cap( 1 );
727  }
728 
729  QRegExp myAxisRegExp( "\\+a=(\\S+)" );
730  myStart = myAxisRegExp.indexIn( myProj4String );
731 
732  long mySrsId = 0;
733  QgsCoordinateReferenceSystem::RecordMap myRecord;
734 
735  /*
736  * We try to match the proj string to and srsid using the following logic:
737  * - perform a whole text search on proj4 string (if not null)
738  */
739  myRecord = getRecord( "select * from tbl_srs where parameters=" + quotedValue( myProj4String ) + " order by deprecated" );
740  if ( myRecord.empty() )
741  {
742  // Ticket #722 - aaronr
743  // Check if we can swap the lat_1 and lat_2 params (if they exist) to see if we match...
744  // First we check for lat_1 and lat_2
745  QRegExp myLat1RegExp( "\\+lat_1=\\S+" );
746  QRegExp myLat2RegExp( "\\+lat_2=\\S+" );
747  int myStart1 = 0;
748  int myLength1 = 0;
749  int myStart2 = 0;
750  int myLength2 = 0;
751  QString lat1Str;
752  QString lat2Str;
753  myStart1 = myLat1RegExp.indexIn( myProj4String, myStart1 );
754  myStart2 = myLat2RegExp.indexIn( myProj4String, myStart2 );
755  if ( myStart1 != -1 && myStart2 != -1 )
756  {
757  myLength1 = myLat1RegExp.matchedLength();
758  myLength2 = myLat2RegExp.matchedLength();
759  lat1Str = myProj4String.mid( myStart1 + LAT_PREFIX_LEN, myLength1 - LAT_PREFIX_LEN );
760  lat2Str = myProj4String.mid( myStart2 + LAT_PREFIX_LEN, myLength2 - LAT_PREFIX_LEN );
761  }
762  // If we found the lat_1 and lat_2 we need to swap and check to see if we can find it...
763  if ( !lat1Str.isEmpty() && !lat2Str.isEmpty() )
764  {
765  // Make our new string to check...
766  QString proj4StringModified = myProj4String;
767  // First just swap in the lat_2 value for lat_1 value
768  proj4StringModified.replace( myStart1 + LAT_PREFIX_LEN, myLength1 - LAT_PREFIX_LEN, lat2Str );
769  // Now we have to find the lat_2 location again since it has potentially moved...
770  myStart2 = 0;
771  myStart2 = myLat2RegExp.indexIn( proj4String, myStart2 );
772  proj4StringModified.replace( myStart2 + LAT_PREFIX_LEN, myLength2 - LAT_PREFIX_LEN, lat1Str );
773  QgsDebugMsg( "trying proj4string match with swapped lat_1,lat_2" );
774  myRecord = getRecord( "select * from tbl_srs where parameters=" + quotedValue( proj4StringModified.trimmed() ) + " order by deprecated" );
775  }
776  }
777 
778  if ( myRecord.empty() )
779  {
780  // match all parameters individually:
781  // - order of parameters doesn't matter
782  // - found definition may have more parameters (like +towgs84 in GDAL)
783  // - retry without datum, if no match is found (looks like +datum<>WGS84 was dropped in GDAL)
784 
785  QString sql = QStringLiteral( "SELECT * FROM tbl_srs WHERE " );
786  QString delim;
787  QString datum;
788 
789  // split on spaces followed by a plus sign (+) to deal
790  // also with parameters containing spaces (e.g. +nadgrids)
791  // make sure result is trimmed (#5598)
792  QStringList myParams;
793  Q_FOREACH ( const QString &param, myProj4String.split( QRegExp( "\\s+(?=\\+)" ), QString::SkipEmptyParts ) )
794  {
795  QString arg = QStringLiteral( "' '||parameters||' ' LIKE %1" ).arg( quotedValue( QStringLiteral( "% %1 %" ).arg( param.trimmed() ) ) );
796  if ( param.startsWith( QLatin1String( "+datum=" ) ) )
797  {
798  datum = arg;
799  }
800  else
801  {
802  sql += delim + arg;
803  delim = QStringLiteral( " AND " );
804  myParams << param.trimmed();
805  }
806  }
807 
808  if ( !datum.isEmpty() )
809  {
810  myRecord = getRecord( sql + delim + datum + " order by deprecated" );
811  }
812 
813  if ( myRecord.empty() )
814  {
815  // datum might have disappeared in definition - retry without it
816  myRecord = getRecord( sql + " order by deprecated" );
817  }
818 
819  if ( !myRecord.empty() )
820  {
821  // Bugfix 8487 : test param lists are equal, except for +datum
822  QStringList foundParams;
823  Q_FOREACH ( const QString &param, myRecord["parameters"].split( QRegExp( "\\s+(?=\\+)" ), QString::SkipEmptyParts ) )
824  {
825  if ( !param.startsWith( QLatin1String( "+datum=" ) ) )
826  foundParams << param.trimmed();
827  }
828 
829  myParams.sort();
830  foundParams.sort();
831 
832  if ( myParams != foundParams )
833  {
834  myRecord.clear();
835  }
836  }
837  }
838 
839  if ( !myRecord.empty() )
840  {
841  mySrsId = myRecord[QStringLiteral( "srs_id" )].toLong();
842  if ( mySrsId > 0 )
843  {
844  createFromSrsId( mySrsId );
845  }
846  }
847  else
848  {
849  // Last ditch attempt to piece together what we know of the projection to find a match...
850  setProj4String( myProj4String );
851  mySrsId = findMatchingProj();
852  if ( mySrsId > 0 )
853  {
854  createFromSrsId( mySrsId );
855  }
856  else
857  {
858  d->mIsValid = false;
859  }
860  }
861 
862  // if we failed to look up the projection in database, don't worry. we can still use it :)
863  if ( !d->mIsValid )
864  {
865  QgsDebugMsg( "Projection is not found in databases." );
866  //setProj4String will set mIsValidFlag to true if there is no issue
867  setProj4String( myProj4String );
868  }
869 
870  sProj4CacheLock.lockForWrite();
871  sProj4Cache.insert( proj4String, *this );
872  sProj4CacheLock.unlock();
873 
874  return d->mIsValid;
875 }
876 
877 //private method meant for internal use by this class only
878 QgsCoordinateReferenceSystem::RecordMap QgsCoordinateReferenceSystem::getRecord( const QString &sql )
879 {
880  QString myDatabaseFileName;
881  QgsCoordinateReferenceSystem::RecordMap myMap;
882  QString myFieldName;
883  QString myFieldValue;
884  sqlite3 *myDatabase = nullptr;
885  const char *myTail = nullptr;
886  sqlite3_stmt *myPreparedStatement = nullptr;
887  int myResult;
888 
889  // Get the full path name to the sqlite3 spatial reference database.
890  myDatabaseFileName = QgsApplication::srsDatabaseFilePath();
891  QFileInfo myInfo( myDatabaseFileName );
892  if ( !myInfo.exists() )
893  {
894  QgsDebugMsg( "failed : " + myDatabaseFileName + " does not exist!" );
895  return myMap;
896  }
897 
898  //check the db is available
899  myResult = openDatabase( myDatabaseFileName, &myDatabase );
900  if ( myResult != SQLITE_OK )
901  {
902  return myMap;
903  }
904 
905  myResult = sqlite3_prepare( myDatabase, sql.toUtf8(), sql.toUtf8().length(), &myPreparedStatement, &myTail );
906  // XXX Need to free memory from the error msg if one is set
907  if ( myResult == SQLITE_OK && sqlite3_step( myPreparedStatement ) == SQLITE_ROW )
908  {
909  int myColumnCount = sqlite3_column_count( myPreparedStatement );
910  //loop through each column in the record adding its expression name and value to the map
911  for ( int myColNo = 0; myColNo < myColumnCount; myColNo++ )
912  {
913  myFieldName = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_name( myPreparedStatement, myColNo ) ) );
914  myFieldValue = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( myPreparedStatement, myColNo ) ) );
915  myMap[myFieldName] = myFieldValue;
916  }
917  if ( sqlite3_step( myPreparedStatement ) != SQLITE_DONE )
918  {
919  QgsDebugMsg( "Multiple records found in srs.db" );
920  myMap.clear();
921  }
922  }
923  else
924  {
925  QgsDebugMsg( "failed : " + sql );
926  }
927 
928  if ( myMap.empty() )
929  {
930  sqlite3_finalize( myPreparedStatement );
931  sqlite3_close( myDatabase );
932 
933  myDatabaseFileName = QgsApplication::qgisUserDatabaseFilePath();
934  QFileInfo myFileInfo;
935  myFileInfo.setFile( myDatabaseFileName );
936  if ( !myFileInfo.exists() )
937  {
938  QgsDebugMsg( "user qgis.db not found" );
939  return myMap;
940  }
941 
942  //check the db is available
943  myResult = openDatabase( myDatabaseFileName, &myDatabase );
944  if ( myResult != SQLITE_OK )
945  {
946  return myMap;
947  }
948 
949  myResult = sqlite3_prepare( myDatabase, sql.toUtf8(), sql.toUtf8().length(), &myPreparedStatement, &myTail );
950  // XXX Need to free memory from the error msg if one is set
951  if ( myResult == SQLITE_OK && sqlite3_step( myPreparedStatement ) == SQLITE_ROW )
952  {
953  int myColumnCount = sqlite3_column_count( myPreparedStatement );
954  //loop through each column in the record adding its field name and value to the map
955  for ( int myColNo = 0; myColNo < myColumnCount; myColNo++ )
956  {
957  myFieldName = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_name( myPreparedStatement, myColNo ) ) );
958  myFieldValue = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( myPreparedStatement, myColNo ) ) );
959  myMap[myFieldName] = myFieldValue;
960  }
961 
962  if ( sqlite3_step( myPreparedStatement ) != SQLITE_DONE )
963  {
964  QgsDebugMsg( "Multiple records found in srs.db" );
965  myMap.clear();
966  }
967  }
968  else
969  {
970  QgsDebugMsg( "failed : " + sql );
971  }
972  }
973  sqlite3_finalize( myPreparedStatement );
974  sqlite3_close( myDatabase );
975 
976  return myMap;
977 }
978 
979 // Accessors -----------------------------------
980 
982 {
983  return d->mSrsId;
984 }
985 
987 {
988  return d->mSRID;
989 }
990 
992 {
993  return d->mAuthId;
994 }
995 
997 {
998  if ( d->mDescription.isNull() )
999  {
1000  return QString();
1001  }
1002  else
1003  {
1004  return d->mDescription;
1005  }
1006 }
1007 
1009 {
1010  if ( d->mProjectionAcronym.isNull() )
1011  {
1012  return QString();
1013  }
1014  else
1015  {
1016  return d->mProjectionAcronym;
1017  }
1018 }
1019 
1021 {
1022  if ( d->mEllipsoidAcronym.isNull() )
1023  {
1024  return QString();
1025  }
1026  else
1027  {
1028  return d->mEllipsoidAcronym;
1029  }
1030 }
1031 
1033 {
1034  if ( !d->mIsValid )
1035  return QString();
1036 
1037  if ( d->mProj4.isEmpty() )
1038  {
1039  char *proj4src = nullptr;
1040  OSRExportToProj4( d->mCRS, &proj4src );
1041  d->mProj4 = proj4src;
1042  CPLFree( proj4src );
1043  }
1044  // Stray spaces at the end?
1045  return d->mProj4.trimmed();
1046 }
1047 
1049 {
1050  return d->mIsGeographic;
1051 }
1052 
1054 {
1055  if ( !d->mIsValid )
1057 
1058  return d->mMapUnits;
1059 }
1060 
1061 
1062 // Mutators -----------------------------------
1063 
1064 
1065 void QgsCoordinateReferenceSystem::setInternalId( long srsId )
1066 {
1067  d.detach();
1068  d->mSrsId = srsId;
1069 }
1070 void QgsCoordinateReferenceSystem::setAuthId( const QString &authId )
1071 {
1072  d.detach();
1073  d->mAuthId = authId;
1074 }
1075 void QgsCoordinateReferenceSystem::setSrid( long srid )
1076 {
1077  d.detach();
1078  d->mSRID = srid;
1079 }
1080 void QgsCoordinateReferenceSystem::setDescription( const QString &description )
1081 {
1082  d.detach();
1083  d->mDescription = description;
1084 }
1085 void QgsCoordinateReferenceSystem::setProj4String( const QString &proj4String )
1086 {
1087  d.detach();
1088  d->mProj4 = proj4String;
1089 
1090  QgsLocaleNumC l;
1091 
1092  OSRDestroySpatialReference( d->mCRS );
1093  d->mCRS = OSRNewSpatialReference( nullptr );
1094  d->mIsValid = OSRImportFromProj4( d->mCRS, proj4String.trimmed().toLatin1().constData() ) == OGRERR_NONE;
1095  // OSRImportFromProj4() may accept strings that are not valid proj.4 strings,
1096  // e.g if they lack a +ellps parameter, it will automatically add +ellps=WGS84, but as
1097  // we use the original mProj4 with QgsCoordinateTransform, it will fail to initialize
1098  // so better detect it now.
1099  projPJ proj = pj_init_plus( proj4String.trimmed().toLatin1().constData() );
1100  if ( !proj )
1101  {
1102  QgsDebugMsg( "proj.4 string rejected by pj_init_plus()" );
1103  d->mIsValid = false;
1104  }
1105  else
1106  {
1107  pj_free( proj );
1108  }
1109  d->mWkt.clear();
1110  setMapUnits();
1111 }
1112 
1113 void QgsCoordinateReferenceSystem::setGeographicFlag( bool geoFlag )
1114 {
1115  d.detach();
1116  d->mIsGeographic = geoFlag;
1117 }
1118 void QgsCoordinateReferenceSystem::setEpsg( long epsg )
1119 {
1120  d.detach();
1121  d->mAuthId = QStringLiteral( "EPSG:%1" ).arg( epsg );
1122 }
1123 void QgsCoordinateReferenceSystem::setProjectionAcronym( const QString &projectionAcronym )
1124 {
1125  d.detach();
1126  d->mProjectionAcronym = projectionAcronym;
1127 }
1128 void QgsCoordinateReferenceSystem::setEllipsoidAcronym( const QString &ellipsoidAcronym )
1129 {
1130  d.detach();
1131  d->mEllipsoidAcronym = ellipsoidAcronym;
1132 }
1133 
1134 void QgsCoordinateReferenceSystem::setMapUnits()
1135 {
1136  d.detach();
1137  if ( !d->mIsValid )
1138  {
1139  d->mMapUnits = QgsUnitTypes::DistanceUnknownUnit;
1140  return;
1141  }
1142 
1143  char *unitName = nullptr;
1144 
1145  // Of interest to us is that this call adds in a unit parameter if
1146  // one doesn't already exist.
1147  OSRFixup( d->mCRS );
1148 
1149  if ( OSRIsProjected( d->mCRS ) )
1150  {
1151  double toMeter = OSRGetLinearUnits( d->mCRS, &unitName );
1152  QString unit( unitName );
1153 
1154  // If the units parameter was created during the Fixup() call
1155  // above, the name of the units is likely to be 'unknown'. Try to
1156  // do better than that ... (but perhaps ogr should be enhanced to
1157  // do this instead?).
1158 
1159  static const double FEET_TO_METER = 0.3048;
1160  static const double SMALL_NUM = 1e-3;
1161 
1162  if ( qAbs( toMeter - FEET_TO_METER ) < SMALL_NUM )
1163  unit = QStringLiteral( "Foot" );
1164 
1165  if ( qgsDoubleNear( toMeter, 1.0 ) ) //Unit name for meters would be "metre"
1166  d->mMapUnits = QgsUnitTypes::DistanceMeters;
1167  else if ( unit == QLatin1String( "Foot" ) )
1168  d->mMapUnits = QgsUnitTypes::DistanceFeet;
1169  else
1170  {
1171  d->mMapUnits = QgsUnitTypes::DistanceUnknownUnit;
1172  }
1173  }
1174  else
1175  {
1176  OSRGetAngularUnits( d->mCRS, &unitName );
1177  QString unit( unitName );
1178  if ( unit == QLatin1String( "degree" ) )
1179  d->mMapUnits = QgsUnitTypes::DistanceDegrees;
1180  else
1181  {
1182  d->mMapUnits = QgsUnitTypes::DistanceUnknownUnit;
1183  }
1184  }
1185 }
1186 
1187 
1189 {
1190  if ( d->mEllipsoidAcronym.isNull() || d->mProjectionAcronym.isNull()
1191  || !d->mIsValid )
1192  {
1193  QgsDebugMsg( "QgsCoordinateReferenceSystem::findMatchingProj will only "
1194  "work if prj acr ellipsoid acr and proj4string are set"
1195  " and the current projection is valid!" );
1196  return 0;
1197  }
1198 
1199  sqlite3 *myDatabase = nullptr;
1200  const char *myTail = nullptr;
1201  sqlite3_stmt *myPreparedStatement = nullptr;
1202  int myResult;
1203 
1204  // Set up the query to retrieve the projection information
1205  // needed to populate the list
1206  QString mySql = QString( "select srs_id,parameters from tbl_srs where "
1207  "projection_acronym=%1 and ellipsoid_acronym=%2 order by deprecated" )
1208  .arg( quotedValue( d->mProjectionAcronym ),
1209  quotedValue( d->mEllipsoidAcronym ) );
1210  // Get the full path name to the sqlite3 spatial reference database.
1211  QString myDatabaseFileName = QgsApplication::srsDatabaseFilePath();
1212 
1213  //check the db is available
1214  myResult = openDatabase( myDatabaseFileName, &myDatabase );
1215  if ( myResult != SQLITE_OK )
1216  {
1217  return 0;
1218  }
1219 
1220  myResult = sqlite3_prepare( myDatabase, mySql.toUtf8(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
1221 // XXX Need to free memory from the error msg if one is set
1222  if ( myResult == SQLITE_OK )
1223  {
1224 
1225  while ( sqlite3_step( myPreparedStatement ) == SQLITE_ROW )
1226  {
1227  QString mySrsId = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( myPreparedStatement, 0 ) ) );
1228  QString myProj4String = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( myPreparedStatement, 1 ) ) );
1229  if ( toProj4() == myProj4String.trimmed() )
1230  {
1231  // close the sqlite3 statement
1232  sqlite3_finalize( myPreparedStatement );
1233  sqlite3_close( myDatabase );
1234  return mySrsId.toLong();
1235  }
1236  }
1237  }
1238  // close the sqlite3 statement
1239  sqlite3_finalize( myPreparedStatement );
1240  sqlite3_close( myDatabase );
1241  //
1242  // Try the users db now
1243  //
1244 
1245  myDatabaseFileName = QgsApplication::qgisUserDatabaseFilePath();
1246  //check the db is available
1247  myResult = openDatabase( myDatabaseFileName, &myDatabase );
1248  if ( myResult != SQLITE_OK )
1249  {
1250  return 0;
1251  }
1252 
1253  myResult = sqlite3_prepare( myDatabase, mySql.toUtf8(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
1254 // XXX Need to free memory from the error msg if one is set
1255  if ( myResult == SQLITE_OK )
1256  {
1257 
1258  while ( sqlite3_step( myPreparedStatement ) == SQLITE_ROW )
1259  {
1260  QString mySrsId = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( myPreparedStatement, 0 ) ) );
1261  QString myProj4String = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( myPreparedStatement, 1 ) ) );
1262  if ( toProj4() == myProj4String.trimmed() )
1263  {
1264  // close the sqlite3 statement
1265  sqlite3_finalize( myPreparedStatement );
1266  sqlite3_close( myDatabase );
1267  return mySrsId.toLong();
1268  }
1269  }
1270  }
1271 
1272  // close the sqlite3 statement
1273  sqlite3_finalize( myPreparedStatement );
1274  sqlite3_close( myDatabase );
1275  return 0;
1276 }
1277 
1279 {
1280  return ( !d->mIsValid && !srs.d->mIsValid ) ||
1281  ( d->mIsValid && srs.d->mIsValid && srs.authid() == authid() );
1282 }
1283 
1285 {
1286  return !( *this == srs );
1287 }
1288 
1290 {
1291  if ( d->mWkt.isEmpty() )
1292  {
1293  char *wkt = nullptr;
1294  if ( OSRExportToWkt( d->mCRS, &wkt ) == OGRERR_NONE )
1295  {
1296  d->mWkt = wkt;
1297  CPLFree( wkt );
1298  }
1299  }
1300  return d->mWkt;
1301 }
1302 
1303 bool QgsCoordinateReferenceSystem::readXml( const QDomNode &node )
1304 {
1305  d.detach();
1306  bool result = true;
1307  QDomNode srsNode = node.namedItem( QStringLiteral( "spatialrefsys" ) );
1308 
1309  if ( ! srsNode.isNull() )
1310  {
1311  bool initialized = false;
1312 
1313  long srsid = srsNode.namedItem( QStringLiteral( "srsid" ) ).toElement().text().toLong();
1314 
1315  QDomNode myNode;
1316 
1317  if ( srsid < USER_CRS_START_ID )
1318  {
1319  myNode = srsNode.namedItem( QStringLiteral( "authid" ) );
1320  if ( !myNode.isNull() )
1321  {
1322  operator=( QgsCoordinateReferenceSystem::fromOgcWmsCrs( myNode.toElement().text() ) );
1323  if ( isValid() )
1324  {
1325  initialized = true;
1326  }
1327  }
1328 
1329  if ( !initialized )
1330  {
1331  myNode = srsNode.namedItem( QStringLiteral( "epsg" ) );
1332  if ( !myNode.isNull() )
1333  {
1334  operator=( QgsCoordinateReferenceSystem::fromEpsgId( myNode.toElement().text().toLong() ) );
1335  if ( isValid() )
1336  {
1337  initialized = true;
1338  }
1339  }
1340  }
1341  }
1342 
1343  if ( !initialized )
1344  {
1345  myNode = srsNode.namedItem( QStringLiteral( "proj4" ) );
1346 
1347  if ( !createFromProj4( myNode.toElement().text() ) )
1348  {
1349  // Setting from elements one by one
1350 
1351  myNode = srsNode.namedItem( QStringLiteral( "proj4" ) );
1352  setProj4String( myNode.toElement().text() );
1353 
1354  myNode = srsNode.namedItem( QStringLiteral( "srsid" ) );
1355  setInternalId( myNode.toElement().text().toLong() );
1356 
1357  myNode = srsNode.namedItem( QStringLiteral( "srid" ) );
1358  setSrid( myNode.toElement().text().toLong() );
1359 
1360  myNode = srsNode.namedItem( QStringLiteral( "authid" ) );
1361  setAuthId( myNode.toElement().text() );
1362 
1363  myNode = srsNode.namedItem( QStringLiteral( "description" ) );
1364  setDescription( myNode.toElement().text() );
1365 
1366  myNode = srsNode.namedItem( QStringLiteral( "projectionacronym" ) );
1367  setProjectionAcronym( myNode.toElement().text() );
1368 
1369  myNode = srsNode.namedItem( QStringLiteral( "ellipsoidacronym" ) );
1370  setEllipsoidAcronym( myNode.toElement().text() );
1371 
1372  myNode = srsNode.namedItem( QStringLiteral( "geographicflag" ) );
1373  if ( myNode.toElement().text().compare( QLatin1String( "true" ) ) )
1374  {
1375  setGeographicFlag( true );
1376  }
1377  else
1378  {
1379  setGeographicFlag( false );
1380  }
1381 
1382  //make sure the map units have been set
1383  setMapUnits();
1384 
1385  [email protected] this srs needs to be validated!!!
1386  d->mIsValid = true; //shamelessly hard coded for now
1387  }
1388  //TODO: createFromProj4 used to save to the user database any new CRS
1389  // this behavior was changed in order to separate creation and saving.
1390  // Not sure if it necessary to save it here, should be checked by someone
1391  // familiar with the code (should also give a more descriptive name to the generated CRS)
1392  if ( d->mSrsId == 0 )
1393  {
1394  QString myName = QStringLiteral( " * %1 (%2)" )
1395  .arg( QObject::tr( "Generated CRS", "A CRS automatically generated from layer info get this prefix for description" ),
1396  toProj4() );
1397  saveAsUserCrs( myName );
1398  }
1399 
1400  }
1401  }
1402  else
1403  {
1404  // Return default CRS if none was found in the XML.
1406  result = false;
1407  }
1408  return result;
1409 }
1410 
1411 bool QgsCoordinateReferenceSystem::writeXml( QDomNode &node, QDomDocument &doc ) const
1412 {
1413 
1414  QDomElement myLayerNode = node.toElement();
1415  QDomElement mySrsElement = doc.createElement( QStringLiteral( "spatialrefsys" ) );
1416 
1417  QDomElement myProj4Element = doc.createElement( QStringLiteral( "proj4" ) );
1418  myProj4Element.appendChild( doc.createTextNode( toProj4() ) );
1419  mySrsElement.appendChild( myProj4Element );
1420 
1421  QDomElement mySrsIdElement = doc.createElement( QStringLiteral( "srsid" ) );
1422  mySrsIdElement.appendChild( doc.createTextNode( QString::number( srsid() ) ) );
1423  mySrsElement.appendChild( mySrsIdElement );
1424 
1425  QDomElement mySridElement = doc.createElement( QStringLiteral( "srid" ) );
1426  mySridElement.appendChild( doc.createTextNode( QString::number( postgisSrid() ) ) );
1427  mySrsElement.appendChild( mySridElement );
1428 
1429  QDomElement myEpsgElement = doc.createElement( QStringLiteral( "authid" ) );
1430  myEpsgElement.appendChild( doc.createTextNode( authid() ) );
1431  mySrsElement.appendChild( myEpsgElement );
1432 
1433  QDomElement myDescriptionElement = doc.createElement( QStringLiteral( "description" ) );
1434  myDescriptionElement.appendChild( doc.createTextNode( description() ) );
1435  mySrsElement.appendChild( myDescriptionElement );
1436 
1437  QDomElement myProjectionAcronymElement = doc.createElement( QStringLiteral( "projectionacronym" ) );
1438  myProjectionAcronymElement.appendChild( doc.createTextNode( projectionAcronym() ) );
1439  mySrsElement.appendChild( myProjectionAcronymElement );
1440 
1441  QDomElement myEllipsoidAcronymElement = doc.createElement( QStringLiteral( "ellipsoidacronym" ) );
1442  myEllipsoidAcronymElement.appendChild( doc.createTextNode( ellipsoidAcronym() ) );
1443  mySrsElement.appendChild( myEllipsoidAcronymElement );
1444 
1445  QDomElement myGeographicFlagElement = doc.createElement( QStringLiteral( "geographicflag" ) );
1446  QString myGeoFlagText = QStringLiteral( "false" );
1447  if ( isGeographic() )
1448  {
1449  myGeoFlagText = QStringLiteral( "true" );
1450  }
1451 
1452  myGeographicFlagElement.appendChild( doc.createTextNode( myGeoFlagText ) );
1453  mySrsElement.appendChild( myGeographicFlagElement );
1454 
1455  myLayerNode.appendChild( mySrsElement );
1456 
1457  return true;
1458 }
1459 
1460 
1461 
1462 //
1463 // Static helper methods below this point only please!
1464 //
1465 
1466 
1467 // Returns the whole proj4 string for the selected srsid
1468 //this is a static method! NOTE I've made it private for now to reduce API clutter TS
1469 QString QgsCoordinateReferenceSystem::proj4FromSrsId( const int srsId )
1470 {
1471 
1472  QString myDatabaseFileName;
1473  QString myProjString;
1474  QString mySql = QStringLiteral( "select parameters from tbl_srs where srs_id = %1 order by deprecated" ).arg( srsId );
1475 
1476  //
1477  // Determine if this is a user projection or a system on
1478  // user projection defs all have srs_id >= 100000
1479  //
1480  if ( srsId >= USER_CRS_START_ID )
1481  {
1482  myDatabaseFileName = QgsApplication::qgisUserDatabaseFilePath();
1483  QFileInfo myFileInfo;
1484  myFileInfo.setFile( myDatabaseFileName );
1485  if ( !myFileInfo.exists() ) //its unlikely that this condition will ever be reached
1486  {
1487  QgsDebugMsg( "users qgis.db not found" );
1488  return QString();
1489  }
1490  }
1491  else //must be a system projection then
1492  {
1493  myDatabaseFileName = QgsApplication::srsDatabaseFilePath();
1494  }
1495 
1496  sqlite3 *db = nullptr;
1497  int rc;
1498  rc = openDatabase( myDatabaseFileName, &db );
1499  if ( rc )
1500  {
1501  return QString();
1502  }
1503  // prepare the sql statement
1504  const char *pzTail = nullptr;
1505  sqlite3_stmt *ppStmt = nullptr;
1506 
1507  rc = sqlite3_prepare( db, mySql.toUtf8(), mySql.toUtf8().length(), &ppStmt, &pzTail );
1508  // XXX Need to free memory from the error msg if one is set
1509 
1510  if ( rc == SQLITE_OK )
1511  {
1512  if ( sqlite3_step( ppStmt ) == SQLITE_ROW )
1513  {
1514  myProjString = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, 0 ) ) );
1515  }
1516  }
1517  // close the statement
1518  sqlite3_finalize( ppStmt );
1519  // close the database
1520  sqlite3_close( db );
1521 
1522  //Q_ASSERT(myProjString.length() > 0);
1523  return myProjString;
1524 }
1525 
1526 int QgsCoordinateReferenceSystem::openDatabase( const QString &path, sqlite3 **db, bool readonly )
1527 {
1528  int myResult = readonly
1529  ? sqlite3_open_v2( path.toUtf8().data(), db, SQLITE_OPEN_READONLY, nullptr )
1530  : sqlite3_open( path.toUtf8().data(), db );
1531 
1532  if ( myResult != SQLITE_OK )
1533  {
1534  QgsDebugMsg( "Can't open database: " + QString( sqlite3_errmsg( *db ) ) );
1535  // XXX This will likely never happen since on open, sqlite creates the
1536  // database if it does not exist.
1537  // ... unfortunately it happens on Windows
1538  QgsMessageLog::logMessage( QObject::tr( "Could not open CRS database %1\nError(%2): %3" )
1539  .arg( path )
1540  .arg( myResult )
1541  .arg( sqlite3_errmsg( *db ) ), QObject::tr( "CRS" ) );
1542  }
1543  return myResult;
1544 }
1545 
1547 {
1548  mCustomSrsValidation = f;
1549 }
1550 
1552 {
1553  return mCustomSrsValidation;
1554 }
1555 
1556 void QgsCoordinateReferenceSystem::debugPrint()
1557 {
1558  QgsDebugMsg( "***SpatialRefSystem***" );
1559  QgsDebugMsg( "* Valid : " + ( d->mIsValid ? QString( "true" ) : QString( "false" ) ) );
1560  QgsDebugMsg( "* SrsId : " + QString::number( d->mSrsId ) );
1561  QgsDebugMsg( "* Proj4 : " + toProj4() );
1562  QgsDebugMsg( "* WKT : " + toWkt() );
1563  QgsDebugMsg( "* Desc. : " + d->mDescription );
1565  {
1566  QgsDebugMsg( "* Units : meters" );
1567  }
1568  else if ( mapUnits() == QgsUnitTypes::DistanceFeet )
1569  {
1570  QgsDebugMsg( "* Units : feet" );
1571  }
1572  else if ( mapUnits() == QgsUnitTypes::DistanceDegrees )
1573  {
1574  QgsDebugMsg( "* Units : degrees" );
1575  }
1576 }
1577 
1579 {
1580  d.detach();
1581  d->mValidationHint = html;
1582 }
1583 
1585 {
1586  return d->mValidationHint;
1587 }
1588 
1591 
1593 {
1594  if ( !d->mIsValid )
1595  {
1596  QgsDebugMsg( "Can't save an invalid CRS!" );
1597  return false;
1598  }
1599 
1600  QString mySql;
1601 
1602  QString proj4String = d->mProj4;
1603  if ( proj4String.isEmpty() )
1604  {
1605  proj4String = toProj4();
1606  }
1607 
1608  //if this is the first record we need to ensure that its srs_id is 10000. For
1609  //any rec after that sqlite3 will take care of the autonumbering
1610  //this was done to support sqlite 3.0 as it does not yet support
1611  //the autoinc related system tables.
1612  if ( getRecordCount() == 0 )
1613  {
1614  mySql = "insert into tbl_srs (srs_id,description,projection_acronym,ellipsoid_acronym,parameters,is_geo) values ("
1615  + QString::number( USER_CRS_START_ID )
1616  + ',' + quotedValue( name )
1617  + ',' + quotedValue( projectionAcronym() )
1618  + ',' + quotedValue( ellipsoidAcronym() )
1619  + ',' + quotedValue( toProj4() )
1620  + ",0)"; // <-- is_geo shamelessly hard coded for now
1621  }
1622  else
1623  {
1624  mySql = "insert into tbl_srs (description,projection_acronym,ellipsoid_acronym,parameters,is_geo) values ("
1625  + quotedValue( name )
1626  + ',' + quotedValue( projectionAcronym() )
1627  + ',' + quotedValue( ellipsoidAcronym() )
1628  + ',' + quotedValue( toProj4() )
1629  + ",0)"; // <-- is_geo shamelessly hard coded for now
1630  }
1631  sqlite3 *myDatabase = nullptr;
1632  const char *myTail = nullptr;
1633  sqlite3_stmt *myPreparedStatement = nullptr;
1634  int myResult;
1635  //check the db is available
1636  myResult = sqlite3_open( QgsApplication::qgisUserDatabaseFilePath().toUtf8().data(), &myDatabase );
1637  if ( myResult != SQLITE_OK )
1638  {
1639  QgsDebugMsg( QString( "Can't open or create database %1: %2" )
1641  sqlite3_errmsg( myDatabase ) ) );
1642  return false;
1643  }
1644  myResult = sqlite3_prepare( myDatabase, mySql.toUtf8(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
1645 
1646  qint64 return_id;
1647  if ( myResult == SQLITE_OK && sqlite3_step( myPreparedStatement ) == SQLITE_DONE )
1648  {
1649  QgsMessageLog::logMessage( QObject::tr( "Saved user CRS [%1]" ).arg( toProj4() ), QObject::tr( "CRS" ) );
1650 
1651  return_id = sqlite3_last_insert_rowid( myDatabase );
1652  setInternalId( return_id );
1653 
1654  //We add the just created user CRS to the list of recently used CRS
1655  QgsSettings settings;
1656  //QStringList recentProjections = settings.value( "/UI/recentProjections" ).toStringList();
1657  QStringList projectionsProj4 = settings.value( QStringLiteral( "UI/recentProjectionsProj4" ) ).toStringList();
1658  QStringList projectionsAuthId = settings.value( QStringLiteral( "UI/recentProjectionsAuthId" ) ).toStringList();
1659  //recentProjections.append();
1660  //settings.setValue( "/UI/recentProjections", recentProjections );
1661  projectionsProj4.append( toProj4() );
1662  projectionsAuthId.append( authid() );
1663  settings.setValue( QStringLiteral( "UI/recentProjectionsProj4" ), projectionsProj4 );
1664  settings.setValue( QStringLiteral( "UI/recentProjectionsAuthId" ), projectionsAuthId );
1665 
1666  }
1667  else
1668  return_id = -1;
1669  return return_id;
1670 }
1671 
1672 long QgsCoordinateReferenceSystem::getRecordCount()
1673 {
1674  sqlite3 *myDatabase = nullptr;
1675  const char *myTail = nullptr;
1676  sqlite3_stmt *myPreparedStatement = nullptr;
1677  int myResult;
1678  long myRecordCount = 0;
1679  //check the db is available
1680  myResult = sqlite3_open_v2( QgsApplication::qgisUserDatabaseFilePath().toUtf8().data(), &myDatabase, SQLITE_OPEN_READONLY, nullptr );
1681  if ( myResult != SQLITE_OK )
1682  {
1683  QgsDebugMsg( QString( "Can't open database: %1" ).arg( sqlite3_errmsg( myDatabase ) ) );
1684  return 0;
1685  }
1686  // Set up the query to retrieve the projection information needed to populate the ELLIPSOID list
1687  QString mySql = QStringLiteral( "select count(*) from tbl_srs" );
1688  myResult = sqlite3_prepare( myDatabase, mySql.toUtf8(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
1689  // XXX Need to free memory from the error msg if one is set
1690  if ( myResult == SQLITE_OK )
1691  {
1692  if ( sqlite3_step( myPreparedStatement ) == SQLITE_ROW )
1693  {
1694  QString myRecordCountString = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( myPreparedStatement, 0 ) ) );
1695  myRecordCount = myRecordCountString.toLong();
1696  }
1697  }
1698  // close the sqlite3 statement
1699  sqlite3_finalize( myPreparedStatement );
1700  sqlite3_close( myDatabase );
1701  return myRecordCount;
1702 }
1703 
1704 QString QgsCoordinateReferenceSystem::quotedValue( QString value )
1705 {
1706  value.replace( '\'', QLatin1String( "''" ) );
1707  return value.prepend( '\'' ).append( '\'' );
1708 }
1709 
1710 // adapted from gdal/ogr/ogr_srs_dict.cpp
1711 bool QgsCoordinateReferenceSystem::loadWkts( QHash<int, QString> &wkts, const char *filename )
1712 {
1713  qDebug( "Loading %s", filename );
1714  const char *pszFilename = CPLFindFile( "gdal", filename );
1715  if ( !pszFilename )
1716  return false;
1717 
1718  QFile csv( pszFilename );
1719  if ( !csv.open( QIODevice::ReadOnly ) )
1720  return false;
1721 
1722  QTextStream lines( &csv );
1723 
1724  for ( ;; )
1725  {
1726  QString line = lines.readLine();
1727  if ( line.isNull() )
1728  break;
1729 
1730  if ( line.startsWith( '#' ) )
1731  {
1732  continue;
1733  }
1734  else if ( line.startsWith( QLatin1String( "include " ) ) )
1735  {
1736  if ( !loadWkts( wkts, line.mid( 8 ).toUtf8() ) )
1737  break;
1738  }
1739  else
1740  {
1741  int pos = line.indexOf( ',' );
1742  if ( pos < 0 )
1743  return false;
1744 
1745  bool ok;
1746  int epsg = line.leftRef( pos ).toInt( &ok );
1747  if ( !ok )
1748  return false;
1749 
1750  wkts.insert( epsg, line.mid( pos + 1 ) );
1751  }
1752  }
1753 
1754  csv.close();
1755 
1756  return true;
1757 }
1758 
1759 bool QgsCoordinateReferenceSystem::loadIds( QHash<int, QString> &wkts )
1760 {
1761  OGRSpatialReferenceH crs = OSRNewSpatialReference( nullptr );
1762 
1763  Q_FOREACH ( const QString &csv, QStringList() << "gcs.csv" << "pcs.csv" << "vertcs.csv" << "compdcs.csv" << "geoccs.csv" )
1764  {
1765  QString filename = CPLFindFile( "gdal", csv.toUtf8() );
1766 
1767  QFile f( filename );
1768  if ( !f.open( QIODevice::ReadOnly ) )
1769  continue;
1770 
1771  QTextStream lines( &f );
1772  int l = 0, n = 0;
1773 
1774  lines.readLine();
1775  for ( ;; )
1776  {
1777  l++;
1778  QString line = lines.readLine();
1779  if ( line.isNull() )
1780  break;
1781 
1782  int pos = line.indexOf( ',' );
1783  if ( pos < 0 )
1784  continue;
1785 
1786  bool ok;
1787  int epsg = line.leftRef( pos ).toInt( &ok );
1788  if ( !ok )
1789  continue;
1790 
1791  // some CRS are known to fail (see http://trac.osgeo.org/gdal/ticket/2900)
1792  if ( epsg == 2218 || epsg == 2221 || epsg == 2296 || epsg == 2297 || epsg == 2298 || epsg == 2299 || epsg == 2300 || epsg == 2301 || epsg == 2302 ||
1793  epsg == 2303 || epsg == 2304 || epsg == 2305 || epsg == 2306 || epsg == 2307 || epsg == 2963 || epsg == 2985 || epsg == 2986 || epsg == 3052 ||
1794  epsg == 3053 || epsg == 3139 || epsg == 3144 || epsg == 3145 || epsg == 3173 || epsg == 3295 || epsg == 3993 || epsg == 4087 || epsg == 4088 ||
1795  epsg == 5017 || epsg == 5221 || epsg == 5224 || epsg == 5225 || epsg == 5514 || epsg == 5515 || epsg == 5516 || epsg == 5819 || epsg == 5820 ||
1796  epsg == 5821 || epsg == 6200 || epsg == 6201 || epsg == 6202 || epsg == 6244 || epsg == 6245 || epsg == 6246 || epsg == 6247 || epsg == 6248 ||
1797  epsg == 6249 || epsg == 6250 || epsg == 6251 || epsg == 6252 || epsg == 6253 || epsg == 6254 || epsg == 6255 || epsg == 6256 || epsg == 6257 ||
1798  epsg == 6258 || epsg == 6259 || epsg == 6260 || epsg == 6261 || epsg == 6262 || epsg == 6263 || epsg == 6264 || epsg == 6265 || epsg == 6266 ||
1799  epsg == 6267 || epsg == 6268 || epsg == 6269 || epsg == 6270 || epsg == 6271 || epsg == 6272 || epsg == 6273 || epsg == 6274 || epsg == 6275 ||
1800  epsg == 6966 || epsg == 7082 || epsg == 32600 || epsg == 32663 || epsg == 32700 )
1801  continue;
1802 
1803  if ( OSRImportFromEPSG( crs, epsg ) != OGRERR_NONE )
1804  {
1805  qDebug( "EPSG %d: not imported", epsg );
1806  continue;
1807  }
1808 
1809  char *wkt = nullptr;
1810  if ( OSRExportToWkt( crs, &wkt ) != OGRERR_NONE )
1811  {
1812  qWarning( "EPSG %d: not exported to WKT", epsg );
1813  continue;
1814  }
1815 
1816  wkts.insert( epsg, wkt );
1817  n++;
1818 
1819  CPLFree( wkt );
1820  }
1821 
1822  f.close();
1823 
1824  qDebug( "Loaded %d/%d from %s", n, l, filename.toUtf8().constData() );
1825  }
1826 
1827  OSRDestroySpatialReference( crs );
1828 
1829  return true;
1830 }
1831 
1833 {
1834  QString dbFilePath = QgsApplication::srsDatabaseFilePath();
1835  syncDatumTransform( dbFilePath );
1836 
1837  int inserted = 0, updated = 0, deleted = 0, errors = 0;
1838 
1839  qDebug( "Load srs db from: %s", QgsApplication::srsDatabaseFilePath().toLocal8Bit().constData() );
1840 
1841  sqlite3 *database = nullptr;
1842  if ( sqlite3_open( dbFilePath.toUtf8().constData(), &database ) != SQLITE_OK )
1843  {
1844  qCritical( "Could not open database: %s [%s]\n", QgsApplication::srsDatabaseFilePath().toLocal8Bit().constData(), sqlite3_errmsg( database ) );
1845  return -1;
1846  }
1847 
1848  if ( sqlite3_exec( database, "BEGIN TRANSACTION", nullptr, nullptr, nullptr ) != SQLITE_OK )
1849  {
1850  qCritical( "Could not begin transaction: %s [%s]\n", QgsApplication::srsDatabaseFilePath().toLocal8Bit().constData(), sqlite3_errmsg( database ) );
1851  return -1;
1852  }
1853 
1854  // fix up database, if not done already //
1855  if ( sqlite3_exec( database, "alter table tbl_srs add noupdate boolean", nullptr, nullptr, nullptr ) == SQLITE_OK )
1856  ( void )sqlite3_exec( database, "update tbl_srs set noupdate=(auth_name='EPSG' and auth_id in (5513,5514,5221,2065,102067,4156,4818))", nullptr, nullptr, nullptr );
1857 
1858  ( void )sqlite3_exec( database, "UPDATE tbl_srs SET srid=141001 WHERE srid=41001 AND auth_name='OSGEO' AND auth_id='41001'", nullptr, nullptr, nullptr );
1859 
1860  OGRSpatialReferenceH crs = OSRNewSpatialReference( nullptr );
1861  const char *tail = nullptr;
1862  sqlite3_stmt *select = nullptr;
1863  char *errMsg = nullptr;
1864 
1865  QString proj4;
1866  QString sql;
1867  QHash<int, QString> wkts;
1868  loadIds( wkts );
1869  loadWkts( wkts, "epsg.wkt" );
1870 
1871  qDebug( "%d WKTs loaded", wkts.count() );
1872 
1873  for ( QHash<int, QString>::const_iterator it = wkts.constBegin(); it != wkts.constEnd(); ++it )
1874  {
1875  QByteArray ba( it.value().toUtf8() );
1876  char *psz = ba.data();
1877  OGRErr ogrErr = OSRImportFromWkt( crs, &psz );
1878  if ( ogrErr != OGRERR_NONE )
1879  continue;
1880 
1881  if ( OSRExportToProj4( crs, &psz ) != OGRERR_NONE )
1882  continue;
1883 
1884  proj4 = psz;
1885  proj4 = proj4.trimmed();
1886 
1887  CPLFree( psz );
1888 
1889  if ( proj4.isEmpty() )
1890  continue;
1891 
1892  sql = QStringLiteral( "SELECT parameters,noupdate FROM tbl_srs WHERE auth_name='EPSG' AND auth_id='%1'" ).arg( it.key() );
1893  if ( sqlite3_prepare( database, sql.toLatin1(), sql.size(), &select, &tail ) != SQLITE_OK )
1894  {
1895  qCritical( "Could not prepare: %s [%s]\n", sql.toLatin1().constData(), sqlite3_errmsg( database ) );
1896  continue;
1897  }
1898 
1899  QString srsProj4;
1900  if ( sqlite3_step( select ) == SQLITE_ROW )
1901  {
1902  srsProj4 = reinterpret_cast< const char * >( sqlite3_column_text( select, 0 ) );
1903 
1904  if ( QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( select, 1 ) ) ).toInt() != 0 )
1905  continue;
1906  }
1907 
1908  sqlite3_finalize( select );
1909 
1910  if ( !srsProj4.isEmpty() )
1911  {
1912  if ( proj4 != srsProj4 )
1913  {
1914  errMsg = nullptr;
1915  sql = QStringLiteral( "UPDATE tbl_srs SET parameters=%1 WHERE auth_name='EPSG' AND auth_id=%2" ).arg( quotedValue( proj4 ) ).arg( it.key() );
1916 
1917  if ( sqlite3_exec( database, sql.toUtf8(), nullptr, nullptr, &errMsg ) != SQLITE_OK )
1918  {
1919  qCritical( "Could not execute: %s [%s/%s]\n",
1920  sql.toLocal8Bit().constData(),
1921  sqlite3_errmsg( database ),
1922  errMsg ? errMsg : "(unknown error)" );
1923  errors++;
1924  }
1925  else
1926  {
1927  updated++;
1928  }
1929  }
1930  }
1931  else
1932  {
1933  QRegExp projRegExp( "\\+proj=(\\S+)" );
1934  if ( projRegExp.indexIn( proj4 ) < 0 )
1935  {
1936  QgsDebugMsg( QString( "EPSG %1: no +proj argument found [%2]" ).arg( it.key() ).arg( proj4 ) );
1937  continue;
1938  }
1939 
1940  QRegExp ellipseRegExp( "\\+ellps=(\\S+)" );
1941  QString ellps;
1942  if ( ellipseRegExp.indexIn( proj4 ) >= 0 )
1943  {
1944  ellps = ellipseRegExp.cap( 1 );
1945  }
1946 
1947  QString name( OSRIsGeographic( crs ) ? OSRGetAttrValue( crs, "GEOCS", 0 ) : OSRGetAttrValue( crs, "PROJCS", 0 ) );
1948  if ( name.isEmpty() )
1949  name = QObject::tr( "Imported from GDAL" );
1950 
1951  sql = QStringLiteral( "INSERT INTO tbl_srs(description,projection_acronym,ellipsoid_acronym,parameters,srid,auth_name,auth_id,is_geo,deprecated) VALUES (%1,%2,%3,%4,%5,'EPSG',%5,%6,0)" )
1952  .arg( quotedValue( name ),
1953  quotedValue( projRegExp.cap( 1 ) ),
1954  quotedValue( ellps ),
1955  quotedValue( proj4 ) )
1956  .arg( it.key() )
1957  .arg( OSRIsGeographic( crs ) );
1958 
1959  errMsg = nullptr;
1960  if ( sqlite3_exec( database, sql.toUtf8(), nullptr, nullptr, &errMsg ) == SQLITE_OK )
1961  {
1962  inserted++;
1963  }
1964  else
1965  {
1966  qCritical( "Could not execute: %s [%s/%s]\n",
1967  sql.toLocal8Bit().constData(),
1968  sqlite3_errmsg( database ),
1969  errMsg ? errMsg : "(unknown error)" );
1970  errors++;
1971 
1972  if ( errMsg )
1973  sqlite3_free( errMsg );
1974  }
1975  }
1976  }
1977 
1978  sql = QStringLiteral( "DELETE FROM tbl_srs WHERE auth_name='EPSG' AND NOT auth_id IN (" );
1979  QString delim;
1980  QHash<int, QString>::const_iterator it = wkts.constBegin();
1981  for ( ; it != wkts.constEnd(); ++it )
1982  {
1983  sql += delim + QString::number( it.key() );
1984  delim = ',';
1985  }
1986  sql += QLatin1String( ") AND NOT noupdate" );
1987 
1988  if ( sqlite3_exec( database, sql.toUtf8(), nullptr, nullptr, nullptr ) == SQLITE_OK )
1989  {
1990  deleted = sqlite3_changes( database );
1991  }
1992  else
1993  {
1994  errors++;
1995  qCritical( "Could not execute: %s [%s]\n",
1996  sql.toLocal8Bit().constData(),
1997  sqlite3_errmsg( database ) );
1998  }
1999 
2000 #if !defined(PJ_VERSION) || PJ_VERSION!=470
2001  sql = QStringLiteral( "select auth_name,auth_id,parameters from tbl_srs WHERE auth_name<>'EPSG' AND NOT deprecated AND NOT noupdate" );
2002  if ( sqlite3_prepare( database, sql.toLatin1(), sql.size(), &select, &tail ) == SQLITE_OK )
2003  {
2004  while ( sqlite3_step( select ) == SQLITE_ROW )
2005  {
2006  const char *auth_name = reinterpret_cast< const char * >( sqlite3_column_text( select, 0 ) );
2007  const char *auth_id = reinterpret_cast< const char * >( sqlite3_column_text( select, 1 ) );
2008  const char *params = reinterpret_cast< const char * >( sqlite3_column_text( select, 2 ) );
2009 
2010  QString input = QStringLiteral( "+init=%1:%2" ).arg( QString( auth_name ).toLower(), auth_id );
2011  projPJ pj = pj_init_plus( input.toLatin1() );
2012  if ( !pj )
2013  {
2014  input = QStringLiteral( "+init=%1:%2" ).arg( QString( auth_name ).toUpper(), auth_id );
2015  pj = pj_init_plus( input.toLatin1() );
2016  }
2017 
2018  if ( pj )
2019  {
2020  char *def = pj_get_def( pj, 0 );
2021  if ( def )
2022  {
2023  proj4 = def;
2024  pj_dalloc( def );
2025 
2026  input.prepend( ' ' ).append( ' ' );
2027  if ( proj4.startsWith( input ) )
2028  {
2029  proj4 = proj4.mid( input.size() );
2030  proj4 = proj4.trimmed();
2031  }
2032 
2033  if ( proj4 != params )
2034  {
2035  sql = QStringLiteral( "UPDATE tbl_srs SET parameters=%1 WHERE auth_name=%2 AND auth_id=%3" )
2036  .arg( quotedValue( proj4 ),
2037  quotedValue( auth_name ),
2038  quotedValue( auth_id ) );
2039 
2040  if ( sqlite3_exec( database, sql.toUtf8(), nullptr, nullptr, &errMsg ) == SQLITE_OK )
2041  {
2042  updated++;
2043  }
2044  else
2045  {
2046  qCritical( "Could not execute: %s [%s/%s]\n",
2047  sql.toLocal8Bit().constData(),
2048  sqlite3_errmsg( database ),
2049  errMsg ? errMsg : "(unknown error)" );
2050  errors++;
2051  }
2052  }
2053  }
2054  else
2055  {
2056  QgsDebugMsg( QString( "could not retrieve proj string for %1 from PROJ" ).arg( input ) );
2057  }
2058  }
2059  else
2060  {
2061  QgsDebugMsgLevel( QString( "could not retrieve crs for %1 from PROJ" ).arg( input ), 3 );
2062  }
2063 
2064  pj_free( pj );
2065  }
2066  }
2067  else
2068  {
2069  errors++;
2070  qCritical( "Could not execute: %s [%s]\n",
2071  sql.toLocal8Bit().constData(),
2072  sqlite3_errmsg( database ) );
2073  }
2074 #endif
2075 
2076  OSRDestroySpatialReference( crs );
2077 
2078  if ( sqlite3_exec( database, "COMMIT", nullptr, nullptr, nullptr ) != SQLITE_OK )
2079  {
2080  qCritical( "Could not commit transaction: %s [%s]\n", QgsApplication::srsDatabaseFilePath().toLocal8Bit().constData(), sqlite3_errmsg( database ) );
2081  return -1;
2082  }
2083 
2084  sqlite3_close( database );
2085 
2086  qWarning( "CRS update (inserted:%d updated:%d deleted:%d errors:%d)", inserted, updated, deleted, errors );
2087 
2088  if ( errors > 0 )
2089  return -errors;
2090  else
2091  return updated + inserted;
2092 }
2093 
2094 bool QgsCoordinateReferenceSystem::syncDatumTransform( const QString &dbPath )
2095 {
2096  const char *filename = CSVFilename( "datum_shift.csv" );
2097  FILE *fp = VSIFOpen( filename, "rb" );
2098  if ( !fp )
2099  {
2100  return false;
2101  }
2102 
2103  char **fieldnames = CSVReadParseLine( fp );
2104 
2105  // "SEQ_KEY","COORD_OP_CODE","SOURCE_CRS_CODE","TARGET_CRS_CODE","REMARKS","COORD_OP_SCOPE","AREA_OF_USE_CODE","AREA_SOUTH_BOUND_LAT","AREA_NORTH_BOUND_LAT","AREA_WEST_BOUND_LON","AREA_EAST_BOUND_LON","SHOW_OPERATION","DEPRECATED","COORD_OP_METHOD_CODE","DX","DY","DZ","RX","RY","RZ","DS","PREFERRED"
2106 
2107  struct
2108  {
2109  const char *src; //skip-init-check
2110  const char *dst; //skip-init-check
2111  int idx;
2112  } map[] =
2113  {
2114  // { "SEQ_KEY", "", -1 },
2115  { "SOURCE_CRS_CODE", "source_crs_code", -1 },
2116  { "TARGET_CRS_CODE", "target_crs_code", -1 },
2117  { "REMARKS", "remarks", -1 },
2118  { "COORD_OP_SCOPE", "scope", -1 },
2119  { "AREA_OF_USE_CODE", "area_of_use_code", -1 },
2120  // { "AREA_SOUTH_BOUND_LAT", "", -1 },
2121  // { "AREA_NORTH_BOUND_LAT", "", -1 },
2122  // { "AREA_WEST_BOUND_LON", "", -1 },
2123  // { "AREA_EAST_BOUND_LON", "", -1 },
2124  // { "SHOW_OPERATION", "", -1 },
2125  { "DEPRECATED", "deprecated", -1 },
2126  { "COORD_OP_METHOD_CODE", "coord_op_method_code", -1 },
2127  { "DX", "p1", -1 },
2128  { "DY", "p2", -1 },
2129  { "DZ", "p3", -1 },
2130  { "RX", "p4", -1 },
2131  { "RY", "p5", -1 },
2132  { "RZ", "p6", -1 },
2133  { "DS", "p7", -1 },
2134  { "PREFERRED", "preferred", -1 },
2135  { "COORD_OP_CODE", "coord_op_code", -1 },
2136  };
2137 
2138  QString update = QStringLiteral( "UPDATE tbl_datum_transform SET " );
2139  QString insert, values;
2140 
2141  int n = CSLCount( fieldnames );
2142 
2143  int idxid = -1, idxrx = -1, idxry = -1, idxrz = -1, idxmcode = -1;
2144  for ( unsigned int i = 0; i < sizeof( map ) / sizeof( *map ); i++ )
2145  {
2146  bool last = i == sizeof( map ) / sizeof( *map ) - 1;
2147 
2148  map[i].idx = CSLFindString( fieldnames, map[i].src );
2149  if ( map[i].idx < 0 )
2150  {
2151  qWarning( "field %s not found", map[i].src );
2152  CSLDestroy( fieldnames );
2153  fclose( fp );
2154  return false;
2155  }
2156 
2157  if ( strcmp( map[i].src, "COORD_OP_CODE" ) == 0 )
2158  idxid = i;
2159  if ( strcmp( map[i].src, "RX" ) == 0 )
2160  idxrx = i;
2161  if ( strcmp( map[i].src, "RY" ) == 0 )
2162  idxry = i;
2163  if ( strcmp( map[i].src, "RZ" ) == 0 )
2164  idxrz = i;
2165  if ( strcmp( map[i].src, "COORD_OP_METHOD_CODE" ) == 0 )
2166  idxmcode = i;
2167 
2168  if ( i > 0 )
2169  {
2170  insert += ',';
2171  values += ',';
2172 
2173  if ( last )
2174  {
2175  update += QLatin1String( " WHERE " );
2176  }
2177  else
2178  {
2179  update += ',';
2180  }
2181  }
2182 
2183  update += QStringLiteral( "%1=%%2" ).arg( map[i].dst ).arg( i + 1 );
2184 
2185  insert += map[i].dst;
2186  values += QStringLiteral( "%%1" ).arg( i + 1 );
2187  }
2188 
2189  insert = "INSERT INTO tbl_datum_transform(" + insert + ") VALUES (" + values + ')';
2190 
2191  CSLDestroy( fieldnames );
2192 
2193  Q_ASSERT( idxid >= 0 );
2194  Q_ASSERT( idxrx >= 0 );
2195  Q_ASSERT( idxry >= 0 );
2196  Q_ASSERT( idxrz >= 0 );
2197 
2198  sqlite3 *db = nullptr;
2199  int openResult = sqlite3_open( dbPath.toUtf8().constData(), &db );
2200  if ( openResult != SQLITE_OK )
2201  {
2202  fclose( fp );
2203  return false;
2204  }
2205 
2206  if ( sqlite3_exec( db, "BEGIN TRANSACTION", nullptr, nullptr, nullptr ) != SQLITE_OK )
2207  {
2208  qCritical( "Could not begin transaction: %s [%s]\n", QgsApplication::srsDatabaseFilePath().toLocal8Bit().constData(), sqlite3_errmsg( db ) );
2209  sqlite3_close( db );
2210  fclose( fp );
2211  return false;
2212  }
2213 
2214  QStringList v;
2215  v.reserve( sizeof( map ) / sizeof( *map ) );
2216 
2217  while ( !feof( fp ) )
2218  {
2219  char **values = CSVReadParseLine( fp );
2220 
2221  v.clear();
2222 
2223  if ( CSLCount( values ) == 0 )
2224  break;
2225 
2226  if ( CSLCount( values ) < n )
2227  {
2228  qWarning( "Only %d columns", CSLCount( values ) );
2229  continue;
2230  }
2231 
2232  for ( unsigned int i = 0; i < sizeof( map ) / sizeof( *map ); i++ )
2233  {
2234  int idx = map[i].idx;
2235  Q_ASSERT( idx != -1 );
2236  Q_ASSERT( idx < n );
2237  v.insert( i, *values[ idx ] ? quotedValue( values[idx] ) : QStringLiteral( "NULL" ) );
2238  }
2239 
2240  //switch sign of rotation parameters. See http://trac.osgeo.org/proj/wiki/GenParms#towgs84-DatumtransformationtoWGS84
2241  if ( v.at( idxmcode ).compare( QLatin1String( "'9607'" ) ) == 0 )
2242  {
2243  v[ idxmcode ] = QStringLiteral( "'9606'" );
2244  v[ idxrx ] = '\'' + qgsDoubleToString( -( v[ idxrx ].remove( '\'' ).toDouble() ) ) + '\'';
2245  v[ idxry ] = '\'' + qgsDoubleToString( -( v[ idxry ].remove( '\'' ).toDouble() ) ) + '\'';
2246  v[ idxrz ] = '\'' + qgsDoubleToString( -( v[ idxrz ].remove( '\'' ).toDouble() ) ) + '\'';
2247  }
2248 
2249  //entry already in db?
2250  sqlite3_stmt *stmt = nullptr;
2251  QString cOpCode;
2252  QString sql = QStringLiteral( "SELECT coord_op_code FROM tbl_datum_transform WHERE coord_op_code=%1" ).arg( v[ idxid ] );
2253  int prepareRes = sqlite3_prepare( db, sql.toLatin1(), sql.size(), &stmt, nullptr );
2254  if ( prepareRes != SQLITE_OK )
2255  continue;
2256 
2257  if ( sqlite3_step( stmt ) == SQLITE_ROW )
2258  {
2259  cOpCode = reinterpret_cast< const char * >( sqlite3_column_text( stmt, 0 ) );
2260  }
2261  sqlite3_finalize( stmt );
2262 
2263  sql = cOpCode.isEmpty() ? insert : update;
2264  for ( int i = 0; i < v.size(); i++ )
2265  {
2266  sql = sql.arg( v[i] );
2267  }
2268 
2269  if ( sqlite3_exec( db, sql.toUtf8(), nullptr, nullptr, nullptr ) != SQLITE_OK )
2270  {
2271  qCritical( "SQL: %s", sql.toUtf8().constData() );
2272  qCritical( "Error: %s", sqlite3_errmsg( db ) );
2273  }
2274  }
2275 
2276  if ( sqlite3_exec( db, "COMMIT", nullptr, nullptr, nullptr ) != SQLITE_OK )
2277  {
2278  qCritical( "Could not commit transaction: %s [%s]\n", QgsApplication::srsDatabaseFilePath().toLocal8Bit().constData(), sqlite3_errmsg( db ) );
2279  return false;
2280  }
2281 
2282  sqlite3_close( db );
2283  return true;
2284 }
2285 
2287 {
2288  if ( isGeographic() )
2289  {
2290  return d->mAuthId;
2291  }
2292  else if ( d->mCRS )
2293  {
2294  return OSRGetAuthorityName( d->mCRS, "GEOGCS" ) + QStringLiteral( ":" ) + OSRGetAuthorityCode( d->mCRS, "GEOGCS" );
2295  }
2296  else
2297  {
2298  return QString();
2299  }
2300 }
2301 
2303 {
2304  QStringList projections;
2305 
2306  // Read settings from persistent storage
2307  QgsSettings settings;
2308  projections = settings.value( QStringLiteral( "UI/recentProjections" ) ).toStringList();
2309  /*** The reading (above) of internal id from persistent storage should be removed sometime in the future */
2310  /*** This is kept now for backwards compatibility */
2311 
2312  QStringList projectionsProj4 = settings.value( QStringLiteral( "UI/recentProjectionsProj4" ) ).toStringList();
2313  QStringList projectionsAuthId = settings.value( QStringLiteral( "UI/recentProjectionsAuthId" ) ).toStringList();
2314  if ( projectionsAuthId.size() >= projections.size() )
2315  {
2316  // We had saved state with AuthId and Proj4. Use that instead
2317  // to find out the crs id
2318  projections.clear();
2319  for ( int i = 0; i < projectionsAuthId.size(); i++ )
2320  {
2321  // Create a crs from the EPSG
2323  crs.createFromOgcWmsCrs( projectionsAuthId.at( i ) );
2324  if ( ! crs.isValid() )
2325  {
2326  // Couldn't create from EPSG, try the Proj4 string instead
2327  if ( i >= projectionsProj4.size() || !crs.createFromProj4( projectionsProj4.at( i ) ) )
2328  {
2329  // No? Skip this entry
2330  continue;
2331  }
2332  //If the CRS can be created but do not correspond to a CRS in the database, skip it (for example a deleted custom CRS)
2333  if ( crs.srsid() == 0 )
2334  {
2335  continue;
2336  }
2337  }
2338  projections << QString::number( crs.srsid() );
2339  }
2340  }
2341  return projections;
2342 }
2343 
2345 {
2346  sSrIdCacheLock.lockForWrite();
2347  sSrIdCache.clear();
2348  sSrIdCacheLock.unlock();
2349  sOgcLock.lockForWrite();
2350  sOgcCache.clear();
2351  sOgcLock.unlock();
2352  sProj4CacheLock.lockForWrite();
2353  sProj4Cache.clear();
2354  sProj4CacheLock.unlock();
2355  sCRSWktLock.lockForWrite();
2356  sWktCache.clear();
2357  sCRSWktLock.unlock();
2358  sCRSSrsIdLock.lockForWrite();
2359  sSrsIdCache.clear();
2360  sCRSSrsIdLock.unlock();
2361  sCrsStringLock.lockForWrite();
2362  sStringCache.clear();
2363  sCrsStringLock.unlock();
2364 }
QString geographicCrsAuthId() const
Returns auth id of related geographic CRS.
QgsCoordinateReferenceSystem()
Constructs an invalid CRS object.
bool operator!=(const QgsCoordinateReferenceSystem &srs) const
Overloaded != operator used to compare to CRS&#39;s.
static QgsCoordinateReferenceSystem fromProj4(const QString &proj4)
Creates a CRS from a proj4 style formatted string.
bool createFromString(const QString &definition)
Set up this CRS from a string definition.
static QList< long > validSrsIds()
Returns a list of all valid SRS IDs present in the CRS database.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:51
static QString qgisUserDatabaseFilePath()
Returns the path to the user qgis.db file.
bool saveAsUserCrs(const QString &name)
Save the proj4-string as a custom CRS.
#define QgsDebugMsg(str)
Definition: qgslogger.h:36
void validate()
Perform some validation on this CRS.
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:124
static void setCustomCrsValidation(CUSTOM_CRS_VALIDATION f)
Sets custom function to force valid CRS QGIS uses implementation in QgisGui::customSrsValidation.
QString toProj4() const
Returns a Proj4 string representation of this CRS.
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:198
Internal ID used by QGIS in the local SQLite database.
#define FEET_TO_METER
bool createFromOgcWmsCrs(const QString &crs)
Sets this CRS to the given OGC WMS-format Coordinate Reference Systems.
static Q_INVOKABLE QgsCoordinateReferenceSystem fromEpsgId(long epsg)
Creates a CRS from a given EPSG ID.
void setValidationHint(const QString &html)
Set user hint for validation.
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the CRS.
static QgsCoordinateReferenceSystem fromWkt(const QString &wkt)
Creates a CRS from a WKT spatial ref sys definition string.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:37
bool createFromSrsId(const long srsId)
Sets this CRS by lookup of internal QGIS CRS ID in the CRS database.
static QStringList recentProjections()
Returns a list of recently used projections.
QString validationHint()
Get user hint for validation.
long postgisSrid() const
Returns PostGIS SRID for the CRS.
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:186
long findMatchingProj()
Walks the CRS databases (both system and user database) trying to match stored PROJ.4 string to a database entry in order to fill in further pieces of information about CRS.
const long GEOCRS_ID
Magic number for a geographic coord sys in QGIS srs.db tbl_srs.srs_id.
Definition: qgis.h:311
static void logMessage(const QString &message, const QString &tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
QString description() const
Returns the descriptive name of the CRS, e.g., "WGS 84" or "GDA 94 / Vicgrid94".
Degrees, for planar geographic CRS distance measurements.
Definition: qgsunittypes.h:50
QgsCoordinateReferenceSystem & operator=(const QgsCoordinateReferenceSystem &srs)
Assignment operator.
const QString GEO_EPSG_CRS_AUTHID
Geographic coord sys from EPSG authority.
Definition: qgis.cpp:69
bool isGeographic() const
Returns whether the CRS is a geographic CRS (using lat/lon coordinates)
bool createFromWkt(const QString &wkt)
Sets this CRS using a WKT definition.
CrsType
Enumeration of types of IDs accepted in createFromId() method.
struct sqlite3 sqlite3
static CUSTOM_CRS_VALIDATION customCrsValidation()
Gets custom function.
QString projectionAcronym() const
Returns the projection acronym for the projection used by the CRS.
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:42
Unknown distance unit.
Definition: qgsunittypes.h:53
const int USER_CRS_START_ID
Magick number that determines whether a projection crsid is a system (srs.db) or user (~/...
Definition: qgis.h:325
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
bool createFromUserInput(const QString &definition)
Set up this CRS from various text formats.
bool hasAxisInverted() const
Returns whether axis is inverted (e.g., for WMS 1.3) for the CRS.
This class represents a coordinate reference system (CRS).
QString toWkt() const
Returns a WKT representation of this CRS.
bool createFromProj4(const QString &projString)
Sets this CRS by passing it a PROJ.4 style formatted string.
bool createFromId(const long id, CrsType type=PostgisCrsId)
Sets this CRS by lookup of the given ID in the CRS database.
const int LAT_PREFIX_LEN
The length of the string "+lat_1=".
Definition: qgis.h:321
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), const Section section=Section::NoSection) const
Returns the value for setting key.
static QString srsDatabaseFilePath()
Returns the path to the srs.db file.
QgsUnitTypes::DistanceUnit mapUnits() const
Returns the units for the projection used by the CRS.
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
static void invalidateCache()
Clears the internal cache used to initialize QgsCoordinateReferenceSystem objects.
void(* CUSTOM_CRS_VALIDATION)(QgsCoordinateReferenceSystem &)
long srsid() const
Returns the internal CRS ID, if available.
bool writeXml(QDomNode &node, QDomDocument &doc) const
Stores state to the given Dom node in the given document.
void setValue(const QString &key, const QVariant &value, const Section section=Section::NoSection)
Sets the value of setting key to value.
bool operator==(const QgsCoordinateReferenceSystem &srs) const
Overloaded == operator used to compare to CRS&#39;s.
static int syncDatabase()
Update proj.4 parameters in our database from proj.4.
QString authid() const
Returns the authority identifier for the CRS.
static void setupESRIWktFix()
Make sure that ESRI WKT import is done properly.
bool createFromSrid(const long srid)
Sets this CRS by lookup of the given PostGIS SRID in the CRS database.
static QgsCoordinateReferenceSystem fromSrsId(long srsId)
Creates a CRS from a specified QGIS SRS ID.
void * OGRSpatialReferenceH
bool isValid() const
Returns whether this CRS is correctly initialized and usable.