QGIS API Documentation  2.5.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules 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 : sherman@mrcc.com
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 ) ||
1134  ( mIsValidFlag && theSrs.mIsValidFlag && theSrs.authid() == authid() );
1135 }
1136 
1138 {
1139  return !( *this == theSrs );
1140 }
1141 
1143 {
1144  if ( mWkt.isEmpty() )
1145  {
1146  char *wkt;
1147  if ( OSRExportToWkt( mCRS, &wkt ) == OGRERR_NONE )
1148  {
1149  mWkt = wkt;
1150  OGRFree( wkt );
1151  }
1152  }
1153  return mWkt;
1154 }
1155 
1156 bool QgsCoordinateReferenceSystem::readXML( QDomNode & theNode )
1157 {
1158  QgsDebugMsg( "Reading Spatial Ref Sys from xml ------------------------!" );
1159  QDomNode srsNode = theNode.namedItem( "spatialrefsys" );
1160 
1161  if ( ! srsNode.isNull() )
1162  {
1163  bool initialized = false;
1164 
1165  long srsid = srsNode.namedItem( "srsid" ).toElement().text().toLong();
1166 
1167  QDomNode myNode;
1168 
1169  if ( srsid < USER_CRS_START_ID )
1170  {
1171  myNode = srsNode.namedItem( "authid" );
1172  if ( !myNode.isNull() )
1173  {
1174  operator=( QgsCRSCache::instance()->crsByAuthId( myNode.toElement().text() ) );
1175  if ( isValid() )
1176  {
1177  initialized = true;
1178  }
1179  }
1180 
1181  if ( !initialized )
1182  {
1183  myNode = srsNode.namedItem( "epsg" );
1184  if ( !myNode.isNull() )
1185  {
1186  operator=( QgsCRSCache::instance()->crsByEpsgId( myNode.toElement().text().toLong() ) );
1187  if ( isValid() )
1188  {
1189  initialized = true;
1190  }
1191  }
1192  }
1193  }
1194  else
1195  {
1196  QgsDebugMsg( "Ignoring authid/epsg for user crs." );
1197  }
1198 
1199  if ( initialized )
1200  {
1201  QgsDebugMsg( "Set from auth id" );
1202  }
1203  else
1204  {
1205  myNode = srsNode.namedItem( "proj4" );
1206 
1207  if ( createFromProj4( myNode.toElement().text() ) )
1208  {
1209  // createFromProj4() sets everything, including map units
1210  QgsDebugMsg( "Setting from proj4 string" );
1211  }
1212  else
1213  {
1214  QgsDebugMsg( "Setting from elements one by one" );
1215 
1216  myNode = srsNode.namedItem( "proj4" );
1217  setProj4String( myNode.toElement().text() );
1218 
1219  myNode = srsNode.namedItem( "srsid" );
1220  setInternalId( myNode.toElement().text().toLong() );
1221 
1222  myNode = srsNode.namedItem( "srid" );
1223  setSrid( myNode.toElement().text().toLong() );
1224 
1225  myNode = srsNode.namedItem( "authid" );
1226  setAuthId( myNode.toElement().text() );
1227 
1228  myNode = srsNode.namedItem( "description" );
1229  setDescription( myNode.toElement().text() );
1230 
1231  myNode = srsNode.namedItem( "projectionacronym" );
1232  setProjectionAcronym( myNode.toElement().text() );
1233 
1234  myNode = srsNode.namedItem( "ellipsoidacronym" );
1235  setEllipsoidAcronym( myNode.toElement().text() );
1236 
1237  myNode = srsNode.namedItem( "geographicflag" );
1238  if ( myNode.toElement().text().compare( "true" ) )
1239  {
1240  setGeographicFlag( true );
1241  }
1242  else
1243  {
1244  setGeographicFlag( false );
1245  }
1246 
1247  //make sure the map units have been set
1248  setMapUnits();
1249 
1250  //@TODO this srs needs to be validated!!!
1251  mIsValidFlag = true; //shamelessly hard coded for now
1252  }
1253  //TODO: createFromProj4 used to save to the user database any new CRS
1254  // this behavior was changed in order to separate creation and saving.
1255  // Not sure if it necessary to save it here, should be checked by someone
1256  // familiar with the code (should also give a more descriptive name to the generated CRS)
1257  if ( mSrsId == 0 )
1258  {
1259  QString myName = QString( " * %1 (%2)" )
1260  .arg( QObject::tr( "Generated CRS", "A CRS automatically generated from layer info get this prefix for description" ) )
1261  .arg( toProj4() );
1262  saveAsUserCRS( myName );
1263  }
1264 
1265  }
1266  }
1267  else
1268  {
1269  // Return default CRS if none was found in the XML.
1271  }
1272  return true;
1273 }
1274 
1275 bool QgsCoordinateReferenceSystem::writeXML( QDomNode & theNode, QDomDocument & theDoc ) const
1276 {
1277 
1278  QDomElement myLayerNode = theNode.toElement();
1279  QDomElement mySrsElement = theDoc.createElement( "spatialrefsys" );
1280 
1281  QDomElement myProj4Element = theDoc.createElement( "proj4" );
1282  myProj4Element.appendChild( theDoc.createTextNode( toProj4() ) );
1283  mySrsElement.appendChild( myProj4Element );
1284 
1285  QDomElement mySrsIdElement = theDoc.createElement( "srsid" );
1286  mySrsIdElement.appendChild( theDoc.createTextNode( QString::number( srsid() ) ) );
1287  mySrsElement.appendChild( mySrsIdElement );
1288 
1289  QDomElement mySridElement = theDoc.createElement( "srid" );
1290  mySridElement.appendChild( theDoc.createTextNode( QString::number( postgisSrid() ) ) );
1291  mySrsElement.appendChild( mySridElement );
1292 
1293  QDomElement myEpsgElement = theDoc.createElement( "authid" );
1294  myEpsgElement.appendChild( theDoc.createTextNode( authid() ) );
1295  mySrsElement.appendChild( myEpsgElement );
1296 
1297  QDomElement myDescriptionElement = theDoc.createElement( "description" );
1298  myDescriptionElement.appendChild( theDoc.createTextNode( description() ) );
1299  mySrsElement.appendChild( myDescriptionElement );
1300 
1301  QDomElement myProjectionAcronymElement = theDoc.createElement( "projectionacronym" );
1302  myProjectionAcronymElement.appendChild( theDoc.createTextNode( projectionAcronym() ) );
1303  mySrsElement.appendChild( myProjectionAcronymElement );
1304 
1305  QDomElement myEllipsoidAcronymElement = theDoc.createElement( "ellipsoidacronym" );
1306  myEllipsoidAcronymElement.appendChild( theDoc.createTextNode( ellipsoidAcronym() ) );
1307  mySrsElement.appendChild( myEllipsoidAcronymElement );
1308 
1309  QDomElement myGeographicFlagElement = theDoc.createElement( "geographicflag" );
1310  QString myGeoFlagText = "false";
1311  if ( geographicFlag() )
1312  {
1313  myGeoFlagText = "true";
1314  }
1315 
1316  myGeographicFlagElement.appendChild( theDoc.createTextNode( myGeoFlagText ) );
1317  mySrsElement.appendChild( myGeographicFlagElement );
1318 
1319  myLayerNode.appendChild( mySrsElement );
1320 
1321  return true;
1322 }
1323 
1324 
1325 
1326 //
1327 // Static helper methods below this point only please!
1328 //
1329 
1330 
1331 // Returns the whole proj4 string for the selected srsid
1332 //this is a static method! NOTE I've made it private for now to reduce API clutter TS
1334 {
1335 
1336  QString myDatabaseFileName;
1337  QString myProjString;
1338  QString mySql = QString( "select parameters from tbl_srs where srs_id = %1 order by deprecated" ).arg( theSrsId );
1339 
1340  QgsDebugMsg( "mySrsId = " + QString::number( theSrsId ) );
1341  QgsDebugMsg( "USER_CRS_START_ID = " + QString::number( USER_CRS_START_ID ) );
1342  QgsDebugMsg( "Selection sql : " + mySql );
1343 
1344  //
1345  // Determine if this is a user projection or a system on
1346  // user projection defs all have srs_id >= 100000
1347  //
1348  if ( theSrsId >= USER_CRS_START_ID )
1349  {
1350  myDatabaseFileName = QgsApplication::qgisUserDbFilePath();
1351  QFileInfo myFileInfo;
1352  myFileInfo.setFile( myDatabaseFileName );
1353  if ( !myFileInfo.exists( ) ) //its unlikely that this condition will ever be reached
1354  {
1355  QgsDebugMsg( "users qgis.db not found" );
1356  return NULL;
1357  }
1358  }
1359  else //must be a system projection then
1360  {
1361  myDatabaseFileName = QgsApplication::srsDbFilePath();
1362  }
1363  QgsDebugMsg( "db = " + myDatabaseFileName );
1364 
1365  sqlite3 *db;
1366  int rc;
1367  rc = openDb( myDatabaseFileName, &db );
1368  if ( rc )
1369  {
1370  return QString();
1371  }
1372  // prepare the sql statement
1373  const char *pzTail;
1374  sqlite3_stmt *ppStmt;
1375 
1376  rc = sqlite3_prepare( db, mySql.toUtf8(), mySql.toUtf8().length(), &ppStmt, &pzTail );
1377  // XXX Need to free memory from the error msg if one is set
1378 
1379  if ( rc == SQLITE_OK )
1380  {
1381  if ( sqlite3_step( ppStmt ) == SQLITE_ROW )
1382  {
1383  myProjString = QString::fromUtf8(( char* )sqlite3_column_text( ppStmt, 0 ) );
1384  }
1385  }
1386  // close the statement
1387  sqlite3_finalize( ppStmt );
1388  // close the database
1389  sqlite3_close( db );
1390 
1391  //Q_ASSERT(myProjString.length() > 0);
1392  return myProjString;
1393 }
1394 
1395 int QgsCoordinateReferenceSystem::openDb( QString path, sqlite3 **db, bool readonly )
1396 {
1397  QgsDebugMsgLevel( "path = " + path, 3 );
1398  int myResult = readonly
1399  ? sqlite3_open_v2( path.toUtf8().data(), db, SQLITE_OPEN_READONLY, NULL )
1400  : sqlite3_open( path.toUtf8().data(), db );
1401 
1402  if ( myResult != SQLITE_OK )
1403  {
1404  QgsDebugMsg( "Can't open database: " + QString( sqlite3_errmsg( *db ) ) );
1405  // XXX This will likely never happen since on open, sqlite creates the
1406  // database if it does not exist.
1407  // ... unfortunately it happens on Windows
1408  QgsMessageLog::logMessage( QObject::tr( "Could not open CRS database %1\nError(%2): %3" )
1409  .arg( path )
1410  .arg( myResult )
1411  .arg( sqlite3_errmsg( *db ) ), QObject::tr( "CRS" ) );
1412  }
1413  return myResult;
1414 }
1415 
1417 {
1419 }
1420 
1422 {
1423  return mCustomSrsValidation;
1424 }
1425 
1427 {
1428  QgsDebugMsg( "***SpatialRefSystem***" );
1429  QgsDebugMsg( "* Valid : " + ( mIsValidFlag ? QString( "true" ) : QString( "false" ) ) );
1430  QgsDebugMsg( "* SrsId : " + QString::number( mSrsId ) );
1431  QgsDebugMsg( "* Proj4 : " + toProj4() );
1432  QgsDebugMsg( "* WKT : " + toWkt() );
1433  QgsDebugMsg( "* Desc. : " + mDescription );
1434  if ( mapUnits() == QGis::Meters )
1435  {
1436  QgsDebugMsg( "* Units : meters" );
1437  }
1438  else if ( mapUnits() == QGis::Feet )
1439  {
1440  QgsDebugMsg( "* Units : feet" );
1441  }
1442  else if ( mapUnits() == QGis::Degrees )
1443  {
1444  QgsDebugMsg( "* Units : degrees" );
1445  }
1446 }
1447 
1449 {
1450  mValidationHint = html;
1451 }
1452 
1454 {
1455  return mValidationHint;
1456 }
1457 
1460 
1462 {
1463  if ( ! mIsValidFlag )
1464  {
1465  QgsDebugMsg( "Can't save an invalid CRS!" );
1466  return false;
1467  }
1468 
1469  QString mySql;
1470 
1471  QString proj4String = mProj4;
1472  if ( proj4String.isEmpty() )
1473  {
1474  proj4String = toProj4();
1475  }
1476 
1477  //if this is the first record we need to ensure that its srs_id is 10000. For
1478  //any rec after that sqlite3 will take care of the autonumering
1479  //this was done to support sqlite 3.0 as it does not yet support
1480  //the autoinc related system tables.
1481  if ( getRecordCount() == 0 )
1482  {
1483  mySql = "insert into tbl_srs (srs_id,description,projection_acronym,ellipsoid_acronym,parameters,is_geo) values ("
1484  + QString::number( USER_CRS_START_ID )
1485  + "," + quotedValue( name )
1486  + "," + quotedValue( projectionAcronym() )
1487  + "," + quotedValue( ellipsoidAcronym() )
1488  + "," + quotedValue( toProj4() )
1489  + ",0)"; // <-- is_geo shamelessly hard coded for now
1490  }
1491  else
1492  {
1493  mySql = "insert into tbl_srs (description,projection_acronym,ellipsoid_acronym,parameters,is_geo) values ("
1494  + quotedValue( name )
1495  + "," + quotedValue( projectionAcronym() )
1496  + "," + quotedValue( ellipsoidAcronym() )
1497  + "," + quotedValue( toProj4() )
1498  + ",0)"; // <-- is_geo shamelessly hard coded for now
1499  }
1500  sqlite3 *myDatabase;
1501  const char *myTail;
1502  sqlite3_stmt *myPreparedStatement;
1503  int myResult;
1504  //check the db is available
1505  myResult = sqlite3_open( QgsApplication::qgisUserDbFilePath().toUtf8().data(), &myDatabase );
1506  if ( myResult != SQLITE_OK )
1507  {
1508  QgsDebugMsg( QString( "Can't open or create database %1: %2" )
1510  .arg( sqlite3_errmsg( myDatabase ) ) );
1511  return false;
1512  }
1513  QgsDebugMsg( QString( "Update or insert sql \n%1" ).arg( mySql ) );
1514  myResult = sqlite3_prepare( myDatabase, mySql.toUtf8(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
1515  sqlite3_step( myPreparedStatement );
1516 
1517  QgsMessageLog::logMessage( QObject::tr( "Saved user CRS [%1]" ).arg( toProj4() ), QObject::tr( "CRS" ) );
1518 
1519  qint64 return_id;
1520  if ( myResult == SQLITE_OK )
1521  {
1522  return_id = sqlite3_last_insert_rowid( myDatabase );
1523  setInternalId( return_id );
1524 
1525  //We add the just created user CRS to the list of recently used CRS
1526  QSettings settings;
1527  //QStringList recentProjections = settings.value( "/UI/recentProjections" ).toStringList();
1528  QStringList projectionsProj4 = settings.value( "/UI/recentProjectionsProj4" ).toStringList();
1529  QStringList projectionsAuthId = settings.value( "/UI/recentProjectionsAuthId" ).toStringList();
1530  //recentProjections.append();
1531  //settings.setValue( "/UI/recentProjections", recentProjections );
1532  projectionsProj4.append( toProj4() );
1533  projectionsAuthId.append( authid() );
1534  settings.setValue( "/UI/recentProjectionsProj4", projectionsProj4 );
1535  settings.setValue( "/UI/recentProjectionsAuthId", projectionsAuthId );
1536 
1537  }
1538  else
1539  return_id = -1;
1540  return return_id;
1541 }
1542 
1544 {
1545  sqlite3 *myDatabase;
1546  const char *myTail;
1547  sqlite3_stmt *myPreparedStatement;
1548  int myResult;
1549  long myRecordCount = 0;
1550  //check the db is available
1551  myResult = sqlite3_open_v2( QgsApplication::qgisUserDbFilePath().toUtf8().data(), &myDatabase, SQLITE_OPEN_READONLY, NULL );
1552  if ( myResult != SQLITE_OK )
1553  {
1554  QgsDebugMsg( QString( "Can't open database: %1" ).arg( sqlite3_errmsg( myDatabase ) ) );
1555  return 0;
1556  }
1557  // Set up the query to retrieve the projection information needed to populate the ELLIPSOID list
1558  QString mySql = "select count(*) from tbl_srs";
1559  myResult = sqlite3_prepare( myDatabase, mySql.toUtf8(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
1560  // XXX Need to free memory from the error msg if one is set
1561  if ( myResult == SQLITE_OK )
1562  {
1563  if ( sqlite3_step( myPreparedStatement ) == SQLITE_ROW )
1564  {
1565  QString myRecordCountString = QString::fromUtf8(( char * )sqlite3_column_text( myPreparedStatement, 0 ) );
1566  myRecordCount = myRecordCountString.toLong();
1567  }
1568  }
1569  // close the sqlite3 statement
1570  sqlite3_finalize( myPreparedStatement );
1571  sqlite3_close( myDatabase );
1572  return myRecordCount;
1573 }
1574 
1576 {
1577  value.replace( "'", "''" );
1578  return value.prepend( "'" ).append( "'" );
1579 }
1580 
1581 // adapted from gdal/ogr/ogr_srs_dict.cpp
1582 bool QgsCoordinateReferenceSystem::loadWkts( QHash<int, QString> &wkts, const char *filename )
1583 {
1584  qDebug( "Loading %s", filename );
1585  const char *pszFilename = CPLFindFile( "gdal", filename );
1586  if ( !pszFilename )
1587  return false;
1588 
1589  QFile csv( pszFilename );
1590  if ( !csv.open( QIODevice::ReadOnly ) )
1591  return false;
1592 
1593  QTextStream lines( &csv );
1594 
1595  for ( ;; )
1596  {
1597  QString line = lines.readLine();
1598  if ( line.isNull() )
1599  break;
1600 
1601  if ( line.startsWith( '#' ) )
1602  {
1603  continue;
1604  }
1605  else if ( line.startsWith( "include " ) )
1606  {
1607  if ( !loadWkts( wkts, line.mid( 8 ).toUtf8() ) )
1608  break;
1609  }
1610  else
1611  {
1612  int pos = line.indexOf( "," );
1613  if ( pos < 0 )
1614  return false;
1615 
1616  bool ok;
1617  int epsg = line.left( pos ).toInt( &ok );
1618  if ( !ok )
1619  return false;
1620 
1621  wkts.insert( epsg, line.mid( pos + 1 ) );
1622  }
1623  }
1624 
1625  csv.close();
1626 
1627  return true;
1628 }
1629 
1630 bool QgsCoordinateReferenceSystem::loadIDs( QHash<int, QString> &wkts )
1631 {
1632  OGRSpatialReferenceH crs = OSRNewSpatialReference( NULL );
1633 
1634  foreach ( QString csv, QStringList() << "gcs.csv" << "pcs.csv" << "vertcs.csv" << "compdcs.csv" << "geoccs.csv" )
1635  {
1636  QString filename = CPLFindFile( "gdal", csv.toUtf8() );
1637 
1638  QFile f( filename );
1639  if ( !f.open( QIODevice::ReadOnly ) )
1640  continue;
1641 
1642  QTextStream lines( &f );
1643  int l = 0, n = 0;
1644 
1645  lines.readLine();
1646  for ( ;; )
1647  {
1648  l++;
1649  QString line = lines.readLine();
1650  if ( line.isNull() )
1651  break;
1652 
1653  int pos = line.indexOf( "," );
1654  if ( pos < 0 )
1655  continue;
1656 
1657  bool ok;
1658  int epsg = line.left( pos ).toInt( &ok );
1659  if ( !ok )
1660  continue;
1661 
1662  // some CRS are known to fail (see http://trac.osgeo.org/gdal/ticket/2900)
1663  if ( epsg == 2218 || epsg == 2221 || epsg == 2296 || epsg == 2297 || epsg == 2298 || epsg == 2299 || epsg == 2300 || epsg == 2301 || epsg == 2302 ||
1664  epsg == 2303 || epsg == 2304 || epsg == 2305 || epsg == 2306 || epsg == 2307 || epsg == 2963 || epsg == 2985 || epsg == 2986 || epsg == 3052 ||
1665  epsg == 3053 || epsg == 3139 || epsg == 3144 || epsg == 3145 || epsg == 3173 || epsg == 3295 || epsg == 3993 || epsg == 4087 || epsg == 4088 ||
1666  epsg == 5017 || epsg == 5221 || epsg == 5224 || epsg == 5225 || epsg == 5514 || epsg == 5515 || epsg == 5516 || epsg == 5819 || epsg == 5820 ||
1667  epsg == 5821 || epsg == 32600 || epsg == 32663 || epsg == 32700 )
1668  continue;
1669 
1670  if ( OSRImportFromEPSG( crs, epsg ) != OGRERR_NONE )
1671  {
1672  qDebug( "EPSG %d: not imported", epsg );
1673  continue;
1674  }
1675 
1676  char *wkt = 0;
1677  if ( OSRExportToWkt( crs, &wkt ) != OGRERR_NONE )
1678  {
1679  qWarning( "EPSG %d: not exported to WKT", epsg );
1680  continue;
1681  }
1682 
1683  wkts.insert( epsg, wkt );
1684  n++;
1685 
1686  OGRFree( wkt );
1687  }
1688 
1689  f.close();
1690 
1691  qDebug( "Loaded %d/%d from %s", n, l, filename.toUtf8().constData() );
1692  }
1693 
1694  OSRDestroySpatialReference( crs );
1695 
1696  return true;
1697 }
1698 
1700 {
1701  QString dbFilePath = QgsApplication::srsDbFilePath();
1702  syncDatumTransform( dbFilePath );
1703 
1704  int inserted = 0, updated = 0, deleted = 0, errors = 0;
1705 
1706  qDebug( "Load srs db from: %s", QgsApplication::srsDbFilePath().toLocal8Bit().constData() );
1707 
1708  sqlite3 *database;
1709  if ( sqlite3_open( dbFilePath.toUtf8().constData(), &database ) != SQLITE_OK )
1710  {
1711  qCritical( "Could not open database: %s [%s]\n", QgsApplication::srsDbFilePath().toLocal8Bit().constData(), sqlite3_errmsg( database ) );
1712  return -1;
1713  }
1714 
1715  if ( sqlite3_exec( database, "BEGIN TRANSACTION", 0, 0, 0 ) != SQLITE_OK )
1716  {
1717  qCritical( "Could not begin transaction: %s [%s]\n", QgsApplication::srsDbFilePath().toLocal8Bit().constData(), sqlite3_errmsg( database ) );
1718  return -1;
1719 
1720  }
1721 
1722  // fix up database, if not done already //
1723  if ( sqlite3_exec( database, "alter table tbl_srs add noupdate boolean", 0, 0, 0 ) == SQLITE_OK )
1724  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 );
1725 
1726  sqlite3_exec( database, "UPDATE tbl_srs SET srid=141001 WHERE srid=41001 AND auth_name='OSGEO' AND auth_id='41001'", 0, 0, 0 );
1727 
1728  OGRSpatialReferenceH crs = OSRNewSpatialReference( NULL );
1729  const char *tail;
1730  sqlite3_stmt *select;
1731  char *errMsg = NULL;
1732 
1733  QString proj4;
1734  QString sql;
1735  QHash<int, QString> wkts;
1736  loadIDs( wkts );
1737  loadWkts( wkts, "epsg.wkt" );
1738 
1739  qDebug( "%d WKTs loaded", wkts.count() );
1740 
1741  for ( QHash<int, QString>::const_iterator it = wkts.constBegin(); it != wkts.constEnd(); ++it )
1742  {
1743  QByteArray ba( it.value().toUtf8() );
1744  char *psz = ba.data();
1745  OGRErr ogrErr = OSRImportFromWkt( crs, &psz );
1746  if ( ogrErr != OGRERR_NONE )
1747  continue;
1748 
1749  if ( OSRExportToProj4( crs, &psz ) != OGRERR_NONE )
1750  continue;
1751 
1752  proj4 = psz;
1753  proj4 = proj4.trimmed();
1754 
1755  CPLFree( psz );
1756 
1757  if ( proj4.isEmpty() )
1758  continue;
1759 
1760  sql = QString( "SELECT parameters,noupdate FROM tbl_srs WHERE auth_name='EPSG' AND auth_id='%1'" ).arg( it.key() );
1761  if ( sqlite3_prepare( database, sql.toAscii(), sql.size(), &select, &tail ) != SQLITE_OK )
1762  {
1763  qCritical( "Could not prepare: %s [%s]\n", sql.toAscii().constData(), sqlite3_errmsg( database ) );
1764  continue;
1765  }
1766 
1767  QString srsProj4;
1768  if ( sqlite3_step( select ) == SQLITE_ROW )
1769  {
1770  srsProj4 = ( const char * ) sqlite3_column_text( select, 0 );
1771 
1772  if ( QString::fromUtf8(( char * )sqlite3_column_text( select, 1 ) ).toInt() != 0 )
1773  continue;
1774  }
1775 
1776  sqlite3_finalize( select );
1777 
1778  if ( !srsProj4.isEmpty() )
1779  {
1780  if ( proj4 != srsProj4 )
1781  {
1782  errMsg = NULL;
1783  sql = QString( "UPDATE tbl_srs SET parameters=%1 WHERE auth_name='EPSG' AND auth_id=%2" ).arg( quotedValue( proj4 ) ).arg( it.key() );
1784 
1785  if ( sqlite3_exec( database, sql.toUtf8(), 0, 0, &errMsg ) != SQLITE_OK )
1786  {
1787  qCritical( "Could not execute: %s [%s/%s]\n",
1788  sql.toLocal8Bit().constData(),
1789  sqlite3_errmsg( database ),
1790  errMsg ? errMsg : "(unknown error)" );
1791  errors++;
1792  }
1793  else
1794  {
1795  updated++;
1796  QgsDebugMsgLevel( QString( "SQL: %1\n OLD:%2\n NEW:%3" ).arg( sql ).arg( srsProj4 ).arg( proj4 ), 3 );
1797  }
1798  }
1799  }
1800  else
1801  {
1802  QRegExp projRegExp( "\\+proj=(\\S+)" );
1803  if ( projRegExp.indexIn( proj4 ) < 0 )
1804  {
1805  QgsDebugMsg( QString( "EPSG %1: no +proj argument found [%2]" ).arg( it.key() ).arg( proj4 ) );
1806  continue;
1807  }
1808 
1809  QRegExp ellipseRegExp( "\\+ellps=(\\S+)" );
1810  QString ellps;
1811  if ( ellipseRegExp.indexIn( proj4 ) >= 0 )
1812  {
1813  ellps = ellipseRegExp.cap( 1 );
1814  }
1815 
1816  QString name( OSRIsGeographic( crs ) ? OSRGetAttrValue( crs, "GEOCS", 0 ) : OSRGetAttrValue( crs, "PROJCS", 0 ) );
1817  if ( name.isEmpty() )
1818  name = QObject::tr( "Imported from GDAL" );
1819 
1820  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)" )
1821  .arg( quotedValue( name ) )
1822  .arg( quotedValue( projRegExp.cap( 1 ) ) )
1823  .arg( quotedValue( ellps ) )
1824  .arg( quotedValue( proj4 ) )
1825  .arg( it.key() )
1826  .arg( OSRIsGeographic( crs ) );
1827 
1828  errMsg = NULL;
1829  if ( sqlite3_exec( database, sql.toUtf8(), 0, 0, &errMsg ) == SQLITE_OK )
1830  {
1831  inserted++;
1832  }
1833  else
1834  {
1835  qCritical( "Could not execute: %s [%s/%s]\n",
1836  sql.toLocal8Bit().constData(),
1837  sqlite3_errmsg( database ),
1838  errMsg ? errMsg : "(unknown error)" );
1839  errors++;
1840 
1841  if ( errMsg )
1842  sqlite3_free( errMsg );
1843  }
1844  }
1845  }
1846 
1847  sql = "DELETE FROM tbl_srs WHERE auth_name='EPSG' AND NOT auth_id IN (";
1848  QString delim;
1849  foreach ( int i, wkts.keys() )
1850  {
1851  sql += delim + QString::number( i );
1852  delim = ",";
1853  }
1854  sql += ") AND NOT noupdate";
1855 
1856  if ( sqlite3_exec( database, sql.toUtf8(), 0, 0, 0 ) == SQLITE_OK )
1857  {
1858  deleted = sqlite3_changes( database );
1859  }
1860  else
1861  {
1862  errors++;
1863  qCritical( "Could not execute: %s [%s]\n",
1864  sql.toLocal8Bit().constData(),
1865  sqlite3_errmsg( database ) );
1866  }
1867 
1868 #if !defined(PJ_VERSION) || PJ_VERSION!=470
1869  sql = QString( "select auth_name,auth_id,parameters from tbl_srs WHERE auth_name<>'EPSG' AND NOT deprecated AND NOT noupdate" );
1870  if ( sqlite3_prepare( database, sql.toAscii(), sql.size(), &select, &tail ) == SQLITE_OK )
1871  {
1872  while ( sqlite3_step( select ) == SQLITE_ROW )
1873  {
1874  const char *auth_name = ( const char * ) sqlite3_column_text( select, 0 );
1875  const char *auth_id = ( const char * ) sqlite3_column_text( select, 1 );
1876  const char *params = ( const char * ) sqlite3_column_text( select, 2 );
1877 
1878  QString input = QString( "+init=%1:%2" ).arg( QString( auth_name ).toLower() ).arg( auth_id );
1879  projPJ pj = pj_init_plus( input.toAscii() );
1880  if ( !pj )
1881  {
1882  input = QString( "+init=%1:%2" ).arg( QString( auth_name ).toUpper() ).arg( auth_id );
1883  pj = pj_init_plus( input.toAscii() );
1884  }
1885 
1886  if ( pj )
1887  {
1888  char *def = pj_get_def( pj, 0 );
1889  if ( def )
1890  {
1891  proj4 = def;
1892  pj_dalloc( def );
1893 
1894  input.prepend( ' ' ).append( ' ' );
1895  if ( proj4.startsWith( input ) )
1896  {
1897  proj4 = proj4.mid( input.size() );
1898  proj4 = proj4.trimmed();
1899  }
1900 
1901  if ( proj4 != params )
1902  {
1903  sql = QString( "UPDATE tbl_srs SET parameters=%1 WHERE auth_name=%2 AND auth_id=%3" )
1904  .arg( quotedValue( proj4 ) )
1905  .arg( quotedValue( auth_name ) )
1906  .arg( quotedValue( auth_id ) );
1907 
1908  if ( sqlite3_exec( database, sql.toUtf8(), 0, 0, &errMsg ) == SQLITE_OK )
1909  {
1910  updated++;
1911  QgsDebugMsgLevel( QString( "SQL: %1\n OLD:%2\n NEW:%3" ).arg( sql ).arg( params ).arg( proj4 ), 3 );
1912  }
1913  else
1914  {
1915  qCritical( "Could not execute: %s [%s/%s]\n",
1916  sql.toLocal8Bit().constData(),
1917  sqlite3_errmsg( database ),
1918  errMsg ? errMsg : "(unknown error)" );
1919  errors++;
1920  }
1921  }
1922  }
1923  else
1924  {
1925  QgsDebugMsg( QString( "could not retrieve proj string for %1 from PROJ" ).arg( input ) );
1926  }
1927  }
1928  else
1929  {
1930  QgsDebugMsgLevel( QString( "could not retrieve crs for %1 from PROJ" ).arg( input ), 3 );
1931  }
1932 
1933  pj_free( pj );
1934  }
1935  }
1936  else
1937  {
1938  errors++;
1939  qCritical( "Could not execute: %s [%s]\n",
1940  sql.toLocal8Bit().constData(),
1941  sqlite3_errmsg( database ) );
1942  }
1943 #endif
1944 
1945  OSRDestroySpatialReference( crs );
1946 
1947  if ( sqlite3_exec( database, "COMMIT", 0, 0, 0 ) != SQLITE_OK )
1948  {
1949  qCritical( "Could not commit transaction: %s [%s]\n", QgsApplication::srsDbFilePath().toLocal8Bit().constData(), sqlite3_errmsg( database ) );
1950  return -1;
1951  }
1952 
1953  sqlite3_close( database );
1954 
1955  qWarning( "CRS update (inserted:%d updated:%d deleted:%d errors:%d)", inserted, updated, deleted, errors );
1956 
1957  if ( errors > 0 )
1958  return -errors;
1959  else
1960  return updated + inserted;
1961 }
1962 
1964 {
1965  const char *filename = CSVFilename( "datum_shift.csv" );
1966  FILE *fp = VSIFOpen( filename, "rb" );
1967  if ( !fp )
1968  {
1969  return false;
1970  }
1971 
1972  char **fieldnames = CSVReadParseLine( fp );
1973 
1974  // "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"
1975 
1976  struct
1977  {
1978  const char *src;
1979  const char *dst;
1980  int idx;
1981  } map[] =
1982  {
1983  // { "SEQ_KEY", "", -1 },
1984  { "SOURCE_CRS_CODE", "source_crs_code", -1 },
1985  { "TARGET_CRS_CODE", "target_crs_code", -1 },
1986  { "REMARKS", "remarks", -1 },
1987  { "COORD_OP_SCOPE", "scope", -1 },
1988  { "AREA_OF_USE_CODE", "area_of_use_code", -1 },
1989  // { "AREA_SOUTH_BOUND_LAT", "", -1 },
1990  // { "AREA_NORTH_BOUND_LAT", "", -1 },
1991  // { "AREA_WEST_BOUND_LON", "", -1 },
1992  // { "AREA_EAST_BOUND_LON", "", -1 },
1993  // { "SHOW_OPERATION", "", -1 },
1994  { "DEPRECATED", "deprecated", -1 },
1995  { "COORD_OP_METHOD_CODE", "coord_op_method_code", -1 },
1996  { "DX", "p1", -1 },
1997  { "DY", "p2", -1 },
1998  { "DZ", "p3", -1 },
1999  { "RX", "p4", -1 },
2000  { "RY", "p5", -1 },
2001  { "RZ", "p6", -1 },
2002  { "DS", "p7", -1 },
2003  { "PREFERRED", "preferred", -1 },
2004  { "COORD_OP_CODE", "coord_op_code", -1 },
2005  };
2006 
2007  QString update = "UPDATE tbl_datum_transform SET ";
2008  QString insert, values;
2009 
2010  int n = CSLCount( fieldnames );
2011 
2012  int idxid = -1, idxrx = -1, idxry = -1, idxrz = -1, idxmcode = -1;
2013  for ( unsigned int i = 0; i < sizeof( map ) / sizeof( *map ); i++ )
2014  {
2015  bool last = i == sizeof( map ) / sizeof( *map ) - 1;
2016 
2017  map[i].idx = CSLFindString( fieldnames, map[i].src );
2018  if ( map[i].idx < 0 )
2019  {
2020  qWarning( "field %s not found", map[i].src );
2021  CSLDestroy( fieldnames );
2022  fclose( fp );
2023  return false;
2024  }
2025 
2026  if ( strcmp( map[i].src, "COORD_OP_CODE" ) == 0 )
2027  idxid = i;
2028  if ( strcmp( map[i].src, "RX" ) == 0 )
2029  idxrx = i;
2030  if ( strcmp( map[i].src, "RY" ) == 0 )
2031  idxry = i;
2032  if ( strcmp( map[i].src, "RZ" ) == 0 )
2033  idxrz = i;
2034  if ( strcmp( map[i].src, "COORD_OP_METHOD_CODE" ) == 0 )
2035  idxmcode = i;
2036 
2037  if ( i > 0 )
2038  {
2039  insert += ",";
2040  values += ",";
2041 
2042  if ( last )
2043  {
2044  update += " WHERE ";
2045  }
2046  else
2047  {
2048  update += ",";
2049  }
2050  }
2051 
2052  update += QString( "%1=%%2" ).arg( map[i].dst ).arg( i + 1 );
2053 
2054  insert += map[i].dst;
2055  values += QString( "%%1" ).arg( i + 1 );
2056  }
2057 
2058  insert = "INSERT INTO tbl_datum_transform(" + insert + ") VALUES (" + values + ")";
2059 
2060  QgsDebugMsgLevel( QString( "insert:%1" ).arg( insert ), 4 );
2061  QgsDebugMsgLevel( QString( "update:%1" ).arg( update ), 4 );
2062 
2063  CSLDestroy( fieldnames );
2064 
2065  Q_ASSERT( idxid >= 0 );
2066  Q_ASSERT( idxrx >= 0 );
2067  Q_ASSERT( idxry >= 0 );
2068  Q_ASSERT( idxrz >= 0 );
2069 
2070  sqlite3 *db;
2071  int openResult = sqlite3_open( dbPath.toUtf8().constData(), &db );
2072  if ( openResult != SQLITE_OK )
2073  {
2074  fclose( fp );
2075  return false;
2076  }
2077 
2078  if ( sqlite3_exec( db, "BEGIN TRANSACTION", 0, 0, 0 ) != SQLITE_OK )
2079  {
2080  qCritical( "Could not begin transaction: %s [%s]\n", QgsApplication::srsDbFilePath().toLocal8Bit().constData(), sqlite3_errmsg( db ) );
2081  sqlite3_close( db );
2082  fclose( fp );
2083  return false;
2084  }
2085 
2086  QStringList v;
2087  v.reserve( sizeof( map ) / sizeof( *map ) );
2088 
2089  while ( !feof( fp ) )
2090  {
2091  char **values = CSVReadParseLine( fp );
2092 
2093  v.clear();
2094 
2095  if ( CSLCount( values ) < n )
2096  {
2097  qWarning( "Only %d columns", CSLCount( values ) );
2098  continue;
2099  }
2100 
2101  for ( unsigned int i = 0; i < sizeof( map ) / sizeof( *map ); i++ )
2102  {
2103  int idx = map[i].idx;
2104  Q_ASSERT( idx != -1 );
2105  Q_ASSERT( idx < n );
2106  v.insert( i, *values[ idx ] ? quotedValue( values[idx] ) : "NULL" );
2107  }
2108 
2109  //switch sign of rotation parameters. See http://trac.osgeo.org/proj/wiki/GenParms#towgs84-DatumtransformationtoWGS84
2110  if ( v.at( idxmcode ).compare( QString( "'9607'" ) ) == 0 )
2111  {
2112  v[ idxmcode ] = "'9606'";
2113  v[ idxrx ] = "'" + qgsDoubleToString( -( v[ idxrx ].remove( "'" ).toDouble() ) ) + "'";
2114  v[ idxry ] = "'" + qgsDoubleToString( -( v[ idxry ].remove( "'" ).toDouble() ) ) + "'";
2115  v[ idxrz ] = "'" + qgsDoubleToString( -( v[ idxrz ].remove( "'" ).toDouble() ) ) + "'";
2116  }
2117 
2118  //entry already in db?
2119  sqlite3_stmt *stmt;
2120  QString cOpCode;
2121  QString sql = QString( "SELECT coord_op_code FROM tbl_datum_transform WHERE coord_op_code=%1" ).arg( v[ idxid ] );
2122  int prepareRes = sqlite3_prepare( db, sql.toAscii(), sql.size(), &stmt, NULL );
2123  if ( prepareRes != SQLITE_OK )
2124  continue;
2125 
2126  if ( sqlite3_step( stmt ) == SQLITE_ROW )
2127  {
2128  cOpCode = ( const char * ) sqlite3_column_text( stmt, 0 );
2129  }
2130  sqlite3_finalize( stmt );
2131 
2132  sql = cOpCode.isEmpty() ? insert : update;
2133  for ( int i = 0; i < v.size(); i++ )
2134  {
2135  sql = sql.arg( v[i] );
2136  }
2137 
2138  if ( sqlite3_exec( db, sql.toUtf8(), 0, 0, 0 ) != SQLITE_OK )
2139  {
2140  qCritical( "SQL: %s", sql.toUtf8().constData() );
2141  qCritical( "Error: %s", sqlite3_errmsg( db ) );
2142  }
2143  }
2144 
2145  if ( sqlite3_exec( db, "COMMIT", 0, 0, 0 ) != SQLITE_OK )
2146  {
2147  qCritical( "Could not commit transaction: %s [%s]\n", QgsApplication::srsDbFilePath().toLocal8Bit().constData(), sqlite3_errmsg( db ) );
2148  return false;
2149  }
2150 
2151  sqlite3_close( db );
2152  return true;
2153 }
2154 
2156 {
2157  if ( geographicFlag() )
2158  {
2159  return mAuthId;
2160  }
2161  else if ( mCRS )
2162  {
2163  return OSRGetAuthorityName( mCRS, "GEOGCS" ) + QString( ":" ) + OSRGetAuthorityCode( mCRS, "GEOGCS" );
2164  }
2165  else
2166  {
2167  return "";
2168  }
2169 }
QGis::UnitType mMapUnits
The map units.
QgsCoordinateReferenceSystem()
Default constructor.
const QgsCoordinateReferenceSystem & crsByAuthId(const QString &authid)
Returns the CRS for authid, e.g.
bool createFromWkt(const QString &theWkt)
bool mIsValidFlag
Whether this srs is properly defined and valid.
static bool loadIDs(QHash< int, QString > &wkts)
#define QgsDebugMsg(str)
Definition: qgslogger.h:36
QString geographicCRSAuthId() const
Returns auth id of related geographic CRS.
bool saveAsUserCRS(QString name)
Copied from QgsCustomProjectionDialog /// Please refactor into SQL handler !!! ///.
UnitType
Map units that qgis supports.
Definition: qgis.h:229
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:162
void setMapUnits()
Work out the projection units and set the appropriate local variable.
static int openDb(QString path, sqlite3 **db, bool readonly=true)
bool createFromId(const long theId, CrsType theType=PostgisCrsId)
QString mAuthId
If available the authority identifier for this srs.
long getRecordCount()
Helper for getting number of user CRS already in db.
bool mGeoFlag
Whether this is a geographic or projected coordinate system.
The QGis class provides global constants for use throughout the application.
Definition: qgis.h:34
static CUSTOM_CRS_VALIDATION customSrsValidation()
Gets custom function.
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:324
bool createFromString(const QString &theDefinition)
static void logMessage(QString message, QString tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
static void setCustomSrsValidation(CUSTOM_CRS_VALIDATION f)
Sets custom function to force valid CRS QGIS uses implementation in QgisGui::customSrsValidation.
void setEllipsoidAcronym(QString theEllipsoidAcronym)
bool createFromOgcWmsCrs(QString theCrs)
Set up this CRS from the given OGC CRS.
long mSRID
If available, the Postgis spatial_ref_sys identifier for this srs (defaults to 0) ...
QString mEllipsoidAcronym
The official proj4 acronym for the ellipoid.
static CUSTOM_CRS_VALIDATION mCustomSrsValidation
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:37
bool createFromUserInput(const QString &theDefinition)
const long GEOCRS_ID
Magic number for a geographic coord sys in QGIS srs.db tbl_srs.srs_id.
Definition: qgis.h:384
static QString quotedValue(QString value)
Helper for sql-safe value quoting.
QgsCoordinateReferenceSystem & operator=(const QgsCoordinateReferenceSystem &srs)
Assignment operator.
static bool loadWkts(QHash< int, QString > &wkts, const char *filename)
void setProj4String(QString theProj4String)
QString mDescription
A textual description of the srs.
int mAxisInverted
Whether this is a coordinate system has inverted axis.
struct sqlite3 sqlite3
long mSrsId
The internal sqlite3 srs.db primary key for this srs.
QString qgsDoubleToString(const double &a, const int &precision=17)
Definition: qgis.h:316
void * projPJ
const int USER_CRS_START_ID
Magick number that determines whether a projection crsid is a system (srs.db) or user (~/...
Definition: qgis.h:397
bool writeXML(QDomNode &theNode, QDomDocument &theDoc) const
void setDescription(QString theDescription)
Class for storing a coordinate reference system (CRS)
static const QString srsDbFilePath()
Returns the path to the srs.db file.
static QString proj4FromSrsId(const int theSrsId)
A static helper function to find out the proj4 string for a srsid.
static bool syncDatumTransform(const QString &dbPath)
const int LAT_PREFIX_LEN
The length of the string "+lat_1=".
Definition: qgis.h:394
void setProjectionAcronym(QString theProjectionAcronym)
bool loadFromDb(QString db, QString expression, QString value)
bool operator!=(const QgsCoordinateReferenceSystem &theSrs) const
void(* CUSTOM_CRS_VALIDATION)(QgsCoordinateReferenceSystem &)
bool operator==(const QgsCoordinateReferenceSystem &theSrs) const
static const QString qgisUserDbFilePath()
Returns the path to the user qgis.db file.
static QgsCRSCache * instance()
Definition: qgscrscache.cpp:85
const CORE_EXPORT QString GEO_EPSG_CRS_AUTHID
Geographic coord sys from EPSG authority.
Definition: qgis.cpp:71
bool createFromProj4(const QString &theProjString)
void * OGRSpatialReferenceH
QString toProj4() const
Get the Proj Proj4 string representation of this srs.
QString mProjectionAcronym
The official proj4 acronym for the projection family.
#define tr(sourceText)