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