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