QGIS API Documentation  2.99.0-Master (64819bc)
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 #include <QSettings>
32 
33 #include "qgsapplication.h"
34 #include "qgslogger.h"
35 #include "qgsmessagelog.h"
36 #include "qgis.h" //const vals declared here
37 #include "qgslocalec.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( theDefinition );
74 }
75 
77 {
78  d = new QgsCoordinateReferenceSystemPrivate();
79  createFromId( theId, theType );
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  result = sqlite3_prepare( database, sql.toUtf8(),
122  sql.toUtf8().length(),
123  &statement, &tail );
124  while ( 1 )
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 
190 bool QgsCoordinateReferenceSystem::createFromId( const long theId, CrsType theType )
191 {
192  bool result = false;
193  switch ( theType )
194  {
195  case InternalCrsId:
196  result = createFromSrsId( theId );
197  break;
198  case PostgisCrsId:
199  result = createFromSrid( theId );
200  break;
201  case EpsgCrsId:
202  result = createFromOgcWmsCrs( QStringLiteral( "EPSG:%1" ).arg( theId ) );
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 &theDefinition )
212 {
213  sCrsStringLock.lockForRead();
214  QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sStringCache.constFind( theDefinition );
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( theDefinition ) == 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( theDefinition ) == 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( theDefinition, *this );
266  sCrsStringLock.unlock();
267  return result;
268 }
269 
270 bool QgsCoordinateReferenceSystem::createFromUserInput( const QString &theDefinition )
271 {
272  QString theWkt;
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 ( theDefinition.startsWith( QLatin1String( "ESRI::" ) ) )
278  {
279  setupESRIWktFix();
280  }
281 
282  if ( OSRSetFromUserInput( crs, theDefinition.toLocal8Bit().constData() ) == OGRERR_NONE )
283  {
284  if ( OSRExportToWkt( crs, &wkt ) == OGRERR_NONE )
285  {
286  theWkt = wkt;
287  CPLFree( wkt );
288  }
289  OSRDestroySpatialReference( crs );
290  }
291  //QgsDebugMsg( "theDefinition: " + theDefinition + " theWkt = " + theWkt );
292  return createFromWkt( theWkt );
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( theCrs );
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 = theCrs;
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( theCrs, *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( theCrs, *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( theCrs, *this );
384  sOgcLock.unlock();
385 
386  return d->mIsValid;
387  }
388 
389  sOgcLock.lockForWrite();
390  sOgcCache.insert( theCrs, 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 
574 bool QgsCoordinateReferenceSystem::createFromWkt( const QString &theWkt )
575 {
576  d.detach();
577 
578  sCRSWktLock.lockForRead();
579  QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sWktCache.constFind( theWkt );
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 ( theWkt.isEmpty() )
594  {
595  QgsDebugMsg( "theWkt is uninitialized, operation failed" );
596  return d->mIsValid;
597  }
598  QgsDebugMsg( "wkt: " + theWkt );
599  QByteArray ba = theWkt.toLatin1();
600  const char *pWkt = ba.data();
601 
602  OGRErr myInputResult = OSRImportFromWkt( d->mCRS, const_cast< char ** >( & pWkt ) );
603 
604  if ( myInputResult != OGRERR_NONE )
605  {
606  QgsDebugMsg( "\n---------------------------------------------------------------" );
607  QgsDebugMsg( "This CRS could *** NOT *** be set from the supplied Wkt " );
608  QgsDebugMsg( "INPUT: " + theWkt );
609  QgsDebugMsg( QString( "UNUSED WKT: %1" ).arg( pWkt ) );
610  QgsDebugMsg( "---------------------------------------------------------------\n" );
611 
612  sCRSWktLock.lockForWrite();
613  sWktCache.insert( theWkt, *this );
614  sCRSWktLock.unlock();
615  return d->mIsValid;
616  }
617 
618  if ( OSRAutoIdentifyEPSG( d->mCRS ) == OGRERR_NONE )
619  {
620  QString authid = QStringLiteral( "%1:%2" )
621  .arg( OSRGetAuthorityName( d->mCRS, nullptr ),
622  OSRGetAuthorityCode( d->mCRS, nullptr ) );
623  QgsDebugMsg( "authid recognized as " + authid );
624  bool result = createFromOgcWmsCrs( authid );
625  sCRSWktLock.lockForWrite();
626  sWktCache.insert( theWkt, *this );
627  sCRSWktLock.unlock();
628  return result;
629  }
630 
631  // always morph from esri as it doesn't hurt anything
632  // FW: Hey, that's not right! It can screw stuff up! Disable
633  //myOgrSpatialRef.morphFromESRI();
634 
635  // create the proj4 structs needed for transforming
636  char *proj4src = nullptr;
637  OSRExportToProj4( d->mCRS, &proj4src );
638 
639  //now that we have the proj4string, delegate to createFromProj4 so
640  // that we can try to fill in the remaining class members...
641  //create from Proj will set the isValidFlag
642  if ( !createFromProj4( proj4src ) )
643  {
644  CPLFree( proj4src );
645 
646  // try fixed up version
647  OSRFixup( d->mCRS );
648 
649  OSRExportToProj4( d->mCRS, &proj4src );
650 
651  createFromProj4( proj4src );
652  }
653  //TODO: createFromProj4 used to save to the user database any new CRS
654  // this behavior was changed in order to separate creation and saving.
655  // Not sure if it necessary to save it here, should be checked by someone
656  // familiar with the code (should also give a more descriptive name to the generated CRS)
657  if ( d->mSrsId == 0 )
658  {
659  QString myName = QStringLiteral( " * %1 (%2)" )
660  .arg( QObject::tr( "Generated CRS", "A CRS automatically generated from layer info get this prefix for description" ),
661  toProj4() );
662  saveAsUserCrs( myName );
663  }
664 
665  CPLFree( proj4src );
666 
667  sCRSWktLock.lockForWrite();
668  sWktCache.insert( theWkt, *this );
669  sCRSWktLock.unlock();
670 
671  return d->mIsValid;
672  //setMapunits will be called by createfromproj above
673 }
674 
676 {
677  return d->mIsValid;
678 }
679 
680 bool QgsCoordinateReferenceSystem::createFromProj4( const QString &theProj4String )
681 {
682  d.detach();
683 
684  sProj4CacheLock.lockForRead();
685  QHash< QString, QgsCoordinateReferenceSystem >::const_iterator crsIt = sProj4Cache.constFind( theProj4String );
686  if ( crsIt != sProj4Cache.constEnd() )
687  {
688  // found a match in the cache
689  *this = crsIt.value();
690  sProj4CacheLock.unlock();
691  return true;
692  }
693  sProj4CacheLock.unlock();
694 
695  //
696  // Examples:
697  // +proj=tmerc +lat_0=0 +lon_0=-62 +k=0.999500 +x_0=400000 +y_0=0
698  // +ellps=clrk80 +towgs84=-255,-15,71,0,0,0,0 +units=m +no_defs
699  //
700  // +proj=lcc +lat_1=46.8 +lat_0=46.8 +lon_0=2.337229166666664 +k_0=0.99987742
701  // +x_0=600000 +y_0=2200000 +a=6378249.2 +b=6356515.000000472 +units=m +no_defs
702  //
703  QString myProj4String = theProj4String.trimmed();
704  QgsDebugMsg( "proj4: " + myProj4String );
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  QgsDebugMsg( "proj string supplied has no +proj argument" );
713 
714  sProj4CacheLock.lockForWrite();
715  sProj4Cache.insert( theProj4String, *this );
716  sProj4CacheLock.unlock();
717 
718  return d->mIsValid;
719  }
720 
721  d->mProjectionAcronym = myProjRegExp.cap( 1 );
722 
723  QRegExp myEllipseRegExp( "\\+ellps=(\\S+)" );
724  myStart = myEllipseRegExp.indexIn( myProj4String );
725  if ( myStart == -1 )
726  {
727  QgsDebugMsg( "proj string supplied has no +ellps argument" );
728  d->mEllipsoidAcronym.clear();
729  }
730  else
731  {
732  d->mEllipsoidAcronym = myEllipseRegExp.cap( 1 );
733  }
734 
735  QRegExp myAxisRegExp( "\\+a=(\\S+)" );
736  myStart = myAxisRegExp.indexIn( myProj4String );
737  if ( myStart == -1 )
738  {
739  QgsDebugMsg( "proj string supplied has no +a argument" );
740  }
741 
742  long mySrsId = 0;
743  QgsCoordinateReferenceSystem::RecordMap myRecord;
744 
745  /*
746  * We try to match the proj string to and srsid using the following logic:
747  * - perform a whole text search on proj4 string (if not null)
748  */
749  myRecord = getRecord( "select * from tbl_srs where parameters=" + quotedValue( myProj4String ) + " order by deprecated" );
750  if ( myRecord.empty() )
751  {
752  // Ticket #722 - aaronr
753  // Check if we can swap the lat_1 and lat_2 params (if they exist) to see if we match...
754  // First we check for lat_1 and lat_2
755  QRegExp myLat1RegExp( "\\+lat_1=\\S+" );
756  QRegExp myLat2RegExp( "\\+lat_2=\\S+" );
757  int myStart1 = 0;
758  int myLength1 = 0;
759  int myStart2 = 0;
760  int myLength2 = 0;
761  QString lat1Str = QLatin1String( "" );
762  QString lat2Str = QLatin1String( "" );
763  myStart1 = myLat1RegExp.indexIn( myProj4String, myStart1 );
764  myStart2 = myLat2RegExp.indexIn( myProj4String, myStart2 );
765  if ( myStart1 != -1 && myStart2 != -1 )
766  {
767  myLength1 = myLat1RegExp.matchedLength();
768  myLength2 = myLat2RegExp.matchedLength();
769  lat1Str = myProj4String.mid( myStart1 + LAT_PREFIX_LEN, myLength1 - LAT_PREFIX_LEN );
770  lat2Str = myProj4String.mid( myStart2 + LAT_PREFIX_LEN, myLength2 - LAT_PREFIX_LEN );
771  }
772  // If we found the lat_1 and lat_2 we need to swap and check to see if we can find it...
773  if ( lat1Str != QLatin1String( "" ) && lat2Str != QLatin1String( "" ) )
774  {
775  // Make our new string to check...
776  QString theProj4StringModified = myProj4String;
777  // First just swap in the lat_2 value for lat_1 value
778  theProj4StringModified.replace( myStart1 + LAT_PREFIX_LEN, myLength1 - LAT_PREFIX_LEN, lat2Str );
779  // Now we have to find the lat_2 location again since it has potentially moved...
780  myStart2 = 0;
781  myStart2 = myLat2RegExp.indexIn( theProj4String, myStart2 );
782  theProj4StringModified.replace( myStart2 + LAT_PREFIX_LEN, myLength2 - LAT_PREFIX_LEN, lat1Str );
783  QgsDebugMsg( "trying proj4string match with swapped lat_1,lat_2" );
784  myRecord = getRecord( "select * from tbl_srs where parameters=" + quotedValue( theProj4StringModified.trimmed() ) + " order by deprecated" );
785  }
786  }
787 
788  if ( myRecord.empty() )
789  {
790  // match all parameters individually:
791  // - order of parameters doesn't matter
792  // - found definition may have more parameters (like +towgs84 in GDAL)
793  // - retry without datum, if no match is found (looks like +datum<>WGS84 was dropped in GDAL)
794 
795  QString sql = QStringLiteral( "SELECT * FROM tbl_srs WHERE " );
796  QString delim = QLatin1String( "" );
797  QString datum;
798 
799  // split on spaces followed by a plus sign (+) to deal
800  // also with parameters containing spaces (e.g. +nadgrids)
801  // make sure result is trimmed (#5598)
802  QStringList myParams;
803  Q_FOREACH ( const QString& param, myProj4String.split( QRegExp( "\\s+(?=\\+)" ), QString::SkipEmptyParts ) )
804  {
805  QString arg = QStringLiteral( "' '||parameters||' ' LIKE %1" ).arg( quotedValue( QStringLiteral( "% %1 %" ).arg( param.trimmed() ) ) );
806  if ( param.startsWith( QLatin1String( "+datum=" ) ) )
807  {
808  datum = arg;
809  }
810  else
811  {
812  sql += delim + arg;
813  delim = QStringLiteral( " AND " );
814  myParams << param.trimmed();
815  }
816  }
817 
818  if ( !datum.isEmpty() )
819  {
820  myRecord = getRecord( sql + delim + datum + " order by deprecated" );
821  }
822 
823  if ( myRecord.empty() )
824  {
825  // datum might have disappeared in definition - retry without it
826  myRecord = getRecord( sql + " order by deprecated" );
827  }
828 
829  if ( !myRecord.empty() )
830  {
831  // Bugfix 8487 : test param lists are equal, except for +datum
832  QStringList foundParams;
833  Q_FOREACH ( const QString& param, myRecord["parameters"].split( QRegExp( "\\s+(?=\\+)" ), QString::SkipEmptyParts ) )
834  {
835  if ( !param.startsWith( QLatin1String( "+datum=" ) ) )
836  foundParams << param.trimmed();
837  }
838 
839  myParams.sort();
840  foundParams.sort();
841 
842  if ( myParams != foundParams )
843  {
844  myRecord.clear();
845  }
846  }
847  }
848 
849  if ( !myRecord.empty() )
850  {
851  mySrsId = myRecord[QStringLiteral( "srs_id" )].toLong();
852  QgsDebugMsg( "proj4string param match search for srsid returned srsid: " + QString::number( mySrsId ) );
853  if ( mySrsId > 0 )
854  {
855  createFromSrsId( mySrsId );
856  }
857  }
858  else
859  {
860  // Last ditch attempt to piece together what we know of the projection to find a match...
861  QgsDebugMsg( "globbing search for srsid from this proj string" );
862  setProj4String( myProj4String );
863  mySrsId = findMatchingProj();
864  QgsDebugMsg( "globbing search for srsid returned srsid: " + QString::number( mySrsId ) );
865  if ( mySrsId > 0 )
866  {
867  createFromSrsId( mySrsId );
868  }
869  else
870  {
871  d->mIsValid = false;
872  }
873  }
874 
875  // if we failed to look up the projection in database, don't worry. we can still use it :)
876  if ( !d->mIsValid )
877  {
878  QgsDebugMsg( "Projection is not found in databases." );
879  //setProj4String will set mIsValidFlag to true if there is no issue
880  setProj4String( myProj4String );
881  }
882 
883  sProj4CacheLock.lockForWrite();
884  sProj4Cache.insert( theProj4String, *this );
885  sProj4CacheLock.unlock();
886 
887  return d->mIsValid;
888 }
889 
890 //private method meant for internal use by this class only
891 QgsCoordinateReferenceSystem::RecordMap QgsCoordinateReferenceSystem::getRecord( const QString& theSql )
892 {
893  QString myDatabaseFileName;
894  QgsCoordinateReferenceSystem::RecordMap myMap;
895  QString myFieldName;
896  QString myFieldValue;
897  sqlite3 *myDatabase = nullptr;
898  const char *myTail = nullptr;
899  sqlite3_stmt *myPreparedStatement = nullptr;
900  int myResult;
901 
902  QgsDebugMsg( "running query: " + theSql );
903  // Get the full path name to the sqlite3 spatial reference database.
904  myDatabaseFileName = QgsApplication::srsDatabaseFilePath();
905  QFileInfo myInfo( myDatabaseFileName );
906  if ( !myInfo.exists() )
907  {
908  QgsDebugMsg( "failed : " + myDatabaseFileName + " does not exist!" );
909  return myMap;
910  }
911 
912  //check the db is available
913  myResult = openDatabase( myDatabaseFileName, &myDatabase );
914  if ( myResult != SQLITE_OK )
915  {
916  return myMap;
917  }
918 
919  myResult = sqlite3_prepare( myDatabase, theSql.toUtf8(), theSql.toUtf8().length(), &myPreparedStatement, &myTail );
920  // XXX Need to free memory from the error msg if one is set
921  if ( myResult == SQLITE_OK && sqlite3_step( myPreparedStatement ) == SQLITE_ROW )
922  {
923  QgsDebugMsg( "trying system srs.db" );
924  int myColumnCount = sqlite3_column_count( myPreparedStatement );
925  //loop through each column in the record adding its expression name and value to the map
926  for ( int myColNo = 0; myColNo < myColumnCount; myColNo++ )
927  {
928  myFieldName = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_name( myPreparedStatement, myColNo ) ) );
929  myFieldValue = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( myPreparedStatement, myColNo ) ) );
930  myMap[myFieldName] = myFieldValue;
931  }
932  if ( sqlite3_step( myPreparedStatement ) != SQLITE_DONE )
933  {
934  QgsDebugMsg( "Multiple records found in srs.db" );
935  myMap.clear();
936  }
937  }
938  else
939  {
940  QgsDebugMsg( "failed : " + theSql );
941  }
942 
943  if ( myMap.empty() )
944  {
945  QgsDebugMsg( "trying user qgis.db" );
946  sqlite3_finalize( myPreparedStatement );
947  sqlite3_close( myDatabase );
948 
949  myDatabaseFileName = QgsApplication::qgisUserDatabaseFilePath();
950  QFileInfo myFileInfo;
951  myFileInfo.setFile( myDatabaseFileName );
952  if ( !myFileInfo.exists() )
953  {
954  QgsDebugMsg( "user qgis.db not found" );
955  return myMap;
956  }
957 
958  //check the db is available
959  myResult = openDatabase( myDatabaseFileName, &myDatabase );
960  if ( myResult != SQLITE_OK )
961  {
962  return myMap;
963  }
964 
965  myResult = sqlite3_prepare( myDatabase, theSql.toUtf8(), theSql.toUtf8().length(), &myPreparedStatement, &myTail );
966  // XXX Need to free memory from the error msg if one is set
967  if ( myResult == SQLITE_OK && sqlite3_step( myPreparedStatement ) == SQLITE_ROW )
968  {
969  int myColumnCount = sqlite3_column_count( myPreparedStatement );
970  //loop through each column in the record adding its field name and value to the map
971  for ( int myColNo = 0; myColNo < myColumnCount; myColNo++ )
972  {
973  myFieldName = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_name( myPreparedStatement, myColNo ) ) );
974  myFieldValue = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( myPreparedStatement, myColNo ) ) );
975  myMap[myFieldName] = myFieldValue;
976  }
977 
978  if ( sqlite3_step( myPreparedStatement ) != SQLITE_DONE )
979  {
980  QgsDebugMsg( "Multiple records found in srs.db" );
981  myMap.clear();
982  }
983  }
984  else
985  {
986  QgsDebugMsg( "failed : " + theSql );
987  }
988  }
989  sqlite3_finalize( myPreparedStatement );
990  sqlite3_close( myDatabase );
991 
992 #ifdef QGISDEBUG
993  QgsDebugMsg( "retrieved: " + theSql );
994  RecordMap::Iterator it;
995  for ( it = myMap.begin(); it != myMap.end(); ++it )
996  {
997  QgsDebugMsgLevel( it.key() + " => " + it.value(), 2 );
998  }
999 #endif
1000 
1001  return myMap;
1002 }
1003 
1004 // Accessors -----------------------------------
1005 
1007 {
1008  return d->mSrsId;
1009 }
1010 
1012 {
1013  return d->mSRID;
1014 }
1015 
1017 {
1018  return d->mAuthId;
1019 }
1020 
1022 {
1023  if ( d->mDescription.isNull() )
1024  {
1025  return QLatin1String( "" );
1026  }
1027  else
1028  {
1029  return d->mDescription;
1030  }
1031 }
1032 
1034 {
1035  if ( d->mProjectionAcronym.isNull() )
1036  {
1037  return QLatin1String( "" );
1038  }
1039  else
1040  {
1041  return d->mProjectionAcronym;
1042  }
1043 }
1044 
1046 {
1047  if ( d->mEllipsoidAcronym.isNull() )
1048  {
1049  return QLatin1String( "" );
1050  }
1051  else
1052  {
1053  return d->mEllipsoidAcronym;
1054  }
1055 }
1056 
1058 {
1059  if ( !d->mIsValid )
1060  return QLatin1String( "" );
1061 
1062  if ( d->mProj4.isEmpty() )
1063  {
1064  char *proj4src = nullptr;
1065  OSRExportToProj4( d->mCRS, &proj4src );
1066  d->mProj4 = proj4src;
1067  CPLFree( proj4src );
1068  }
1069  // Stray spaces at the end?
1070  return d->mProj4.trimmed();
1071 }
1072 
1074 {
1075  return d->mIsGeographic;
1076 }
1077 
1079 {
1080  return d->mMapUnits;
1081 }
1082 
1083 
1084 // Mutators -----------------------------------
1085 
1086 
1087 void QgsCoordinateReferenceSystem::setInternalId( long theSrsId )
1088 {
1089  d.detach();
1090  d->mSrsId = theSrsId;
1091 }
1092 void QgsCoordinateReferenceSystem::setAuthId( const QString& authId )
1093 {
1094  d.detach();
1095  d->mAuthId = authId;
1096 }
1097 void QgsCoordinateReferenceSystem::setSrid( long theSrid )
1098 {
1099  d.detach();
1100  d->mSRID = theSrid;
1101 }
1102 void QgsCoordinateReferenceSystem::setDescription( const QString& theDescription )
1103 {
1104  d.detach();
1105  d->mDescription = theDescription;
1106 }
1107 void QgsCoordinateReferenceSystem::setProj4String( const QString& theProj4String )
1108 {
1109  d.detach();
1110  d->mProj4 = theProj4String;
1111 
1112  QgsLocaleNumC l;
1113 
1114  OSRDestroySpatialReference( d->mCRS );
1115  d->mCRS = OSRNewSpatialReference( nullptr );
1116  d->mIsValid = OSRImportFromProj4( d->mCRS, theProj4String.trimmed().toLatin1().constData() ) == OGRERR_NONE;
1117  // OSRImportFromProj4() may accept strings that are not valid proj.4 strings,
1118  // e.g if they lack a +ellps parameter, it will automatically add +ellps=WGS84, but as
1119  // we use the original mProj4 with QgsCoordinateTransform, it will fail to initialize
1120  // so better detect it now.
1121  projPJ theProj = pj_init_plus( theProj4String.trimmed().toLatin1().constData() );
1122  if ( !theProj )
1123  {
1124  QgsDebugMsg( "proj.4 string rejected by pj_init_plus()" );
1125  d->mIsValid = false;
1126  }
1127  else
1128  {
1129  pj_free( theProj );
1130  }
1131  d->mWkt.clear();
1132  setMapUnits();
1133 
1134 #if defined(QGISDEBUG) && QGISDEBUG>=3
1135  debugPrint();
1136 #endif
1137 }
1138 void QgsCoordinateReferenceSystem::setGeographicFlag( bool theGeoFlag )
1139 {
1140  d.detach();
1141  d->mIsGeographic = theGeoFlag;
1142 }
1143 void QgsCoordinateReferenceSystem::setEpsg( long theEpsg )
1144 {
1145  d.detach();
1146  d->mAuthId = QStringLiteral( "EPSG:%1" ).arg( theEpsg );
1147 }
1148 void QgsCoordinateReferenceSystem::setProjectionAcronym( const QString& theProjectionAcronym )
1149 {
1150  d.detach();
1151  d->mProjectionAcronym = theProjectionAcronym;
1152 }
1153 void QgsCoordinateReferenceSystem::setEllipsoidAcronym( const QString& theEllipsoidAcronym )
1154 {
1155  d.detach();
1156  d->mEllipsoidAcronym = theEllipsoidAcronym;
1157 }
1158 
1159 void QgsCoordinateReferenceSystem::setMapUnits()
1160 {
1161  d.detach();
1162  if ( !d->mIsValid )
1163  {
1164  d->mMapUnits = QgsUnitTypes::DistanceUnknownUnit;
1165  return;
1166  }
1167 
1168  char *unitName = nullptr;
1169 
1170  // Of interest to us is that this call adds in a unit parameter if
1171  // one doesn't already exist.
1172  OSRFixup( d->mCRS );
1173 
1174  if ( OSRIsProjected( d->mCRS ) )
1175  {
1176  double toMeter = OSRGetLinearUnits( d->mCRS, &unitName );
1177  QString unit( unitName );
1178 
1179  // If the units parameter was created during the Fixup() call
1180  // above, the name of the units is likely to be 'unknown'. Try to
1181  // do better than that ... (but perhaps ogr should be enhanced to
1182  // do this instead?).
1183 
1184  static const double FEET_TO_METER = 0.3048;
1185  static const double SMALL_NUM = 1e-3;
1186 
1187  if ( qAbs( toMeter - FEET_TO_METER ) < SMALL_NUM )
1188  unit = QStringLiteral( "Foot" );
1189 
1190  QgsDebugMsg( "Projection has linear units of " + unit );
1191 
1192  if ( qgsDoubleNear( toMeter, 1.0 ) ) //Unit name for meters would be "metre"
1193  d->mMapUnits = QgsUnitTypes::DistanceMeters;
1194  else if ( unit == QLatin1String( "Foot" ) )
1195  d->mMapUnits = QgsUnitTypes::DistanceFeet;
1196  else
1197  {
1198  QgsDebugMsg( "Unsupported map units of " + unit );
1199  d->mMapUnits = QgsUnitTypes::DistanceUnknownUnit;
1200  }
1201  }
1202  else
1203  {
1204  OSRGetAngularUnits( d->mCRS, &unitName );
1205  QString unit( unitName );
1206  if ( unit == QLatin1String( "degree" ) )
1207  d->mMapUnits = QgsUnitTypes::DistanceDegrees;
1208  else
1209  {
1210  QgsDebugMsg( "Unsupported map units of " + unit );
1211  d->mMapUnits = QgsUnitTypes::DistanceUnknownUnit;
1212  }
1213  QgsDebugMsgLevel( "Projection has angular units of " + unit, 3 );
1214  }
1215 }
1216 
1217 
1219 {
1220  if ( d->mEllipsoidAcronym.isNull() || d->mProjectionAcronym.isNull()
1221  || !d->mIsValid )
1222  {
1223  QgsDebugMsg( "QgsCoordinateReferenceSystem::findMatchingProj will only "
1224  "work if prj acr ellipsoid acr and proj4string are set"
1225  " and the current projection is valid!" );
1226  return 0;
1227  }
1228 
1229  sqlite3 *myDatabase = nullptr;
1230  const char *myTail = nullptr;
1231  sqlite3_stmt *myPreparedStatement = nullptr;
1232  int myResult;
1233 
1234  // Set up the query to retrieve the projection information
1235  // needed to populate the list
1236  QString mySql = QString( "select srs_id,parameters from tbl_srs where "
1237  "projection_acronym=%1 and ellipsoid_acronym=%2 order by deprecated" )
1238  .arg( quotedValue( d->mProjectionAcronym ),
1239  quotedValue( d->mEllipsoidAcronym ) );
1240  // Get the full path name to the sqlite3 spatial reference database.
1241  QString myDatabaseFileName = QgsApplication::srsDatabaseFilePath();
1242 
1243  //check the db is available
1244  myResult = openDatabase( myDatabaseFileName, &myDatabase );
1245  if ( myResult != SQLITE_OK )
1246  {
1247  return 0;
1248  }
1249 
1250  myResult = sqlite3_prepare( myDatabase, mySql.toUtf8(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
1251 // XXX Need to free memory from the error msg if one is set
1252  if ( myResult == SQLITE_OK )
1253  {
1254 
1255  while ( sqlite3_step( myPreparedStatement ) == SQLITE_ROW )
1256  {
1257  QString mySrsId = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( myPreparedStatement, 0 ) ) );
1258  QString myProj4String = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( myPreparedStatement, 1 ) ) );
1259  if ( toProj4() == myProj4String.trimmed() )
1260  {
1261  QgsDebugMsg( "-------> MATCH FOUND in srs.db srsid: " + mySrsId );
1262  // close the sqlite3 statement
1263  sqlite3_finalize( myPreparedStatement );
1264  sqlite3_close( myDatabase );
1265  return mySrsId.toLong();
1266  }
1267  else
1268  {
1269 // QgsDebugMsg(QString(" Not matched : %1").arg(myProj4String));
1270  }
1271  }
1272  }
1273  QgsDebugMsg( "no match found in srs.db, trying user db now!" );
1274  // close the sqlite3 statement
1275  sqlite3_finalize( myPreparedStatement );
1276  sqlite3_close( myDatabase );
1277  //
1278  // Try the users db now
1279  //
1280 
1281  myDatabaseFileName = QgsApplication::qgisUserDatabaseFilePath();
1282  //check the db is available
1283  myResult = openDatabase( myDatabaseFileName, &myDatabase );
1284  if ( myResult != SQLITE_OK )
1285  {
1286  return 0;
1287  }
1288 
1289  myResult = sqlite3_prepare( myDatabase, mySql.toUtf8(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
1290 // XXX Need to free memory from the error msg if one is set
1291  if ( myResult == SQLITE_OK )
1292  {
1293 
1294  while ( sqlite3_step( myPreparedStatement ) == SQLITE_ROW )
1295  {
1296  QString mySrsId = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( myPreparedStatement, 0 ) ) );
1297  QString myProj4String = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( myPreparedStatement, 1 ) ) );
1298  if ( toProj4() == myProj4String.trimmed() )
1299  {
1300  QgsDebugMsg( "-------> MATCH FOUND in user qgis.db srsid: " + mySrsId );
1301  // close the sqlite3 statement
1302  sqlite3_finalize( myPreparedStatement );
1303  sqlite3_close( myDatabase );
1304  return mySrsId.toLong();
1305  }
1306  else
1307  {
1308 // QgsDebugMsg(QString(" Not matched : %1").arg(myProj4String));
1309  }
1310  }
1311  }
1312  QgsDebugMsg( "no match found in user db" );
1313 
1314  // close the sqlite3 statement
1315  sqlite3_finalize( myPreparedStatement );
1316  sqlite3_close( myDatabase );
1317  return 0;
1318 }
1319 
1321 {
1322  return ( !d->mIsValid && !theSrs.d->mIsValid ) ||
1323  ( d->mIsValid && theSrs.d->mIsValid && theSrs.authid() == authid() );
1324 }
1325 
1327 {
1328  return !( *this == theSrs );
1329 }
1330 
1332 {
1333  if ( d->mWkt.isEmpty() )
1334  {
1335  char *wkt = nullptr;
1336  if ( OSRExportToWkt( d->mCRS, &wkt ) == OGRERR_NONE )
1337  {
1338  d->mWkt = wkt;
1339  CPLFree( wkt );
1340  }
1341  }
1342  return d->mWkt;
1343 }
1344 
1345 bool QgsCoordinateReferenceSystem::readXml( const QDomNode & theNode )
1346 {
1347  d.detach();
1348  QgsDebugMsg( "Reading Spatial Ref Sys from xml ------------------------!" );
1349  bool result = true;
1350  QDomNode srsNode = theNode.namedItem( QStringLiteral( "spatialrefsys" ) );
1351 
1352  if ( ! srsNode.isNull() )
1353  {
1354  bool initialized = false;
1355 
1356  long srsid = srsNode.namedItem( QStringLiteral( "srsid" ) ).toElement().text().toLong();
1357 
1358  QDomNode myNode;
1359 
1360  if ( srsid < USER_CRS_START_ID )
1361  {
1362  myNode = srsNode.namedItem( QStringLiteral( "authid" ) );
1363  if ( !myNode.isNull() )
1364  {
1365  operator=( QgsCoordinateReferenceSystem::fromOgcWmsCrs( myNode.toElement().text() ) );
1366  if ( isValid() )
1367  {
1368  initialized = true;
1369  }
1370  }
1371 
1372  if ( !initialized )
1373  {
1374  myNode = srsNode.namedItem( QStringLiteral( "epsg" ) );
1375  if ( !myNode.isNull() )
1376  {
1377  operator=( QgsCoordinateReferenceSystem::fromEpsgId( myNode.toElement().text().toLong() ) );
1378  if ( isValid() )
1379  {
1380  initialized = true;
1381  }
1382  }
1383  }
1384  }
1385  else
1386  {
1387  QgsDebugMsg( "Ignoring authid/epsg for user crs." );
1388  }
1389 
1390  if ( initialized )
1391  {
1392  QgsDebugMsg( "Set from auth id" );
1393  }
1394  else
1395  {
1396  myNode = srsNode.namedItem( QStringLiteral( "proj4" ) );
1397 
1398  if ( createFromProj4( myNode.toElement().text() ) )
1399  {
1400  // createFromProj4() sets everything, including map units
1401  QgsDebugMsg( "Setting from proj4 string" );
1402  }
1403  else
1404  {
1405  QgsDebugMsg( "Setting from elements one by one" );
1406 
1407  myNode = srsNode.namedItem( QStringLiteral( "proj4" ) );
1408  setProj4String( myNode.toElement().text() );
1409 
1410  myNode = srsNode.namedItem( QStringLiteral( "srsid" ) );
1411  setInternalId( myNode.toElement().text().toLong() );
1412 
1413  myNode = srsNode.namedItem( QStringLiteral( "srid" ) );
1414  setSrid( myNode.toElement().text().toLong() );
1415 
1416  myNode = srsNode.namedItem( QStringLiteral( "authid" ) );
1417  setAuthId( myNode.toElement().text() );
1418 
1419  myNode = srsNode.namedItem( QStringLiteral( "description" ) );
1420  setDescription( myNode.toElement().text() );
1421 
1422  myNode = srsNode.namedItem( QStringLiteral( "projectionacronym" ) );
1423  setProjectionAcronym( myNode.toElement().text() );
1424 
1425  myNode = srsNode.namedItem( QStringLiteral( "ellipsoidacronym" ) );
1426  setEllipsoidAcronym( myNode.toElement().text() );
1427 
1428  myNode = srsNode.namedItem( QStringLiteral( "geographicflag" ) );
1429  if ( myNode.toElement().text().compare( QLatin1String( "true" ) ) )
1430  {
1431  setGeographicFlag( true );
1432  }
1433  else
1434  {
1435  setGeographicFlag( false );
1436  }
1437 
1438  //make sure the map units have been set
1439  setMapUnits();
1440 
1441  [email protected] this srs needs to be validated!!!
1442  d->mIsValid = true; //shamelessly hard coded for now
1443  }
1444  //TODO: createFromProj4 used to save to the user database any new CRS
1445  // this behavior was changed in order to separate creation and saving.
1446  // Not sure if it necessary to save it here, should be checked by someone
1447  // familiar with the code (should also give a more descriptive name to the generated CRS)
1448  if ( d->mSrsId == 0 )
1449  {
1450  QString myName = QStringLiteral( " * %1 (%2)" )
1451  .arg( QObject::tr( "Generated CRS", "A CRS automatically generated from layer info get this prefix for description" ),
1452  toProj4() );
1453  saveAsUserCrs( myName );
1454  }
1455 
1456  }
1457  }
1458  else
1459  {
1460  // Return default CRS if none was found in the XML.
1462  result = false;
1463  }
1464  return result;
1465 }
1466 
1467 bool QgsCoordinateReferenceSystem::writeXml( QDomNode & theNode, QDomDocument & theDoc ) const
1468 {
1469 
1470  QDomElement myLayerNode = theNode.toElement();
1471  QDomElement mySrsElement = theDoc.createElement( QStringLiteral( "spatialrefsys" ) );
1472 
1473  QDomElement myProj4Element = theDoc.createElement( QStringLiteral( "proj4" ) );
1474  myProj4Element.appendChild( theDoc.createTextNode( toProj4() ) );
1475  mySrsElement.appendChild( myProj4Element );
1476 
1477  QDomElement mySrsIdElement = theDoc.createElement( QStringLiteral( "srsid" ) );
1478  mySrsIdElement.appendChild( theDoc.createTextNode( QString::number( srsid() ) ) );
1479  mySrsElement.appendChild( mySrsIdElement );
1480 
1481  QDomElement mySridElement = theDoc.createElement( QStringLiteral( "srid" ) );
1482  mySridElement.appendChild( theDoc.createTextNode( QString::number( postgisSrid() ) ) );
1483  mySrsElement.appendChild( mySridElement );
1484 
1485  QDomElement myEpsgElement = theDoc.createElement( QStringLiteral( "authid" ) );
1486  myEpsgElement.appendChild( theDoc.createTextNode( authid() ) );
1487  mySrsElement.appendChild( myEpsgElement );
1488 
1489  QDomElement myDescriptionElement = theDoc.createElement( QStringLiteral( "description" ) );
1490  myDescriptionElement.appendChild( theDoc.createTextNode( description() ) );
1491  mySrsElement.appendChild( myDescriptionElement );
1492 
1493  QDomElement myProjectionAcronymElement = theDoc.createElement( QStringLiteral( "projectionacronym" ) );
1494  myProjectionAcronymElement.appendChild( theDoc.createTextNode( projectionAcronym() ) );
1495  mySrsElement.appendChild( myProjectionAcronymElement );
1496 
1497  QDomElement myEllipsoidAcronymElement = theDoc.createElement( QStringLiteral( "ellipsoidacronym" ) );
1498  myEllipsoidAcronymElement.appendChild( theDoc.createTextNode( ellipsoidAcronym() ) );
1499  mySrsElement.appendChild( myEllipsoidAcronymElement );
1500 
1501  QDomElement myGeographicFlagElement = theDoc.createElement( QStringLiteral( "geographicflag" ) );
1502  QString myGeoFlagText = QStringLiteral( "false" );
1503  if ( isGeographic() )
1504  {
1505  myGeoFlagText = QStringLiteral( "true" );
1506  }
1507 
1508  myGeographicFlagElement.appendChild( theDoc.createTextNode( myGeoFlagText ) );
1509  mySrsElement.appendChild( myGeographicFlagElement );
1510 
1511  myLayerNode.appendChild( mySrsElement );
1512 
1513  return true;
1514 }
1515 
1516 
1517 
1518 //
1519 // Static helper methods below this point only please!
1520 //
1521 
1522 
1523 // Returns the whole proj4 string for the selected srsid
1524 //this is a static method! NOTE I've made it private for now to reduce API clutter TS
1525 QString QgsCoordinateReferenceSystem::proj4FromSrsId( const int theSrsId )
1526 {
1527 
1528  QString myDatabaseFileName;
1529  QString myProjString;
1530  QString mySql = QStringLiteral( "select parameters from tbl_srs where srs_id = %1 order by deprecated" ).arg( theSrsId );
1531 
1532  QgsDebugMsg( "mySrsId = " + QString::number( theSrsId ) );
1533  QgsDebugMsg( "USER_CRS_START_ID = " + QString::number( USER_CRS_START_ID ) );
1534  QgsDebugMsg( "Selection sql : " + mySql );
1535 
1536  //
1537  // Determine if this is a user projection or a system on
1538  // user projection defs all have srs_id >= 100000
1539  //
1540  if ( theSrsId >= USER_CRS_START_ID )
1541  {
1542  myDatabaseFileName = QgsApplication::qgisUserDatabaseFilePath();
1543  QFileInfo myFileInfo;
1544  myFileInfo.setFile( myDatabaseFileName );
1545  if ( !myFileInfo.exists() ) //its unlikely that this condition will ever be reached
1546  {
1547  QgsDebugMsg( "users qgis.db not found" );
1548  return QString();
1549  }
1550  }
1551  else //must be a system projection then
1552  {
1553  myDatabaseFileName = QgsApplication::srsDatabaseFilePath();
1554  }
1555  QgsDebugMsg( "db = " + myDatabaseFileName );
1556 
1557  sqlite3 *db = nullptr;
1558  int rc;
1559  rc = openDatabase( myDatabaseFileName, &db );
1560  if ( rc )
1561  {
1562  return QString();
1563  }
1564  // prepare the sql statement
1565  const char *pzTail = nullptr;
1566  sqlite3_stmt *ppStmt = nullptr;
1567 
1568  rc = sqlite3_prepare( db, mySql.toUtf8(), mySql.toUtf8().length(), &ppStmt, &pzTail );
1569  // XXX Need to free memory from the error msg if one is set
1570 
1571  if ( rc == SQLITE_OK )
1572  {
1573  if ( sqlite3_step( ppStmt ) == SQLITE_ROW )
1574  {
1575  myProjString = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, 0 ) ) );
1576  }
1577  }
1578  // close the statement
1579  sqlite3_finalize( ppStmt );
1580  // close the database
1581  sqlite3_close( db );
1582 
1583  //Q_ASSERT(myProjString.length() > 0);
1584  return myProjString;
1585 }
1586 
1587 int QgsCoordinateReferenceSystem::openDatabase( const QString& path, sqlite3 **db, bool readonly )
1588 {
1589  QgsDebugMsgLevel( "path = " + path, 3 );
1590  int myResult = readonly
1591  ? sqlite3_open_v2( path.toUtf8().data(), db, SQLITE_OPEN_READONLY, nullptr )
1592  : sqlite3_open( path.toUtf8().data(), db );
1593 
1594  if ( myResult != SQLITE_OK )
1595  {
1596  QgsDebugMsg( "Can't open database: " + QString( sqlite3_errmsg( *db ) ) );
1597  // XXX This will likely never happen since on open, sqlite creates the
1598  // database if it does not exist.
1599  // ... unfortunately it happens on Windows
1600  QgsMessageLog::logMessage( QObject::tr( "Could not open CRS database %1\nError(%2): %3" )
1601  .arg( path )
1602  .arg( myResult )
1603  .arg( sqlite3_errmsg( *db ) ), QObject::tr( "CRS" ) );
1604  }
1605  return myResult;
1606 }
1607 
1609 {
1610  mCustomSrsValidation = f;
1611 }
1612 
1614 {
1615  return mCustomSrsValidation;
1616 }
1617 
1618 void QgsCoordinateReferenceSystem::debugPrint()
1619 {
1620  QgsDebugMsg( "***SpatialRefSystem***" );
1621  QgsDebugMsg( "* Valid : " + ( d->mIsValid ? QString( "true" ) : QString( "false" ) ) );
1622  QgsDebugMsg( "* SrsId : " + QString::number( d->mSrsId ) );
1623  QgsDebugMsg( "* Proj4 : " + toProj4() );
1624  QgsDebugMsg( "* WKT : " + toWkt() );
1625  QgsDebugMsg( "* Desc. : " + d->mDescription );
1627  {
1628  QgsDebugMsg( "* Units : meters" );
1629  }
1630  else if ( mapUnits() == QgsUnitTypes::DistanceFeet )
1631  {
1632  QgsDebugMsg( "* Units : feet" );
1633  }
1634  else if ( mapUnits() == QgsUnitTypes::DistanceDegrees )
1635  {
1636  QgsDebugMsg( "* Units : degrees" );
1637  }
1638 }
1639 
1641 {
1642  d.detach();
1643  d->mValidationHint = html;
1644 }
1645 
1647 {
1648  return d->mValidationHint;
1649 }
1650 
1653 
1655 {
1656  if ( !d->mIsValid )
1657  {
1658  QgsDebugMsg( "Can't save an invalid CRS!" );
1659  return false;
1660  }
1661 
1662  QString mySql;
1663 
1664  QString proj4String = d->mProj4;
1665  if ( proj4String.isEmpty() )
1666  {
1667  proj4String = toProj4();
1668  }
1669 
1670  //if this is the first record we need to ensure that its srs_id is 10000. For
1671  //any rec after that sqlite3 will take care of the autonumbering
1672  //this was done to support sqlite 3.0 as it does not yet support
1673  //the autoinc related system tables.
1674  if ( getRecordCount() == 0 )
1675  {
1676  mySql = "insert into tbl_srs (srs_id,description,projection_acronym,ellipsoid_acronym,parameters,is_geo) values ("
1677  + QString::number( USER_CRS_START_ID )
1678  + ',' + quotedValue( name )
1679  + ',' + quotedValue( projectionAcronym() )
1680  + ',' + quotedValue( ellipsoidAcronym() )
1681  + ',' + quotedValue( toProj4() )
1682  + ",0)"; // <-- is_geo shamelessly hard coded for now
1683  }
1684  else
1685  {
1686  mySql = "insert into tbl_srs (description,projection_acronym,ellipsoid_acronym,parameters,is_geo) values ("
1687  + quotedValue( name )
1688  + ',' + quotedValue( projectionAcronym() )
1689  + ',' + quotedValue( ellipsoidAcronym() )
1690  + ',' + quotedValue( toProj4() )
1691  + ",0)"; // <-- is_geo shamelessly hard coded for now
1692  }
1693  sqlite3 *myDatabase = nullptr;
1694  const char *myTail = nullptr;
1695  sqlite3_stmt *myPreparedStatement = nullptr;
1696  int myResult;
1697  //check the db is available
1698  myResult = sqlite3_open( QgsApplication::qgisUserDatabaseFilePath().toUtf8().data(), &myDatabase );
1699  if ( myResult != SQLITE_OK )
1700  {
1701  QgsDebugMsg( QString( "Can't open or create database %1: %2" )
1703  sqlite3_errmsg( myDatabase ) ) );
1704  return false;
1705  }
1706  QgsDebugMsg( QString( "Update or insert sql \n%1" ).arg( mySql ) );
1707  myResult = sqlite3_prepare( myDatabase, mySql.toUtf8(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
1708 
1709  qint64 return_id;
1710  if ( myResult == SQLITE_OK && sqlite3_step( myPreparedStatement ) == SQLITE_DONE )
1711  {
1712  QgsMessageLog::logMessage( QObject::tr( "Saved user CRS [%1]" ).arg( toProj4() ), QObject::tr( "CRS" ) );
1713 
1714  return_id = sqlite3_last_insert_rowid( myDatabase );
1715  setInternalId( return_id );
1716 
1717  //We add the just created user CRS to the list of recently used CRS
1718  QSettings settings;
1719  //QStringList recentProjections = settings.value( "/UI/recentProjections" ).toStringList();
1720  QStringList projectionsProj4 = settings.value( QStringLiteral( "/UI/recentProjectionsProj4" ) ).toStringList();
1721  QStringList projectionsAuthId = settings.value( QStringLiteral( "/UI/recentProjectionsAuthId" ) ).toStringList();
1722  //recentProjections.append();
1723  //settings.setValue( "/UI/recentProjections", recentProjections );
1724  projectionsProj4.append( toProj4() );
1725  projectionsAuthId.append( authid() );
1726  settings.setValue( QStringLiteral( "/UI/recentProjectionsProj4" ), projectionsProj4 );
1727  settings.setValue( QStringLiteral( "/UI/recentProjectionsAuthId" ), projectionsAuthId );
1728 
1729  }
1730  else
1731  return_id = -1;
1732  return return_id;
1733 }
1734 
1735 long QgsCoordinateReferenceSystem::getRecordCount()
1736 {
1737  sqlite3 *myDatabase = nullptr;
1738  const char *myTail = nullptr;
1739  sqlite3_stmt *myPreparedStatement = nullptr;
1740  int myResult;
1741  long myRecordCount = 0;
1742  //check the db is available
1743  myResult = sqlite3_open_v2( QgsApplication::qgisUserDatabaseFilePath().toUtf8().data(), &myDatabase, SQLITE_OPEN_READONLY, nullptr );
1744  if ( myResult != SQLITE_OK )
1745  {
1746  QgsDebugMsg( QString( "Can't open database: %1" ).arg( sqlite3_errmsg( myDatabase ) ) );
1747  return 0;
1748  }
1749  // Set up the query to retrieve the projection information needed to populate the ELLIPSOID list
1750  QString mySql = QStringLiteral( "select count(*) from tbl_srs" );
1751  myResult = sqlite3_prepare( myDatabase, mySql.toUtf8(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
1752  // XXX Need to free memory from the error msg if one is set
1753  if ( myResult == SQLITE_OK )
1754  {
1755  if ( sqlite3_step( myPreparedStatement ) == SQLITE_ROW )
1756  {
1757  QString myRecordCountString = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( myPreparedStatement, 0 ) ) );
1758  myRecordCount = myRecordCountString.toLong();
1759  }
1760  }
1761  // close the sqlite3 statement
1762  sqlite3_finalize( myPreparedStatement );
1763  sqlite3_close( myDatabase );
1764  return myRecordCount;
1765 }
1766 
1767 QString QgsCoordinateReferenceSystem::quotedValue( QString value )
1768 {
1769  value.replace( '\'', QLatin1String( "''" ) );
1770  return value.prepend( '\'' ).append( '\'' );
1771 }
1772 
1773 // adapted from gdal/ogr/ogr_srs_dict.cpp
1774 bool QgsCoordinateReferenceSystem::loadWkts( QHash<int, QString> &wkts, const char *filename )
1775 {
1776  qDebug( "Loading %s", filename );
1777  const char *pszFilename = CPLFindFile( "gdal", filename );
1778  if ( !pszFilename )
1779  return false;
1780 
1781  QFile csv( pszFilename );
1782  if ( !csv.open( QIODevice::ReadOnly ) )
1783  return false;
1784 
1785  QTextStream lines( &csv );
1786 
1787  for ( ;; )
1788  {
1789  QString line = lines.readLine();
1790  if ( line.isNull() )
1791  break;
1792 
1793  if ( line.startsWith( '#' ) )
1794  {
1795  continue;
1796  }
1797  else if ( line.startsWith( QLatin1String( "include " ) ) )
1798  {
1799  if ( !loadWkts( wkts, line.mid( 8 ).toUtf8() ) )
1800  break;
1801  }
1802  else
1803  {
1804  int pos = line.indexOf( ',' );
1805  if ( pos < 0 )
1806  return false;
1807 
1808  bool ok;
1809  int epsg = line.leftRef( pos ).toInt( &ok );
1810  if ( !ok )
1811  return false;
1812 
1813  wkts.insert( epsg, line.mid( pos + 1 ) );
1814  }
1815  }
1816 
1817  csv.close();
1818 
1819  return true;
1820 }
1821 
1822 bool QgsCoordinateReferenceSystem::loadIds( QHash<int, QString> &wkts )
1823 {
1824  OGRSpatialReferenceH crs = OSRNewSpatialReference( nullptr );
1825 
1826  Q_FOREACH ( const QString& csv, QStringList() << "gcs.csv" << "pcs.csv" << "vertcs.csv" << "compdcs.csv" << "geoccs.csv" )
1827  {
1828  QString filename = CPLFindFile( "gdal", csv.toUtf8() );
1829 
1830  QFile f( filename );
1831  if ( !f.open( QIODevice::ReadOnly ) )
1832  continue;
1833 
1834  QTextStream lines( &f );
1835  int l = 0, n = 0;
1836 
1837  lines.readLine();
1838  for ( ;; )
1839  {
1840  l++;
1841  QString line = lines.readLine();
1842  if ( line.isNull() )
1843  break;
1844 
1845  int pos = line.indexOf( ',' );
1846  if ( pos < 0 )
1847  continue;
1848 
1849  bool ok;
1850  int epsg = line.leftRef( pos ).toInt( &ok );
1851  if ( !ok )
1852  continue;
1853 
1854  // some CRS are known to fail (see http://trac.osgeo.org/gdal/ticket/2900)
1855  if ( epsg == 2218 || epsg == 2221 || epsg == 2296 || epsg == 2297 || epsg == 2298 || epsg == 2299 || epsg == 2300 || epsg == 2301 || epsg == 2302 ||
1856  epsg == 2303 || epsg == 2304 || epsg == 2305 || epsg == 2306 || epsg == 2307 || epsg == 2963 || epsg == 2985 || epsg == 2986 || epsg == 3052 ||
1857  epsg == 3053 || epsg == 3139 || epsg == 3144 || epsg == 3145 || epsg == 3173 || epsg == 3295 || epsg == 3993 || epsg == 4087 || epsg == 4088 ||
1858  epsg == 5017 || epsg == 5221 || epsg == 5224 || epsg == 5225 || epsg == 5514 || epsg == 5515 || epsg == 5516 || epsg == 5819 || epsg == 5820 ||
1859  epsg == 5821 || epsg == 6200 || epsg == 6201 || epsg == 6202 || epsg == 6244 || epsg == 6245 || epsg == 6246 || epsg == 6247 || epsg == 6248 ||
1860  epsg == 6249 || epsg == 6250 || epsg == 6251 || epsg == 6252 || epsg == 6253 || epsg == 6254 || epsg == 6255 || epsg == 6256 || epsg == 6257 ||
1861  epsg == 6258 || epsg == 6259 || epsg == 6260 || epsg == 6261 || epsg == 6262 || epsg == 6263 || epsg == 6264 || epsg == 6265 || epsg == 6266 ||
1862  epsg == 6267 || epsg == 6268 || epsg == 6269 || epsg == 6270 || epsg == 6271 || epsg == 6272 || epsg == 6273 || epsg == 6274 || epsg == 6275 ||
1863  epsg == 32600 || epsg == 32663 || epsg == 32700 )
1864  continue;
1865 
1866  if ( OSRImportFromEPSG( crs, epsg ) != OGRERR_NONE )
1867  {
1868  qDebug( "EPSG %d: not imported", epsg );
1869  continue;
1870  }
1871 
1872  char *wkt = nullptr;
1873  if ( OSRExportToWkt( crs, &wkt ) != OGRERR_NONE )
1874  {
1875  qWarning( "EPSG %d: not exported to WKT", epsg );
1876  continue;
1877  }
1878 
1879  wkts.insert( epsg, wkt );
1880  n++;
1881 
1882  CPLFree( wkt );
1883  }
1884 
1885  f.close();
1886 
1887  qDebug( "Loaded %d/%d from %s", n, l, filename.toUtf8().constData() );
1888  }
1889 
1890  OSRDestroySpatialReference( crs );
1891 
1892  return true;
1893 }
1894 
1896 {
1897  QString dbFilePath = QgsApplication::srsDatabaseFilePath();
1898  syncDatumTransform( dbFilePath );
1899 
1900  int inserted = 0, updated = 0, deleted = 0, errors = 0;
1901 
1902  qDebug( "Load srs db from: %s", QgsApplication::srsDatabaseFilePath().toLocal8Bit().constData() );
1903 
1904  sqlite3 *database = nullptr;
1905  if ( sqlite3_open( dbFilePath.toUtf8().constData(), &database ) != SQLITE_OK )
1906  {
1907  qCritical( "Could not open database: %s [%s]\n", QgsApplication::srsDatabaseFilePath().toLocal8Bit().constData(), sqlite3_errmsg( database ) );
1908  return -1;
1909  }
1910 
1911  if ( sqlite3_exec( database, "BEGIN TRANSACTION", nullptr, nullptr, nullptr ) != SQLITE_OK )
1912  {
1913  qCritical( "Could not begin transaction: %s [%s]\n", QgsApplication::srsDatabaseFilePath().toLocal8Bit().constData(), sqlite3_errmsg( database ) );
1914  return -1;
1915  }
1916 
1917  // fix up database, if not done already //
1918  if ( sqlite3_exec( database, "alter table tbl_srs add noupdate boolean", nullptr, nullptr, nullptr ) == SQLITE_OK )
1919  ( void )sqlite3_exec( database, "update tbl_srs set noupdate=(auth_name='EPSG' and auth_id in (5513,5514,5221,2065,102067,4156,4818))", nullptr, 0, 0 );
1920 
1921  ( void )sqlite3_exec( database, "UPDATE tbl_srs SET srid=141001 WHERE srid=41001 AND auth_name='OSGEO' AND auth_id='41001'", nullptr, 0, 0 );
1922 
1923  OGRSpatialReferenceH crs = OSRNewSpatialReference( nullptr );
1924  const char *tail = nullptr;
1925  sqlite3_stmt *select = nullptr;
1926  char *errMsg = nullptr;
1927 
1928  QString proj4;
1929  QString sql;
1930  QHash<int, QString> wkts;
1931  loadIds( wkts );
1932  loadWkts( wkts, "epsg.wkt" );
1933 
1934  qDebug( "%d WKTs loaded", wkts.count() );
1935 
1936  for ( QHash<int, QString>::const_iterator it = wkts.constBegin(); it != wkts.constEnd(); ++it )
1937  {
1938  QByteArray ba( it.value().toUtf8() );
1939  char *psz = ba.data();
1940  OGRErr ogrErr = OSRImportFromWkt( crs, &psz );
1941  if ( ogrErr != OGRERR_NONE )
1942  continue;
1943 
1944  if ( OSRExportToProj4( crs, &psz ) != OGRERR_NONE )
1945  continue;
1946 
1947  proj4 = psz;
1948  proj4 = proj4.trimmed();
1949 
1950  CPLFree( psz );
1951 
1952  if ( proj4.isEmpty() )
1953  continue;
1954 
1955  sql = QStringLiteral( "SELECT parameters,noupdate FROM tbl_srs WHERE auth_name='EPSG' AND auth_id='%1'" ).arg( it.key() );
1956  if ( sqlite3_prepare( database, sql.toLatin1(), sql.size(), &select, &tail ) != SQLITE_OK )
1957  {
1958  qCritical( "Could not prepare: %s [%s]\n", sql.toLatin1().constData(), sqlite3_errmsg( database ) );
1959  continue;
1960  }
1961 
1962  QString srsProj4;
1963  if ( sqlite3_step( select ) == SQLITE_ROW )
1964  {
1965  srsProj4 = reinterpret_cast< const char * >( sqlite3_column_text( select, 0 ) );
1966 
1967  if ( QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( select, 1 ) ) ).toInt() != 0 )
1968  continue;
1969  }
1970 
1971  sqlite3_finalize( select );
1972 
1973  if ( !srsProj4.isEmpty() )
1974  {
1975  if ( proj4 != srsProj4 )
1976  {
1977  errMsg = nullptr;
1978  sql = QStringLiteral( "UPDATE tbl_srs SET parameters=%1 WHERE auth_name='EPSG' AND auth_id=%2" ).arg( quotedValue( proj4 ) ).arg( it.key() );
1979 
1980  if ( sqlite3_exec( database, sql.toUtf8(), nullptr, nullptr, &errMsg ) != SQLITE_OK )
1981  {
1982  qCritical( "Could not execute: %s [%s/%s]\n",
1983  sql.toLocal8Bit().constData(),
1984  sqlite3_errmsg( database ),
1985  errMsg ? errMsg : "(unknown error)" );
1986  errors++;
1987  }
1988  else
1989  {
1990  updated++;
1991  QgsDebugMsgLevel( QString( "SQL: %1\n OLD:%2\n NEW:%3" ).arg( sql, srsProj4, proj4 ), 3 );
1992  }
1993  }
1994  }
1995  else
1996  {
1997  QRegExp projRegExp( "\\+proj=(\\S+)" );
1998  if ( projRegExp.indexIn( proj4 ) < 0 )
1999  {
2000  QgsDebugMsg( QString( "EPSG %1: no +proj argument found [%2]" ).arg( it.key() ).arg( proj4 ) );
2001  continue;
2002  }
2003 
2004  QRegExp ellipseRegExp( "\\+ellps=(\\S+)" );
2005  QString ellps;
2006  if ( ellipseRegExp.indexIn( proj4 ) >= 0 )
2007  {
2008  ellps = ellipseRegExp.cap( 1 );
2009  }
2010 
2011  QString name( OSRIsGeographic( crs ) ? OSRGetAttrValue( crs, "GEOCS", 0 ) : OSRGetAttrValue( crs, "PROJCS", 0 ) );
2012  if ( name.isEmpty() )
2013  name = QObject::tr( "Imported from GDAL" );
2014 
2015  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)" )
2016  .arg( quotedValue( name ),
2017  quotedValue( projRegExp.cap( 1 ) ),
2018  quotedValue( ellps ),
2019  quotedValue( proj4 ) )
2020  .arg( it.key() )
2021  .arg( OSRIsGeographic( crs ) );
2022 
2023  errMsg = nullptr;
2024  if ( sqlite3_exec( database, sql.toUtf8(), nullptr, nullptr, &errMsg ) == SQLITE_OK )
2025  {
2026  inserted++;
2027  }
2028  else
2029  {
2030  qCritical( "Could not execute: %s [%s/%s]\n",
2031  sql.toLocal8Bit().constData(),
2032  sqlite3_errmsg( database ),
2033  errMsg ? errMsg : "(unknown error)" );
2034  errors++;
2035 
2036  if ( errMsg )
2037  sqlite3_free( errMsg );
2038  }
2039  }
2040  }
2041 
2042  sql = QStringLiteral( "DELETE FROM tbl_srs WHERE auth_name='EPSG' AND NOT auth_id IN (" );
2043  QString delim;
2044  QHash<int, QString>::const_iterator it = wkts.constBegin();
2045  for ( ; it != wkts.constEnd(); ++it )
2046  {
2047  sql += delim + QString::number( it.key() );
2048  delim = ',';
2049  }
2050  sql += QLatin1String( ") AND NOT noupdate" );
2051 
2052  if ( sqlite3_exec( database, sql.toUtf8(), nullptr, nullptr, nullptr ) == SQLITE_OK )
2053  {
2054  deleted = sqlite3_changes( database );
2055  }
2056  else
2057  {
2058  errors++;
2059  qCritical( "Could not execute: %s [%s]\n",
2060  sql.toLocal8Bit().constData(),
2061  sqlite3_errmsg( database ) );
2062  }
2063 
2064 #if !defined(PJ_VERSION) || PJ_VERSION!=470
2065  sql = QStringLiteral( "select auth_name,auth_id,parameters from tbl_srs WHERE auth_name<>'EPSG' AND NOT deprecated AND NOT noupdate" );
2066  if ( sqlite3_prepare( database, sql.toLatin1(), sql.size(), &select, &tail ) == SQLITE_OK )
2067  {
2068  while ( sqlite3_step( select ) == SQLITE_ROW )
2069  {
2070  const char *auth_name = reinterpret_cast< const char * >( sqlite3_column_text( select, 0 ) );
2071  const char *auth_id = reinterpret_cast< const char * >( sqlite3_column_text( select, 1 ) );
2072  const char *params = reinterpret_cast< const char * >( sqlite3_column_text( select, 2 ) );
2073 
2074  QString input = QStringLiteral( "+init=%1:%2" ).arg( QString( auth_name ).toLower(), auth_id );
2075  projPJ pj = pj_init_plus( input.toLatin1() );
2076  if ( !pj )
2077  {
2078  input = QStringLiteral( "+init=%1:%2" ).arg( QString( auth_name ).toUpper(), auth_id );
2079  pj = pj_init_plus( input.toLatin1() );
2080  }
2081 
2082  if ( pj )
2083  {
2084  char *def = pj_get_def( pj, 0 );
2085  if ( def )
2086  {
2087  proj4 = def;
2088  pj_dalloc( def );
2089 
2090  input.prepend( ' ' ).append( ' ' );
2091  if ( proj4.startsWith( input ) )
2092  {
2093  proj4 = proj4.mid( input.size() );
2094  proj4 = proj4.trimmed();
2095  }
2096 
2097  if ( proj4 != params )
2098  {
2099  sql = QStringLiteral( "UPDATE tbl_srs SET parameters=%1 WHERE auth_name=%2 AND auth_id=%3" )
2100  .arg( quotedValue( proj4 ),
2101  quotedValue( auth_name ),
2102  quotedValue( auth_id ) );
2103 
2104  if ( sqlite3_exec( database, sql.toUtf8(), nullptr, nullptr, &errMsg ) == SQLITE_OK )
2105  {
2106  updated++;
2107  QgsDebugMsgLevel( QString( "SQL: %1\n OLD:%2\n NEW:%3" ).arg( sql, params, proj4 ), 3 );
2108  }
2109  else
2110  {
2111  qCritical( "Could not execute: %s [%s/%s]\n",
2112  sql.toLocal8Bit().constData(),
2113  sqlite3_errmsg( database ),
2114  errMsg ? errMsg : "(unknown error)" );
2115  errors++;
2116  }
2117  }
2118  }
2119  else
2120  {
2121  QgsDebugMsg( QString( "could not retrieve proj string for %1 from PROJ" ).arg( input ) );
2122  }
2123  }
2124  else
2125  {
2126  QgsDebugMsgLevel( QString( "could not retrieve crs for %1 from PROJ" ).arg( input ), 3 );
2127  }
2128 
2129  pj_free( pj );
2130  }
2131  }
2132  else
2133  {
2134  errors++;
2135  qCritical( "Could not execute: %s [%s]\n",
2136  sql.toLocal8Bit().constData(),
2137  sqlite3_errmsg( database ) );
2138  }
2139 #endif
2140 
2141  OSRDestroySpatialReference( crs );
2142 
2143  if ( sqlite3_exec( database, "COMMIT", nullptr, nullptr, nullptr ) != SQLITE_OK )
2144  {
2145  qCritical( "Could not commit transaction: %s [%s]\n", QgsApplication::srsDatabaseFilePath().toLocal8Bit().constData(), sqlite3_errmsg( database ) );
2146  return -1;
2147  }
2148 
2149  sqlite3_close( database );
2150 
2151  qWarning( "CRS update (inserted:%d updated:%d deleted:%d errors:%d)", inserted, updated, deleted, errors );
2152 
2153  if ( errors > 0 )
2154  return -errors;
2155  else
2156  return updated + inserted;
2157 }
2158 
2159 bool QgsCoordinateReferenceSystem::syncDatumTransform( const QString& dbPath )
2160 {
2161  const char *filename = CSVFilename( "datum_shift.csv" );
2162  FILE *fp = VSIFOpen( filename, "rb" );
2163  if ( !fp )
2164  {
2165  return false;
2166  }
2167 
2168  char **fieldnames = CSVReadParseLine( fp );
2169 
2170  // "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"
2171 
2172  struct
2173  {
2174  const char *src; //skip-init-check
2175  const char *dst; //skip-init-check
2176  int idx;
2177  } map[] =
2178  {
2179  // { "SEQ_KEY", "", -1 },
2180  { "SOURCE_CRS_CODE", "source_crs_code", -1 },
2181  { "TARGET_CRS_CODE", "target_crs_code", -1 },
2182  { "REMARKS", "remarks", -1 },
2183  { "COORD_OP_SCOPE", "scope", -1 },
2184  { "AREA_OF_USE_CODE", "area_of_use_code", -1 },
2185  // { "AREA_SOUTH_BOUND_LAT", "", -1 },
2186  // { "AREA_NORTH_BOUND_LAT", "", -1 },
2187  // { "AREA_WEST_BOUND_LON", "", -1 },
2188  // { "AREA_EAST_BOUND_LON", "", -1 },
2189  // { "SHOW_OPERATION", "", -1 },
2190  { "DEPRECATED", "deprecated", -1 },
2191  { "COORD_OP_METHOD_CODE", "coord_op_method_code", -1 },
2192  { "DX", "p1", -1 },
2193  { "DY", "p2", -1 },
2194  { "DZ", "p3", -1 },
2195  { "RX", "p4", -1 },
2196  { "RY", "p5", -1 },
2197  { "RZ", "p6", -1 },
2198  { "DS", "p7", -1 },
2199  { "PREFERRED", "preferred", -1 },
2200  { "COORD_OP_CODE", "coord_op_code", -1 },
2201  };
2202 
2203  QString update = QStringLiteral( "UPDATE tbl_datum_transform SET " );
2204  QString insert, values;
2205 
2206  int n = CSLCount( fieldnames );
2207 
2208  int idxid = -1, idxrx = -1, idxry = -1, idxrz = -1, idxmcode = -1;
2209  for ( unsigned int i = 0; i < sizeof( map ) / sizeof( *map ); i++ )
2210  {
2211  bool last = i == sizeof( map ) / sizeof( *map ) - 1;
2212 
2213  map[i].idx = CSLFindString( fieldnames, map[i].src );
2214  if ( map[i].idx < 0 )
2215  {
2216  qWarning( "field %s not found", map[i].src );
2217  CSLDestroy( fieldnames );
2218  fclose( fp );
2219  return false;
2220  }
2221 
2222  if ( strcmp( map[i].src, "COORD_OP_CODE" ) == 0 )
2223  idxid = i;
2224  if ( strcmp( map[i].src, "RX" ) == 0 )
2225  idxrx = i;
2226  if ( strcmp( map[i].src, "RY" ) == 0 )
2227  idxry = i;
2228  if ( strcmp( map[i].src, "RZ" ) == 0 )
2229  idxrz = i;
2230  if ( strcmp( map[i].src, "COORD_OP_METHOD_CODE" ) == 0 )
2231  idxmcode = i;
2232 
2233  if ( i > 0 )
2234  {
2235  insert += ',';
2236  values += ',';
2237 
2238  if ( last )
2239  {
2240  update += QLatin1String( " WHERE " );
2241  }
2242  else
2243  {
2244  update += ',';
2245  }
2246  }
2247 
2248  update += QStringLiteral( "%1=%%2" ).arg( map[i].dst ).arg( i + 1 );
2249 
2250  insert += map[i].dst;
2251  values += QStringLiteral( "%%1" ).arg( i + 1 );
2252  }
2253 
2254  insert = "INSERT INTO tbl_datum_transform(" + insert + ") VALUES (" + values + ')';
2255 
2256  QgsDebugMsgLevel( QString( "insert:%1" ).arg( insert ), 4 );
2257  QgsDebugMsgLevel( QString( "update:%1" ).arg( update ), 4 );
2258 
2259  CSLDestroy( fieldnames );
2260 
2261  Q_ASSERT( idxid >= 0 );
2262  Q_ASSERT( idxrx >= 0 );
2263  Q_ASSERT( idxry >= 0 );
2264  Q_ASSERT( idxrz >= 0 );
2265 
2266  sqlite3 *db = nullptr;
2267  int openResult = sqlite3_open( dbPath.toUtf8().constData(), &db );
2268  if ( openResult != SQLITE_OK )
2269  {
2270  fclose( fp );
2271  return false;
2272  }
2273 
2274  if ( sqlite3_exec( db, "BEGIN TRANSACTION", nullptr, nullptr, nullptr ) != SQLITE_OK )
2275  {
2276  qCritical( "Could not begin transaction: %s [%s]\n", QgsApplication::srsDatabaseFilePath().toLocal8Bit().constData(), sqlite3_errmsg( db ) );
2277  sqlite3_close( db );
2278  fclose( fp );
2279  return false;
2280  }
2281 
2282  QStringList v;
2283  v.reserve( sizeof( map ) / sizeof( *map ) );
2284 
2285  while ( !feof( fp ) )
2286  {
2287  char **values = CSVReadParseLine( fp );
2288 
2289  v.clear();
2290 
2291  if ( CSLCount( values ) == 0 )
2292  break;
2293 
2294  if ( CSLCount( values ) < n )
2295  {
2296  qWarning( "Only %d columns", CSLCount( values ) );
2297  continue;
2298  }
2299 
2300  for ( unsigned int i = 0; i < sizeof( map ) / sizeof( *map ); i++ )
2301  {
2302  int idx = map[i].idx;
2303  Q_ASSERT( idx != -1 );
2304  Q_ASSERT( idx < n );
2305  v.insert( i, *values[ idx ] ? quotedValue( values[idx] ) : QStringLiteral( "NULL" ) );
2306  }
2307 
2308  //switch sign of rotation parameters. See http://trac.osgeo.org/proj/wiki/GenParms#towgs84-DatumtransformationtoWGS84
2309  if ( v.at( idxmcode ).compare( QLatin1String( "'9607'" ) ) == 0 )
2310  {
2311  v[ idxmcode ] = QStringLiteral( "'9606'" );
2312  v[ idxrx ] = '\'' + qgsDoubleToString( -( v[ idxrx ].remove( '\'' ).toDouble() ) ) + '\'';
2313  v[ idxry ] = '\'' + qgsDoubleToString( -( v[ idxry ].remove( '\'' ).toDouble() ) ) + '\'';
2314  v[ idxrz ] = '\'' + qgsDoubleToString( -( v[ idxrz ].remove( '\'' ).toDouble() ) ) + '\'';
2315  }
2316 
2317  //entry already in db?
2318  sqlite3_stmt *stmt = nullptr;
2319  QString cOpCode;
2320  QString sql = QStringLiteral( "SELECT coord_op_code FROM tbl_datum_transform WHERE coord_op_code=%1" ).arg( v[ idxid ] );
2321  int prepareRes = sqlite3_prepare( db, sql.toLatin1(), sql.size(), &stmt, nullptr );
2322  if ( prepareRes != SQLITE_OK )
2323  continue;
2324 
2325  if ( sqlite3_step( stmt ) == SQLITE_ROW )
2326  {
2327  cOpCode = reinterpret_cast< const char * >( sqlite3_column_text( stmt, 0 ) );
2328  }
2329  sqlite3_finalize( stmt );
2330 
2331  sql = cOpCode.isEmpty() ? insert : update;
2332  for ( int i = 0; i < v.size(); i++ )
2333  {
2334  sql = sql.arg( v[i] );
2335  }
2336 
2337  if ( sqlite3_exec( db, sql.toUtf8(), nullptr, nullptr, nullptr ) != SQLITE_OK )
2338  {
2339  qCritical( "SQL: %s", sql.toUtf8().constData() );
2340  qCritical( "Error: %s", sqlite3_errmsg( db ) );
2341  }
2342  }
2343 
2344  if ( sqlite3_exec( db, "COMMIT", nullptr, nullptr, nullptr ) != SQLITE_OK )
2345  {
2346  qCritical( "Could not commit transaction: %s [%s]\n", QgsApplication::srsDatabaseFilePath().toLocal8Bit().constData(), sqlite3_errmsg( db ) );
2347  return false;
2348  }
2349 
2350  sqlite3_close( db );
2351  return true;
2352 }
2353 
2355 {
2356  if ( isGeographic() )
2357  {
2358  return d->mAuthId;
2359  }
2360  else if ( d->mCRS )
2361  {
2362  return OSRGetAuthorityName( d->mCRS, "GEOGCS" ) + QStringLiteral( ":" ) + OSRGetAuthorityCode( d->mCRS, "GEOGCS" );
2363  }
2364  else
2365  {
2366  return QLatin1String( "" );
2367  }
2368 }
2369 
2371 {
2372  QStringList projections;
2373 
2374  // Read settings from persistent storage
2375  QSettings settings;
2376  projections = settings.value( QStringLiteral( "/UI/recentProjections" ) ).toStringList();
2377  /*** The reading (above) of internal id from persistent storage should be removed sometime in the future */
2378  /*** This is kept now for backwards compatibility */
2379 
2380  QStringList projectionsProj4 = settings.value( QStringLiteral( "/UI/recentProjectionsProj4" ) ).toStringList();
2381  QStringList projectionsAuthId = settings.value( QStringLiteral( "/UI/recentProjectionsAuthId" ) ).toStringList();
2382  if ( projectionsAuthId.size() >= projections.size() )
2383  {
2384  // We had saved state with AuthId and Proj4. Use that instead
2385  // to find out the crs id
2386  projections.clear();
2387  for ( int i = 0; i < projectionsAuthId.size(); i++ )
2388  {
2389  // Create a crs from the EPSG
2391  crs.createFromOgcWmsCrs( projectionsAuthId.at( i ) );
2392  if ( ! crs.isValid() )
2393  {
2394  // Couldn't create from EPSG, try the Proj4 string instead
2395  if ( i >= projectionsProj4.size() || !crs.createFromProj4( projectionsProj4.at( i ) ) )
2396  {
2397  // No? Skip this entry
2398  continue;
2399  }
2400  //If the CRS can be created but do not correspond to a CRS in the database, skip it (for example a deleted custom CRS)
2401  if ( crs.srsid() == 0 )
2402  {
2403  continue;
2404  }
2405  }
2406  projections << QString::number( crs.srsid() );
2407  }
2408  }
2409  return projections;
2410 }
2411 
2413 {
2414  sSrIdCacheLock.lockForWrite();
2415  sSrIdCache.clear();
2416  sSrIdCacheLock.unlock();
2417  sOgcLock.lockForWrite();
2418  sOgcCache.clear();
2419  sOgcLock.unlock();
2420  sProj4CacheLock.lockForWrite();
2421  sProj4Cache.clear();
2422  sProj4CacheLock.unlock();
2423  sCRSWktLock.lockForWrite();
2424  sWktCache.clear();
2425  sCRSWktLock.unlock();
2426  sCRSSrsIdLock.lockForWrite();
2427  sSrsIdCache.clear();
2428  sCRSSrsIdLock.unlock();
2429  sCrsStringLock.lockForWrite();
2430  sStringCache.clear();
2431  sCrsStringLock.unlock();
2432 }
QString geographicCrsAuthId() const
Returns auth id of related geographic CRS.
QgsCoordinateReferenceSystem()
Constructs an invalid CRS object.
bool createFromSrid(const long theSrid)
Sets this CRS by lookup of the given PostGIS SRID in the CRS database.
static QgsCoordinateReferenceSystem fromProj4(const QString &proj4)
Creates a CRS from a proj4 style formatted string.
bool createFromWkt(const QString &theWkt)
Sets this CRS using a WKT definition.
static QList< long > validSrsIds()
Returns a list of all valid SRS IDs present in the CRS database.
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 createFromId(const long theId, CrsType theType=PostgisCrsId)
Sets this CRS by lookup of the given ID in the CRS database.
bool createFromOgcWmsCrs(const QString &theCrs)
Sets this CRS to the given OGC WMS-format Coordinate Reference Systems.
bool writeXml(QDomNode &theNode, QDomDocument &theDoc) const
Stores state to the given Dom node in the given document.
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.
bool createFromString(const QString &theDefinition)
Set up this CRS from a string definition.
#define FEET_TO_METER
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.
bool createFromSrsId(const long theSrsId)
Sets this CRS by lookup of internal QGIS CRS ID in the CRS database.
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 operator!=(const QgsCoordinateReferenceSystem &theSrs) const
Overloaded != operator used to compare to CRS&#39;s.
static QStringList recentProjections()
Returns a list of recently used projections.
QString validationHint()
Get user hint for validation.
bool createFromUserInput(const QString &theDefinition)
Set up this CRS from various text formats.
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:300
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:51
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)
CrsType
Enumeration of types of IDs accepted in createFromId() method.
struct sqlite3 sqlite3
bool operator==(const QgsCoordinateReferenceSystem &theSrs) const
Overloaded == operator used to compare to CRS&#39;s.
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:43
Unknown distance unit.
Definition: qgsunittypes.h:52
const int USER_CRS_START_ID
Magick number that determines whether a projection crsid is a system (srs.db) or user (~/...
Definition: qgis.h:314
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
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.
const int LAT_PREFIX_LEN
The length of the string "+lat_1=".
Definition: qgis.h:310
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.
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 readXml(const QDomNode &theNode)
Restores state from the given DOM node.
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.
static QgsCoordinateReferenceSystem fromSrsId(long srsId)
Creates a CRS from a specified QGIS SRS ID.
bool createFromProj4(const QString &theProjString)
Sets this CRS by passing it a PROJ.4 style formatted string.
void * OGRSpatialReferenceH
bool isValid() const
Returns whether this CRS is correctly initialized and usable.