QGIS API Documentation  2.10.1-Pisa
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
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  .arg( 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 ).arg( 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( QString db, QString expression, 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  .arg( 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  .arg( 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  foreach ( 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  foreach ( 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( 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( QString authId )
911 {
912  mAuthId = authId;
913 }
914 void QgsCoordinateReferenceSystem::setSrid( long theSrid )
915 {
916  mSRID = theSrid;
917 }
918 void QgsCoordinateReferenceSystem::setDescription( QString theDescription )
919 {
920  mDescription = theDescription;
921 }
922 void QgsCoordinateReferenceSystem::setProj4String( 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( QString theProjectionAcronym )
947 {
948  mProjectionAcronym = theProjectionAcronym;
949 }
950 void QgsCoordinateReferenceSystem::setEllipsoidAcronym( 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  .arg( 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  .arg( 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( 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  .arg( 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  foreach ( 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 ).arg( srsProj4 ).arg( 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  .arg( quotedValue( projRegExp.cap( 1 ) ) )
1814  .arg( quotedValue( ellps ) )
1815  .arg( 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  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() ).arg( 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() ).arg( 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  .arg( quotedValue( auth_name ) )
1897  .arg( 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 ).arg( params ).arg( 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( QString( "'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" ) + QString( ":" ) + 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()
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)
bool empty() const
QDomNode appendChild(const QDomNode &newChild)
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
QString geographicCRSAuthId() const
Returns auth id of related geographic CRS.
bool saveAsUserCRS(QString name)
Copied from QgsCustomProjectionDialog /// Please refactor into SQL handler !!! ///.
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
int size() const
bool createFromId(const long theId, CrsType theType=PostgisCrsId)
The QGis class provides global constants for use throughout the application.
Definition: qgis.h:34
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:350
bool createFromString(const QString &theDefinition)
int size() const
static void logMessage(QString message, QString tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
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.
QString number(int n, int base)
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.
bool createFromUserInput(const QString &theDefinition)
int toInt(bool *ok, int base) const
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:410
QgsCoordinateReferenceSystem & operator=(const QgsCoordinateReferenceSystem &srs)
Assignment operator.
const QString GEO_EPSG_CRS_AUTHID
Geographic coord sys from EPSG authority.
Definition: qgis.cpp:72
QFuture< void > map(Sequence &sequence, MapFunction function)
iterator end()
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:339
bool isNull() const
long toLong(bool *ok, int base) const
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:423
bool writeXML(QDomNode &theNode, QDomDocument &theDoc) const
QByteArray toLatin1() const
QString mid(int position, int n) const
QStringList toStringList() const
void insert(int i, const T &value)
Class for storing a coordinate reference system (CRS)
static const QString srsDbFilePath()
Returns the path to the srs.db file.
const int LAT_PREFIX_LEN
The length of the string "+lat_1=".
Definition: qgis.h:420
UnitType
Map units that qgis supports.
Definition: qgis.h:229
char * data()
QString left(int n) const
typedef Iterator
bool operator!=(const QgsCoordinateReferenceSystem &theSrs) const
QDomElement createElement(const QString &tagName)
void(* CUSTOM_CRS_VALIDATION)(QgsCoordinateReferenceSystem &)
bool operator==(const QgsCoordinateReferenceSystem &theSrs) const
int compare(const QString &other) const
static const QString qgisUserDbFilePath()
Returns the path to the user qgis.db file.
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
QByteArray toAscii() const
bool createFromProj4(const QString &theProjString)
void * OGRSpatialReferenceH
QString toProj4() const
Get the Proj Proj4 string representation of this srs.
QByteArray toUtf8() const