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