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