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