Quantum GIS API Documentation  1.8
src/core/qgscoordinatereferencesystem.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002                           qgscoordinatereferencesystem.cpp
00003 
00004                              -------------------
00005     begin                : 2007
00006     copyright            : (C) 2007 by Gary E. Sherman
00007     email                : sherman@mrcc.com
00008 ***************************************************************************/
00009 
00010 /***************************************************************************
00011  *                                                                         *
00012  *   This program is free software; you can redistribute it and/or modify  *
00013  *   it under the terms of the GNU General Public License as published by  *
00014  *   the Free Software Foundation; either version 2 of the License, or     *
00015  *   (at your option) any later version.                                   *
00016  *                                                                         *
00017  ***************************************************************************/
00018 #include "qgscoordinatereferencesystem.h"
00019 
00020 #include <cmath>
00021 
00022 #include <QDir>
00023 #include <QTemporaryFile>
00024 #include <QDomNode>
00025 #include <QDomElement>
00026 #include <QFileInfo>
00027 #include <QRegExp>
00028 #include <QTextStream>
00029 
00030 #include "qgsapplication.h"
00031 #include "qgscrscache.h"
00032 #include "qgslogger.h"
00033 #include "qgsmessagelog.h"
00034 #include "qgis.h" //const vals declared here
00035 
00036 #include <sqlite3.h>
00037 #include <proj_api.h>
00038 
00039 //gdal and ogr includes (needed for == operator)
00040 #include <ogr_srs_api.h>
00041 #include <cpl_error.h>
00042 #include <cpl_conv.h>
00043 
00044 CUSTOM_CRS_VALIDATION QgsCoordinateReferenceSystem::mCustomSrsValidation = NULL;
00045 
00046 //--------------------------
00047 
00048 QgsCoordinateReferenceSystem::QgsCoordinateReferenceSystem()
00049     : mMapUnits( QGis::UnknownUnit )
00050     , mIsValidFlag( 0 )
00051     , mValidationHint( "" )
00052 {
00053   mCRS = OSRNewSpatialReference( NULL );
00054 }
00055 
00056 QgsCoordinateReferenceSystem::QgsCoordinateReferenceSystem( QString theDefinition )
00057     : mMapUnits( QGis::UnknownUnit )
00058     , mIsValidFlag( 0 )
00059     , mValidationHint( "" )
00060 {
00061   mCRS = OSRNewSpatialReference( NULL );
00062   createFromString( theDefinition );
00063 }
00064 
00065 
00066 QgsCoordinateReferenceSystem::QgsCoordinateReferenceSystem( const long theId, CrsType theType )
00067     : mMapUnits( QGis::UnknownUnit )
00068     , mIsValidFlag( 0 )
00069     , mValidationHint( "" )
00070 {
00071   mCRS = OSRNewSpatialReference( NULL );
00072   createFromId( theId, theType );
00073 }
00074 
00075 QgsCoordinateReferenceSystem::~QgsCoordinateReferenceSystem()
00076 {
00077   OSRDestroySpatialReference( mCRS );
00078 }
00079 
00080 bool QgsCoordinateReferenceSystem::createFromId( const long theId, CrsType theType )
00081 {
00082   bool result = false;
00083   switch ( theType )
00084   {
00085     case InternalCrsId:
00086       result = createFromSrsId( theId );
00087       break;
00088     case PostgisCrsId:
00089       result = createFromSrid( theId );
00090       break;
00091     case EpsgCrsId:
00092       result = createFromOgcWmsCrs( QString( "EPSG:%1" ).arg( theId ) );
00093       break;
00094     default:
00095       //THIS IS BAD...THIS PART OF CODE SHOULD NEVER BE REACHED...
00096       QgsDebugMsg( "Unexpected case reached!" );
00097   };
00098   return result;
00099 }
00100 
00101 bool QgsCoordinateReferenceSystem::createFromString( const QString theDefinition )
00102 {
00103   bool result = false;
00104   QRegExp reCrsId( "^(epsg|postgis|internal)\\:(\\d+)$", Qt::CaseInsensitive );
00105   if ( reCrsId.indexIn( theDefinition ) == 0 )
00106   {
00107     QString authName = reCrsId.cap( 1 ).toLower();
00108     CrsType type = InternalCrsId;
00109     if ( authName == "epsg" )
00110       type = EpsgCrsId;
00111     if ( authName == "postgis" )
00112       type = PostgisCrsId;
00113     long id = reCrsId.cap( 2 ).toLong();
00114     result = createFromId( id, type );
00115   }
00116   else
00117   {
00118     QRegExp reCrsStr( "^(?:(wkt|proj4)\\:)?(.+)$", Qt::CaseInsensitive );
00119     if ( reCrsStr.indexIn( theDefinition ) == 0 )
00120     {
00121       if ( reCrsStr.cap( 1 ).toLower() == "proj4" )
00122       {
00123         result = createFromProj4( reCrsStr.cap( 2 ) );
00124       }
00125       else
00126       {
00127         result = createFromWkt( reCrsStr.cap( 2 ) );
00128       }
00129     }
00130   }
00131   return result;
00132 }
00133 
00134 bool QgsCoordinateReferenceSystem::createFromUserInput( const QString theDefinition )
00135 {
00136   QString theWkt;
00137   char *wkt = NULL;
00138   OGRSpatialReferenceH crs = OSRNewSpatialReference( NULL );
00139 
00140   // make sure towgs84 parameter is loaded if using an ESRI definition and gdal >= 1.9
00141 #if GDAL_VERSION_NUM >= 1900
00142   if ( theDefinition.startsWith( "ESRI::" ) )
00143   {
00144     setupESRIWktFix();
00145   }
00146 #endif
00147 
00148   if ( OSRSetFromUserInput( crs, theDefinition.toLocal8Bit().constData() ) == OGRERR_NONE )
00149   {
00150     if ( OSRExportToWkt( crs, &wkt ) == OGRERR_NONE )
00151     {
00152       theWkt = wkt;
00153       OGRFree( wkt );
00154     }
00155     OSRDestroySpatialReference( crs );
00156   }
00157   //QgsDebugMsg( "theDefinition: " + theDefinition + " theWkt = " + theWkt );
00158   return createFromWkt( theWkt );
00159 }
00160 
00161 void QgsCoordinateReferenceSystem::setupESRIWktFix( )
00162 {
00163   // make sure towgs84 parameter is loaded if gdal >= 1.9
00164   // this requires setting GDAL_FIX_ESRI_WKT=GEOGCS (see qgis bug #5598 and gdal bug #4673)
00165 #if GDAL_VERSION_NUM >= 1900
00166   const char* configOld = CPLGetConfigOption( "GDAL_FIX_ESRI_WKT", "" );
00167   const char* configNew = "GEOGCS";
00168   // only set if it was not set, to let user change the value if needed
00169   if ( strcmp( configOld, "" ) == 0 )
00170   {
00171     CPLSetConfigOption( "GDAL_FIX_ESRI_WKT", configNew );
00172     if ( strcmp( configNew, CPLGetConfigOption( "GDAL_FIX_ESRI_WKT", "" ) ) != 0 )
00173       QgsLogger::warning( QString( "GDAL_FIX_ESRI_WKT could not be set to %1 : %2"
00174                                  ).arg( configNew ).arg( CPLGetConfigOption( "GDAL_FIX_ESRI_WKT", "" ) ) ) ;
00175     QgsDebugMsg( QString( "set GDAL_FIX_ESRI_WKT : %1" ).arg( configNew ) );
00176   }
00177   else
00178     QgsDebugMsg( QString( "GDAL_FIX_ESRI_WKT was already set : %1" ).arg( configNew ) );
00179 #endif
00180 }
00181 
00182 bool QgsCoordinateReferenceSystem::createFromOgcWmsCrs( QString theCrs )
00183 {
00184   QRegExp re( "(user|custom|qgis):(\\d+)", Qt::CaseInsensitive );
00185   if ( re.exactMatch( theCrs ) && createFromSrsId( re.cap( 2 ).toInt() ) )
00186   {
00187     return true;
00188   }
00189 
00190   if ( loadFromDb( QgsApplication::srsDbFilePath(), "lower(auth_name||':'||auth_id)", theCrs.toLower() ) )
00191     return true;
00192 
00193   if ( theCrs.compare( "CRS:84", Qt::CaseInsensitive ) == 0 )
00194   {
00195     createFromSrsId( GEOCRS_ID );
00196     return true;
00197   }
00198 
00199   return false;
00200 }
00201 
00202 QgsCoordinateReferenceSystem::QgsCoordinateReferenceSystem( const QgsCoordinateReferenceSystem &srs )
00203 {
00204   mCRS = OSRNewSpatialReference( NULL );
00205   *this = srs;
00206 }
00207 
00208 // Assignment operator
00209 QgsCoordinateReferenceSystem& QgsCoordinateReferenceSystem::operator=( const QgsCoordinateReferenceSystem & srs )
00210 {
00211   if ( &srs != this )
00212   {
00213     mSrsId = srs.mSrsId;
00214     mDescription = srs.mDescription;
00215     mProjectionAcronym = srs.mProjectionAcronym;
00216     mEllipsoidAcronym = srs.mEllipsoidAcronym;
00217     mGeoFlag = srs.mGeoFlag;
00218     mAxisInverted = srs.mAxisInverted;
00219     mMapUnits = srs.mMapUnits;
00220     mSRID = srs.mSRID;
00221     mAuthId = srs.mAuthId;
00222     mIsValidFlag = srs.mIsValidFlag;
00223     mValidationHint = srs.mValidationHint;
00224     mWkt = srs.mWkt;
00225     if ( mIsValidFlag )
00226     {
00227       OSRDestroySpatialReference( mCRS );
00228       mCRS = OSRClone( srs.mCRS );
00229     }
00230   }
00231   return *this;
00232 }
00233 
00234 // Misc helper functions -----------------------
00235 
00236 
00237 void QgsCoordinateReferenceSystem::validate()
00238 {
00239   if ( mIsValidFlag )
00240     return;
00241 
00242   // try to validate using custom validation routines
00243   if ( mCustomSrsValidation )
00244     mCustomSrsValidation( this );
00245 
00246   if ( !mIsValidFlag )
00247     // set the default
00248     createFromOgcWmsCrs( GEO_EPSG_CRS_AUTHID );
00249 }
00250 
00251 bool QgsCoordinateReferenceSystem::createFromSrid( long id )
00252 {
00253   return loadFromDb( QgsApplication::srsDbFilePath(), "srid", QString::number( id ) );
00254 }
00255 
00256 bool QgsCoordinateReferenceSystem::createFromEpsg( long id )
00257 {
00258   return createFromOgcWmsCrs( QString( "EPSG:%1" ).arg( id ) );
00259 }
00260 
00261 bool QgsCoordinateReferenceSystem::createFromSrsId( long id )
00262 {
00263   return loadFromDb( id < USER_CRS_START_ID ? QgsApplication::srsDbFilePath() :
00264                      QgsApplication::qgisUserDbFilePath(),
00265                      "srs_id", QString::number( id ) );
00266 }
00267 
00268 bool QgsCoordinateReferenceSystem::loadFromDb( QString db, QString expression, QString value )
00269 {
00270   QgsDebugMsgLevel( "load CRS from " + db + " where " + expression + " is " + value, 3 );
00271   mIsValidFlag = false;
00272   mWkt.clear();
00273 
00274   QFileInfo myInfo( db );
00275   if ( !myInfo.exists() )
00276   {
00277     QgsDebugMsg( "failed : " + db + " does not exist!" );
00278     return mIsValidFlag;
00279   }
00280 
00281   sqlite3      *myDatabase;
00282   const char   *myTail;
00283   sqlite3_stmt *myPreparedStatement;
00284   int           myResult;
00285   //check the db is available
00286   myResult = openDb( db, &myDatabase );
00287   if ( myResult != SQLITE_OK )
00288   {
00289     QgsDebugMsg( "failed : " + db + " could not be opened!" );
00290     return mIsValidFlag;
00291   }
00292 
00293   /*
00294     srs_id INTEGER PRIMARY KEY,
00295     description text NOT NULL,
00296     projection_acronym text NOT NULL,
00297     ellipsoid_acronym NOT NULL,
00298     parameters text NOT NULL,
00299     srid integer NOT NULL,
00300     auth_name varchar NOT NULL,
00301     auth_id integer NOT NULL,
00302     is_geo integer NOT NULL);
00303   */
00304 
00305   QString mySql = "select srs_id,description,projection_acronym,"
00306                   "ellipsoid_acronym,parameters,srid,auth_name||':'||auth_id,is_geo "
00307                   "from tbl_srs where " + expression + "=" + quotedValue( value ) + " order by deprecated";
00308   myResult = sqlite3_prepare( myDatabase, mySql.toUtf8(),
00309                               mySql.toUtf8().length(),
00310                               &myPreparedStatement, &myTail );
00311   // XXX Need to free memory from the error msg if one is set
00312   if ( myResult == SQLITE_OK && sqlite3_step( myPreparedStatement ) == SQLITE_ROW )
00313   {
00314     mSrsId = QString::fromUtf8(( char * )sqlite3_column_text(
00315                                  myPreparedStatement, 0 ) ).toLong();
00316     mDescription = QString::fromUtf8(( char * )sqlite3_column_text(
00317                                        myPreparedStatement, 1 ) );
00318     mProjectionAcronym = QString::fromUtf8(( char * )sqlite3_column_text( myPreparedStatement, 2 ) );
00319     mEllipsoidAcronym = QString::fromUtf8(( char * )sqlite3_column_text( myPreparedStatement, 3 ) );
00320     QString toProj4 = QString::fromUtf8(( char * )sqlite3_column_text( myPreparedStatement, 4 ) );
00321     mSRID = QString::fromUtf8(( char * )sqlite3_column_text( myPreparedStatement, 5 ) ).toLong();
00322     mAuthId = QString::fromUtf8(( char * )sqlite3_column_text( myPreparedStatement, 6 ) );
00323     mGeoFlag = QString::fromUtf8(( char * )sqlite3_column_text( myPreparedStatement, 7 ) ).toInt() != 0;
00324     mAxisInverted = -1;
00325 
00326     if ( mSrsId >= USER_CRS_START_ID && mAuthId.isEmpty() )
00327     {
00328       mAuthId = QString( "USER:%1" ).arg( mSrsId );
00329     }
00330     else if ( mAuthId.startsWith( "EPSG:", Qt::CaseInsensitive ) )
00331     {
00332       OSRDestroySpatialReference( mCRS );
00333       mCRS = OSRNewSpatialReference( NULL );
00334       mIsValidFlag = OSRSetFromUserInput( mCRS, mAuthId.toLower().toAscii() ) == OGRERR_NONE;
00335       setMapUnits();
00336     }
00337 
00338     if ( !mIsValidFlag )
00339     {
00340       setProj4String( toProj4 );
00341     }
00342   }
00343   else
00344   {
00345     QgsDebugMsg( "failed : " + mySql );
00346   }
00347   sqlite3_finalize( myPreparedStatement );
00348   sqlite3_close( myDatabase );
00349   return mIsValidFlag;
00350 }
00351 
00352 bool QgsCoordinateReferenceSystem::axisInverted() const
00353 {
00354   if ( mAxisInverted == -1 )
00355   {
00356     OGRAxisOrientation orientation;
00357     const char *axis0 = OSRGetAxis( mCRS, mGeoFlag ? "GEOGCS" : "PROJCS", 0, &orientation );
00358     mAxisInverted = mGeoFlag
00359                     ? ( orientation == OAO_East || orientation == OAO_West || orientation == OAO_Other )
00360                     : ( orientation == OAO_North || orientation == OAO_South );
00361     QgsDebugMsg( QString( "srid:%1 axis0:%2 orientation:%3 inverted:%4" ).arg( mSRID ).arg( axis0 ).arg( OSRAxisEnumToName( orientation ) ).arg( mAxisInverted ) );
00362     Q_UNUSED( axis0 );
00363   }
00364 
00365   return mAxisInverted != 0;
00366 }
00367 
00368 bool QgsCoordinateReferenceSystem::createFromWkt( QString theWkt )
00369 {
00370   mIsValidFlag = false;
00371   mWkt.clear();
00372 
00373   if ( theWkt.isEmpty() )
00374   {
00375     QgsDebugMsg( "theWkt is uninitialised, operation failed" );
00376     return mIsValidFlag;
00377   }
00378   QgsDebugMsg( "wkt: " + theWkt );
00379   QByteArray ba = theWkt.toLatin1();
00380   const char *pWkt = ba.data();
00381 
00382   OGRErr myInputResult = OSRImportFromWkt( mCRS, ( char ** ) & pWkt );
00383 
00384   if ( myInputResult != OGRERR_NONE )
00385   {
00386     QgsDebugMsg( "\n---------------------------------------------------------------" );
00387     QgsDebugMsg( "This CRS could *** NOT *** be set from the supplied Wkt " );
00388     QgsDebugMsg( "INPUT: " + theWkt );
00389     QgsDebugMsg( QString( "UNUSED WKT: %1" ).arg( pWkt ) );
00390     QgsDebugMsg( "---------------------------------------------------------------\n" );
00391     return mIsValidFlag;
00392   }
00393 
00394   if ( OSRAutoIdentifyEPSG( mCRS ) == OGRERR_NONE )
00395   {
00396     QString authid = QString( "%1:%2" )
00397                      .arg( OSRGetAuthorityName( mCRS, NULL ) )
00398                      .arg( OSRGetAuthorityCode( mCRS, NULL ) );
00399     QgsDebugMsg( "authid recognized as " + authid );
00400     return createFromOgcWmsCrs( authid );
00401   }
00402 
00403   // always morph from esri as it doesn't hurt anything
00404   // FW: Hey, that's not right!  It can screw stuff up! Disable
00405   //myOgrSpatialRef.morphFromESRI();
00406 
00407   // create the proj4 structs needed for transforming
00408   char *proj4src = NULL;
00409   OSRExportToProj4( mCRS, &proj4src );
00410 
00411   //now that we have the proj4string, delegate to createFromProj4 so
00412   // that we can try to fill in the remaining class members...
00413   //create from Proj will set the isValidFlag
00414   if ( !createFromProj4( proj4src ) )
00415   {
00416     CPLFree( proj4src );
00417 
00418     // try fixed up version
00419     OSRFixup( mCRS );
00420 
00421     OSRExportToProj4( mCRS, &proj4src );
00422 
00423     createFromProj4( proj4src );
00424   }
00425 
00426   CPLFree( proj4src );
00427 
00428   return mIsValidFlag;
00429   //setMapunits will be called by createfromproj above
00430 }
00431 
00432 bool QgsCoordinateReferenceSystem::isValid() const
00433 {
00434   return mIsValidFlag;
00435 }
00436 
00437 bool QgsCoordinateReferenceSystem::createFromProj4( const QString theProj4String )
00438 {
00439   //
00440   // Examples:
00441   // +proj=tmerc +lat_0=0 +lon_0=-62 +k=0.999500 +x_0=400000 +y_0=0
00442   // +ellps=clrk80 +towgs84=-255,-15,71,0,0,0,0 +units=m +no_defs
00443   //
00444   // +proj=lcc +lat_1=46.8 +lat_0=46.8 +lon_0=2.337229166666664 +k_0=0.99987742
00445   // +x_0=600000 +y_0=2200000 +a=6378249.2 +b=6356515.000000472 +units=m +no_defs
00446   //
00447   QString myProj4String = theProj4String.trimmed();
00448   QgsDebugMsg( "proj4: " + myProj4String );
00449   mIsValidFlag = false;
00450   mWkt.clear();
00451 
00452   QRegExp myProjRegExp( "\\+proj=(\\S+)" );
00453   int myStart = myProjRegExp.indexIn( myProj4String );
00454   if ( myStart == -1 )
00455   {
00456     QgsDebugMsg( "proj string supplied has no +proj argument" );
00457     return mIsValidFlag;
00458   }
00459 
00460   mProjectionAcronym = myProjRegExp.cap( 1 );
00461 
00462   QRegExp myEllipseRegExp( "\\+ellps=(\\S+)" );
00463   myStart = myEllipseRegExp.indexIn( myProj4String );
00464   if ( myStart == -1 )
00465   {
00466     QgsDebugMsg( "proj string supplied has no +ellps argument" );
00467     mEllipsoidAcronym = "";
00468   }
00469   else
00470   {
00471     mEllipsoidAcronym = myEllipseRegExp.cap( 1 );
00472   }
00473 
00474   QRegExp myAxisRegExp( "\\+a=(\\S+)" );
00475   myStart = myAxisRegExp.indexIn( myProj4String );
00476   if ( myStart == -1 )
00477   {
00478     QgsDebugMsg( "proj string supplied has no +a argument" );
00479   }
00480 
00481   /*
00482    * We try to match the proj string to and srsid using the following logic:
00483    *
00484    * - perform a whole text search on srs name (if not null). The srs name will
00485    *   have been set if this method has been delegated to from createFromWkt.
00486    * Normally we wouldnt expect this to work, but its worth trying first
00487    * as its quicker than methods below..
00488    */
00489   long mySrsId = 0;
00490   QgsCoordinateReferenceSystem::RecordMap myRecord;
00491 
00492   /*
00493    * - if the above does not match perform a whole text search on proj4 string (if not null)
00494    */
00495   // QgsDebugMsg( "wholetext match on name failed, trying proj4string match" );
00496   myRecord = getRecord( "select * from tbl_srs where parameters=" + quotedValue( myProj4String ) + " order by deprecated" );
00497   if ( myRecord.empty() )
00498   {
00499     // Ticket #722 - aaronr
00500     // Check if we can swap the lat_1 and lat_2 params (if they exist) to see if we match...
00501     // First we check for lat_1 and lat_2
00502     QRegExp myLat1RegExp( "\\+lat_1=\\S+" );
00503     QRegExp myLat2RegExp( "\\+lat_2=\\S+" );
00504     int myStart1 = 0;
00505     int myLength1 = 0;
00506     int myStart2 = 0;
00507     int myLength2 = 0;
00508     QString lat1Str = "";
00509     QString lat2Str = "";
00510     myStart1 = myLat1RegExp.indexIn( myProj4String, myStart1 );
00511     myStart2 = myLat2RegExp.indexIn( myProj4String, myStart2 );
00512     if ( myStart1 != -1 && myStart2 != -1 )
00513     {
00514       myLength1 = myLat1RegExp.matchedLength();
00515       myLength2 = myLat2RegExp.matchedLength();
00516       lat1Str = myProj4String.mid( myStart1 + LAT_PREFIX_LEN, myLength1 - LAT_PREFIX_LEN );
00517       lat2Str = myProj4String.mid( myStart2 + LAT_PREFIX_LEN, myLength2 - LAT_PREFIX_LEN );
00518     }
00519     // If we found the lat_1 and lat_2 we need to swap and check to see if we can find it...
00520     if ( lat1Str != "" && lat2Str != "" )
00521     {
00522       // Make our new string to check...
00523       QString theProj4StringModified = myProj4String;
00524       // First just swap in the lat_2 value for lat_1 value
00525       theProj4StringModified.replace( myStart1 + LAT_PREFIX_LEN, myLength1 - LAT_PREFIX_LEN, lat2Str );
00526       // Now we have to find the lat_2 location again since it has potentially moved...
00527       myStart2 = 0;
00528       myStart2 = myLat2RegExp.indexIn( theProj4String, myStart2 );
00529       theProj4StringModified.replace( myStart2 + LAT_PREFIX_LEN, myLength2 - LAT_PREFIX_LEN, lat1Str );
00530       QgsDebugMsg( "trying proj4string match with swapped lat_1,lat_2" );
00531       myRecord = getRecord( "select * from tbl_srs where parameters=" + quotedValue( theProj4StringModified.trimmed() ) + " order by deprecated" );
00532     }
00533   }
00534 
00535   if ( myRecord.empty() )
00536   {
00537     // match all parameters individually:
00538     // - order of parameters doesn't matter
00539     // - found definition may have more parameters (like +towgs84 in GDAL)
00540     // - retry without datum, if no match is found (looks like +datum<>WGS84 was dropped in GDAL)
00541 
00542     QString sql = "SELECT * FROM tbl_srs WHERE ";
00543     QString delim = "";
00544     QString datum;
00545 
00546     // split on spaces followed by a plus sign (+) to deal
00547     // also with parameters containing spaces (e.g. +nadgrids)
00548     // make sure result is trimmed (#5598)
00549     foreach( QString param, myProj4String.split( QRegExp( "\\s+(?=\\+)" ), QString::SkipEmptyParts ) )
00550     {
00551       QString arg = QString( "' '||parameters||' ' LIKE %1" ).arg( quotedValue( QString( "% %1 %" ).arg( param.trimmed() ) ) );
00552       if ( param.startsWith( "+datum=" ) )
00553       {
00554         datum = arg;
00555       }
00556       else
00557       {
00558         sql += delim + arg;
00559         delim = " AND ";
00560       }
00561     }
00562 
00563     if ( !datum.isEmpty() )
00564     {
00565       myRecord = getRecord( sql + delim + datum + " order by deprecated" );
00566     }
00567 
00568     if ( myRecord.empty() )
00569     {
00570       // datum might have disappeared in definition - retry without it
00571       myRecord = getRecord( sql + " order by deprecated" );
00572     }
00573   }
00574 
00575   if ( !myRecord.empty() )
00576   {
00577     mySrsId = myRecord["srs_id"].toLong();
00578     QgsDebugMsg( "proj4string param match search for srsid returned srsid: " + QString::number( mySrsId ) );
00579     if ( mySrsId > 0 )
00580     {
00581       createFromSrsId( mySrsId );
00582     }
00583   }
00584   else
00585   {
00586     // Last ditch attempt to piece together what we know of the projection to find a match...
00587     QgsDebugMsg( "globbing search for srsid from this proj string" );
00588     setProj4String( myProj4String );
00589     mySrsId = findMatchingProj();
00590     QgsDebugMsg( "globbing search for srsid returned srsid: " + QString::number( mySrsId ) );
00591     if ( mySrsId > 0 )
00592     {
00593       createFromSrsId( mySrsId );
00594     }
00595     else
00596     {
00597       mIsValidFlag = false;
00598     }
00599   }
00600 
00601   // if we failed to look up the projection in database, don't worry. we can still use it :)
00602   if ( !mIsValidFlag )
00603   {
00604     QgsDebugMsg( "Projection is not found in databases." );
00605     setProj4String( myProj4String );
00606 
00607     // Is the SRS is valid now, we know it's a decent +proj string that can be entered into the srs.db
00608     if ( mIsValidFlag )
00609     {
00610       // but the proj.4 parsed string might already be in our database
00611       myRecord = getRecord( "select * from tbl_srs where parameters=" + quotedValue( toProj4() ) + " order by deprecated" );
00612       if ( myRecord.empty() )
00613       {
00614         // It's not, so try to add it
00615         QgsDebugMsg( "Projection appears to be valid. Save to database!" );
00616         mIsValidFlag = saveAsUserCRS();
00617 
00618         if ( mIsValidFlag )
00619         {
00620           // but validate that it's there afterwards
00621           myRecord = getRecord( "select * from tbl_srs where parameters=" + quotedValue( toProj4() ) + " order by deprecated" );
00622         }
00623       }
00624 
00625       if ( !myRecord.empty() )
00626       {
00627         // take the srid from the record
00628         mySrsId = myRecord["srs_id"].toLong();
00629         QgsDebugMsg( "proj4string match search for srsid returned srsid: " + QString::number( mySrsId ) );
00630         if ( mySrsId > 0 )
00631         {
00632           createFromSrsId( mySrsId );
00633         }
00634         else
00635         {
00636           QgsDebugMsg( QString( "invalid srid %1 found" ).arg( mySrsId ) );
00637           mIsValidFlag = false;
00638         }
00639       }
00640       else
00641       {
00642         QgsDebugMsg( "Couldn't find newly added proj string?" );
00643         mIsValidFlag = false;
00644       }
00645     }
00646   }
00647 
00648   return mIsValidFlag;
00649 }
00650 
00651 //private method meant for internal use by this class only
00652 QgsCoordinateReferenceSystem::RecordMap QgsCoordinateReferenceSystem::getRecord( QString theSql )
00653 {
00654   QString myDatabaseFileName;
00655   QgsCoordinateReferenceSystem::RecordMap myMap;
00656   QString myFieldName;
00657   QString myFieldValue;
00658   sqlite3      *myDatabase;
00659   const char   *myTail;
00660   sqlite3_stmt *myPreparedStatement;
00661   int           myResult;
00662 
00663   QgsDebugMsg( "running query: " + theSql );
00664   // Get the full path name to the sqlite3 spatial reference database.
00665   myDatabaseFileName = QgsApplication::srsDbFilePath();
00666   QFileInfo myInfo( myDatabaseFileName );
00667   if ( !myInfo.exists() )
00668   {
00669     QgsDebugMsg( "failed : " + myDatabaseFileName +
00670                  " does not exist!" );
00671     return myMap;
00672   }
00673 
00674   //check the db is available
00675   myResult = openDb( myDatabaseFileName, &myDatabase );
00676   if ( myResult != SQLITE_OK )
00677   {
00678     return myMap;
00679   }
00680 
00681   myResult = sqlite3_prepare( myDatabase, theSql.toUtf8(), theSql.toUtf8().length(), &myPreparedStatement, &myTail );
00682   // XXX Need to free memory from the error msg if one is set
00683   if ( myResult == SQLITE_OK && sqlite3_step( myPreparedStatement ) == SQLITE_ROW )
00684   {
00685     QgsDebugMsg( "trying system srs.db" );
00686     int myColumnCount = sqlite3_column_count( myPreparedStatement );
00687     //loop through each column in the record adding its expression name and value to the map
00688     for ( int myColNo = 0; myColNo < myColumnCount; myColNo++ )
00689     {
00690       myFieldName = QString::fromUtf8(( char * )sqlite3_column_name( myPreparedStatement, myColNo ) );
00691       myFieldValue = QString::fromUtf8(( char * )sqlite3_column_text( myPreparedStatement, myColNo ) );
00692       myMap[myFieldName] = myFieldValue;
00693     }
00694   }
00695   else
00696   {
00697     QgsDebugMsg( "trying user qgis.db" );
00698     sqlite3_finalize( myPreparedStatement );
00699     sqlite3_close( myDatabase );
00700 
00701     myDatabaseFileName = QgsApplication::qgisUserDbFilePath();
00702     QFileInfo myFileInfo;
00703     myFileInfo.setFile( myDatabaseFileName );
00704     if ( !myFileInfo.exists( ) )
00705     {
00706       QgsDebugMsg( "user qgis.db not found" );
00707       return myMap;
00708     }
00709 
00710     //check the db is available
00711     myResult = openDb( myDatabaseFileName, &myDatabase );
00712     if ( myResult != SQLITE_OK )
00713     {
00714       return myMap;
00715     }
00716 
00717     myResult = sqlite3_prepare( myDatabase, theSql.toUtf8(), theSql.toUtf8().length(), &myPreparedStatement, &myTail );
00718     // XXX Need to free memory from the error msg if one is set
00719     if ( myResult == SQLITE_OK && sqlite3_step( myPreparedStatement ) == SQLITE_ROW )
00720     {
00721       int myColumnCount = sqlite3_column_count( myPreparedStatement );
00722       //loop through each column in the record adding its field name and value to the map
00723       for ( int myColNo = 0; myColNo < myColumnCount; myColNo++ )
00724       {
00725         myFieldName = QString::fromUtf8(( char * )sqlite3_column_name( myPreparedStatement, myColNo ) );
00726         myFieldValue = QString::fromUtf8(( char * )sqlite3_column_text( myPreparedStatement, myColNo ) );
00727         myMap[myFieldName] = myFieldValue;
00728       }
00729     }
00730     else
00731     {
00732       QgsDebugMsg( "failed :  " + theSql );
00733 
00734     }
00735   }
00736   sqlite3_finalize( myPreparedStatement );
00737   sqlite3_close( myDatabase );
00738 
00739 #ifdef QGISDEBUG
00740   QgsDebugMsg( "retrieved:  " + theSql );
00741   RecordMap::Iterator it;
00742   for ( it = myMap.begin(); it != myMap.end(); ++it )
00743   {
00744     QgsDebugMsgLevel( it.key() + " => " + it.value(), 2 );
00745   }
00746 #endif
00747 
00748   return myMap;
00749 }
00750 
00751 // Accessors -----------------------------------
00752 
00753 long QgsCoordinateReferenceSystem::srsid() const
00754 {
00755   return mSrsId;
00756 }
00757 
00758 long QgsCoordinateReferenceSystem::postgisSrid() const
00759 {
00760 
00761   return mSRID;
00762 
00763 }
00764 
00765 long QgsCoordinateReferenceSystem::epsg() const
00766 {
00767   if ( mAuthId.startsWith( "EPSG:", Qt::CaseInsensitive ) )
00768     return mAuthId.mid( 5 ).toLong();
00769   else
00770     return 0;
00771 }
00772 
00773 QString QgsCoordinateReferenceSystem::authid() const
00774 {
00775   return mAuthId;
00776 }
00777 
00778 QString QgsCoordinateReferenceSystem::description() const
00779 {
00780   if ( mDescription.isNull() )
00781   {
00782     return "";
00783   }
00784   else
00785   {
00786     return mDescription;
00787   }
00788 }
00789 
00790 QString QgsCoordinateReferenceSystem::projectionAcronym() const
00791 {
00792   if ( mProjectionAcronym.isNull() )
00793   {
00794     return "";
00795   }
00796   else
00797   {
00798     return mProjectionAcronym;
00799   }
00800 }
00801 
00802 QString QgsCoordinateReferenceSystem::ellipsoidAcronym() const
00803 {
00804   if ( mEllipsoidAcronym.isNull() )
00805   {
00806     return "";
00807   }
00808   else
00809   {
00810     return mEllipsoidAcronym;
00811   }
00812 }
00813 
00814 QString QgsCoordinateReferenceSystem::toProj4() const
00815 {
00816   if ( !mIsValidFlag )
00817     return "";
00818 
00819   QString toProj4;
00820   char *proj4src = NULL;
00821   OSRExportToProj4( mCRS, &proj4src );
00822   toProj4 = proj4src;
00823   CPLFree( proj4src );
00824 
00825   // Stray spaces at the end?
00826   return toProj4.trimmed();
00827 }
00828 
00829 bool QgsCoordinateReferenceSystem::geographicFlag() const
00830 {
00831   return mGeoFlag;
00832 }
00833 
00834 QGis::UnitType QgsCoordinateReferenceSystem::mapUnits() const
00835 {
00836   return mMapUnits;
00837 }
00838 
00839 
00840 // Mutators -----------------------------------
00841 
00842 
00843 void QgsCoordinateReferenceSystem::setInternalId( long theSrsId )
00844 {
00845   mSrsId = theSrsId;
00846 }
00847 void QgsCoordinateReferenceSystem::setAuthId( QString authId )
00848 {
00849   mAuthId = authId;
00850 }
00851 void QgsCoordinateReferenceSystem::setSrid( long theSrid )
00852 {
00853   mSRID = theSrid;
00854 }
00855 void QgsCoordinateReferenceSystem::setDescription( QString theDescription )
00856 {
00857   mDescription = theDescription;
00858 }
00859 void QgsCoordinateReferenceSystem::setProj4String( QString theProj4String )
00860 {
00861   const char *oldlocale = setlocale( LC_NUMERIC, NULL );
00862 
00863   setlocale( LC_NUMERIC, "C" );
00864   OSRDestroySpatialReference( mCRS );
00865   mCRS = OSRNewSpatialReference( NULL );
00866   mIsValidFlag =
00867     OSRImportFromProj4( mCRS, theProj4String.trimmed().toLatin1().constData() )
00868     == OGRERR_NONE;
00869   mWkt.clear();
00870   setMapUnits();
00871 
00872 #if defined(QGISDEBUG) && QGISDEBUG>=3
00873   debugPrint();
00874 #endif
00875 
00876   setlocale( LC_NUMERIC, oldlocale );
00877 }
00878 void QgsCoordinateReferenceSystem::setGeographicFlag( bool theGeoFlag )
00879 {
00880   mGeoFlag = theGeoFlag;
00881 }
00882 void QgsCoordinateReferenceSystem::setEpsg( long theEpsg )
00883 {
00884   mAuthId = QString( "EPSG:%1" ).arg( theEpsg );
00885 }
00886 void  QgsCoordinateReferenceSystem::setProjectionAcronym( QString theProjectionAcronym )
00887 {
00888   mProjectionAcronym = theProjectionAcronym;
00889 }
00890 void  QgsCoordinateReferenceSystem::setEllipsoidAcronym( QString theEllipsoidAcronym )
00891 {
00892   mEllipsoidAcronym = theEllipsoidAcronym;
00893 }
00894 
00895 void QgsCoordinateReferenceSystem::setMapUnits()
00896 {
00897   if ( !mIsValidFlag )
00898   {
00899     mMapUnits = QGis::UnknownUnit;
00900     return;
00901   }
00902 
00903   char *unitName;
00904 
00905   // Of interest to us is that this call adds in a unit parameter if
00906   // one doesn't already exist.
00907   OSRFixup( mCRS );
00908 
00909   if ( OSRIsProjected( mCRS ) )
00910   {
00911     double toMeter = OSRGetLinearUnits( mCRS, &unitName );
00912     QString unit( unitName );
00913 
00914     // If the units parameter was created during the Fixup() call
00915     // above, the name of the units is likely to be 'unknown'. Try to
00916     // do better than that ... (but perhaps ogr should be enhanced to
00917     // do this instead?).
00918 
00919     static const double feetToMeter = 0.3048;
00920     static const double smallNum = 1e-3;
00921 
00922     if ( qAbs( toMeter - feetToMeter ) < smallNum )
00923       unit = "Foot";
00924 
00925     QgsDebugMsg( "Projection has linear units of " + unit );
00926 
00927     if ( doubleNear( toMeter, 1.0 ) ) //Unit name for meters would be "metre"
00928       mMapUnits = QGis::Meters;
00929     else if ( unit == "Foot" )
00930       mMapUnits = QGis::Feet;
00931     else
00932     {
00933       QgsDebugMsg( "Unsupported map units of " + unit );
00934       mMapUnits = QGis::UnknownUnit;
00935     }
00936   }
00937   else
00938   {
00939     OSRGetAngularUnits( mCRS, &unitName );
00940     QString unit( unitName );
00941     if ( unit == "degree" )
00942       mMapUnits = QGis::Degrees;
00943     else
00944     {
00945       QgsDebugMsg( "Unsupported map units of " + unit );
00946       mMapUnits = QGis::UnknownUnit;
00947     }
00948     QgsDebugMsgLevel( "Projection has angular units of " + unit, 3 );
00949   }
00950 }
00951 
00952 /*
00953 *    check if srs is a geocs or a proj cs (using ogr isGeographic)
00954 *   then sequentially walk through the database (first users qgis.db srs tbl then
00955 *   system srs.db tbl), converting each entry into an ogr srs and using isSame
00956 *   or isSameGeocs (essentially calling the == overloaded operator). We'll try to
00957 *   be smart about this and first parse out the proj and ellpse strings and only
00958 *   check for a match in entities that have the same ellps and proj entries so
00959 *   that it doesnt munch yer cpu so much.
00960 */
00961 long QgsCoordinateReferenceSystem::findMatchingProj()
00962 {
00963   QgsDebugMsg( "entered." );
00964   if ( mEllipsoidAcronym.isNull() ||  mProjectionAcronym.isNull()
00965        || !mIsValidFlag )
00966   {
00967     QgsDebugMsg( "QgsCoordinateReferenceSystem::findMatchingProj will only "
00968                  "work if prj acr ellipsoid acr and proj4string are set"
00969                  " and the current projection is valid!" );
00970     return 0;
00971   }
00972 
00973   sqlite3      *myDatabase;
00974   const char   *myTail;
00975   sqlite3_stmt *myPreparedStatement;
00976   int           myResult;
00977 
00978   // Set up the query to retrieve the projection information
00979   // needed to populate the list
00980   QString mySql = QString( "select srs_id,parameters from tbl_srs where "
00981                            "projection_acronym=%1 and ellipsoid_acronym=%2 order by deprecated" )
00982                   .arg( quotedValue( mProjectionAcronym ) )
00983                   .arg( quotedValue( mEllipsoidAcronym ) );
00984   // Get the full path name to the sqlite3 spatial reference database.
00985   QString myDatabaseFileName = QgsApplication::srsDbFilePath();
00986 
00987   //check the db is available
00988   myResult = openDb( myDatabaseFileName, &myDatabase );
00989   if ( myResult != SQLITE_OK )
00990   {
00991     return 0;
00992   }
00993 
00994   myResult = sqlite3_prepare( myDatabase, mySql.toUtf8(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
00995 // XXX Need to free memory from the error msg if one is set
00996   if ( myResult == SQLITE_OK )
00997   {
00998 
00999     while ( sqlite3_step( myPreparedStatement ) == SQLITE_ROW )
01000     {
01001       QString mySrsId = QString::fromUtf8(( char * )sqlite3_column_text( myPreparedStatement, 0 ) );
01002       QString myProj4String = QString::fromUtf8(( char * )sqlite3_column_text( myPreparedStatement, 1 ) );
01003       if ( toProj4() == myProj4String.trimmed() )
01004       {
01005         QgsDebugMsg( "-------> MATCH FOUND in srs.db srsid: " + mySrsId );
01006         // close the sqlite3 statement
01007         sqlite3_finalize( myPreparedStatement );
01008         sqlite3_close( myDatabase );
01009         return mySrsId.toLong();
01010       }
01011       else
01012       {
01013 // QgsDebugMsg(QString(" Not matched : %1").arg(myProj4String));
01014       }
01015     }
01016   }
01017   QgsDebugMsg( "no match found in srs.db, trying user db now!" );
01018   // close the sqlite3 statement
01019   sqlite3_finalize( myPreparedStatement );
01020   sqlite3_close( myDatabase );
01021   //
01022   // Try the users db now
01023   //
01024 
01025   myDatabaseFileName = QgsApplication::qgisUserDbFilePath();
01026   //check the db is available
01027   myResult = openDb( myDatabaseFileName, &myDatabase );
01028   if ( myResult != SQLITE_OK )
01029   {
01030     return 0;
01031   }
01032 
01033   myResult = sqlite3_prepare( myDatabase, mySql.toUtf8(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
01034 // XXX Need to free memory from the error msg if one is set
01035   if ( myResult == SQLITE_OK )
01036   {
01037 
01038     while ( sqlite3_step( myPreparedStatement ) == SQLITE_ROW )
01039     {
01040       QString mySrsId = QString::fromUtf8(( char * )sqlite3_column_text( myPreparedStatement, 0 ) );
01041       QString myProj4String = QString::fromUtf8(( char * )sqlite3_column_text( myPreparedStatement, 1 ) );
01042       if ( toProj4() == myProj4String.trimmed() )
01043       {
01044         QgsDebugMsg( "-------> MATCH FOUND in user qgis.db srsid: " + mySrsId );
01045         // close the sqlite3 statement
01046         sqlite3_finalize( myPreparedStatement );
01047         sqlite3_close( myDatabase );
01048         return mySrsId.toLong();
01049       }
01050       else
01051       {
01052 // QgsDebugMsg(QString(" Not matched : %1").arg(myProj4String));
01053       }
01054     }
01055   }
01056   QgsDebugMsg( "no match found in user db" );
01057 
01058   // close the sqlite3 statement
01059   sqlite3_finalize( myPreparedStatement );
01060   sqlite3_close( myDatabase );
01061   return 0;
01062 }
01063 
01064 bool QgsCoordinateReferenceSystem::operator==( const QgsCoordinateReferenceSystem &theSrs ) const
01065 {
01066   return mIsValidFlag && theSrs.mIsValidFlag && theSrs.authid() == authid();
01067 }
01068 
01069 bool QgsCoordinateReferenceSystem::operator!=( const QgsCoordinateReferenceSystem &theSrs ) const
01070 {
01071   return  !( *this == theSrs );
01072 }
01073 
01074 bool QgsCoordinateReferenceSystem::equals( QString theProj4String )
01075 {
01076   QgsCoordinateReferenceSystem r;
01077   r.createFromProj4( theProj4String );
01078   return *this == r;
01079 }
01080 
01081 QString QgsCoordinateReferenceSystem::toWkt() const
01082 {
01083   if ( mWkt.isEmpty() )
01084   {
01085     char *wkt;
01086     if ( OSRExportToWkt( mCRS, &wkt ) == OGRERR_NONE )
01087     {
01088       mWkt = wkt;
01089       OGRFree( wkt );
01090     }
01091   }
01092   return mWkt;
01093 }
01094 
01095 bool QgsCoordinateReferenceSystem::readXML( QDomNode & theNode )
01096 {
01097   QgsDebugMsg( "Reading Spatial Ref Sys from xml ------------------------!" );
01098   QDomNode srsNode  = theNode.namedItem( "spatialrefsys" );
01099 
01100   if ( ! srsNode.isNull() )
01101   {
01102     bool initialized = false;
01103 
01104     long srsid = srsNode.namedItem( "srsid" ).toElement().text().toLong();
01105 
01106     QDomNode myNode;
01107 
01108     if ( srsid < USER_CRS_START_ID )
01109     {
01110       myNode = srsNode.namedItem( "authid" );
01111       if ( !myNode.isNull() )
01112       {
01113         operator=( QgsCRSCache::instance()->crsByAuthId( myNode.toElement().text() ) );
01114         if ( isValid() )
01115         {
01116           initialized = true;
01117         }
01118       }
01119 
01120       if ( !initialized )
01121       {
01122         myNode = srsNode.namedItem( "epsg" );
01123         if ( !myNode.isNull() )
01124         {
01125           operator=( QgsCRSCache::instance()->crsByEpsgId( myNode.toElement().text().toLong() ) );
01126           if ( isValid() )
01127           {
01128             initialized = true;
01129           }
01130         }
01131       }
01132     }
01133     else
01134     {
01135       QgsDebugMsg( "Ignoring authid/epsg for user crs." );
01136     }
01137 
01138     if ( initialized )
01139     {
01140       QgsDebugMsg( "Set from auth id" );
01141     }
01142     else
01143     {
01144       myNode = srsNode.namedItem( "proj4" );
01145 
01146       if ( createFromProj4( myNode.toElement().text() ) )
01147       {
01148         // createFromProj4() sets everything, including map units
01149         QgsDebugMsg( "Setting from proj4 string" );
01150       }
01151       else
01152       {
01153         QgsDebugMsg( "Setting from elements one by one" );
01154 
01155         myNode = srsNode.namedItem( "proj4" );
01156         setProj4String( myNode.toElement().text() );
01157 
01158         myNode = srsNode.namedItem( "srsid" );
01159         setInternalId( myNode.toElement().text().toLong() );
01160 
01161         myNode = srsNode.namedItem( "srid" );
01162         setSrid( myNode.toElement().text().toLong() );
01163 
01164         myNode = srsNode.namedItem( "authid" );
01165         setAuthId( myNode.toElement().text() );
01166 
01167         myNode = srsNode.namedItem( "description" );
01168         setDescription( myNode.toElement().text() );
01169 
01170         myNode = srsNode.namedItem( "projectionacronym" );
01171         setProjectionAcronym( myNode.toElement().text() );
01172 
01173         myNode = srsNode.namedItem( "ellipsoidacronym" );
01174         setEllipsoidAcronym( myNode.toElement().text() );
01175 
01176         myNode = srsNode.namedItem( "geographicflag" );
01177         if ( myNode.toElement().text().compare( "true" ) )
01178         {
01179           setGeographicFlag( true );
01180         }
01181         else
01182         {
01183           setGeographicFlag( false );
01184         }
01185 
01186         //make sure the map units have been set
01187         setMapUnits();
01188 
01189         //@TODO this srs needs to be validated!!!
01190         mIsValidFlag = true; //shamelessly hard coded for now
01191       }
01192     }
01193   }
01194   else
01195   {
01196     // Return default CRS if none was found in the XML.
01197     createFromId( GEOCRS_ID, InternalCrsId );
01198   }
01199   return true;
01200 }
01201 
01202 bool QgsCoordinateReferenceSystem::writeXML( QDomNode & theNode, QDomDocument & theDoc ) const
01203 {
01204 
01205   QDomElement myLayerNode = theNode.toElement();
01206   QDomElement mySrsElement  = theDoc.createElement( "spatialrefsys" );
01207 
01208   QDomElement myProj4Element  = theDoc.createElement( "proj4" );
01209   myProj4Element.appendChild( theDoc.createTextNode( toProj4() ) );
01210   mySrsElement.appendChild( myProj4Element );
01211 
01212   QDomElement mySrsIdElement  = theDoc.createElement( "srsid" );
01213   mySrsIdElement.appendChild( theDoc.createTextNode( QString::number( srsid() ) ) );
01214   mySrsElement.appendChild( mySrsIdElement );
01215 
01216   QDomElement mySridElement  = theDoc.createElement( "srid" );
01217   mySridElement.appendChild( theDoc.createTextNode( QString::number( postgisSrid() ) ) );
01218   mySrsElement.appendChild( mySridElement );
01219 
01220   QDomElement myEpsgElement  = theDoc.createElement( "authid" );
01221   myEpsgElement.appendChild( theDoc.createTextNode( authid() ) );
01222   mySrsElement.appendChild( myEpsgElement );
01223 
01224   QDomElement myDescriptionElement  = theDoc.createElement( "description" );
01225   myDescriptionElement.appendChild( theDoc.createTextNode( description() ) );
01226   mySrsElement.appendChild( myDescriptionElement );
01227 
01228   QDomElement myProjectionAcronymElement  = theDoc.createElement( "projectionacronym" );
01229   myProjectionAcronymElement.appendChild( theDoc.createTextNode( projectionAcronym() ) );
01230   mySrsElement.appendChild( myProjectionAcronymElement );
01231 
01232   QDomElement myEllipsoidAcronymElement  = theDoc.createElement( "ellipsoidacronym" );
01233   myEllipsoidAcronymElement.appendChild( theDoc.createTextNode( ellipsoidAcronym() ) );
01234   mySrsElement.appendChild( myEllipsoidAcronymElement );
01235 
01236   QDomElement myGeographicFlagElement  = theDoc.createElement( "geographicflag" );
01237   QString myGeoFlagText = "false";
01238   if ( geographicFlag() )
01239   {
01240     myGeoFlagText = "true";
01241   }
01242 
01243   myGeographicFlagElement.appendChild( theDoc.createTextNode( myGeoFlagText ) );
01244   mySrsElement.appendChild( myGeographicFlagElement );
01245 
01246   myLayerNode.appendChild( mySrsElement );
01247 
01248   return true;
01249 }
01250 
01251 
01252 
01253 //
01254 // Static helper methods below this point only please!
01255 //
01256 
01257 
01258 // Returns the whole proj4 string for the selected srsid
01259 //this is a static method! NOTE I've made it private for now to reduce API clutter TS
01260 QString QgsCoordinateReferenceSystem::proj4FromSrsId( const int theSrsId )
01261 {
01262 
01263   QString myDatabaseFileName;
01264   QString myProjString;
01265   QString mySql = QString( "select parameters from tbl_srs where srs_id = %1 order by deprecated" ).arg( theSrsId );
01266 
01267   QgsDebugMsg( "mySrsId = " + QString::number( theSrsId ) );
01268   QgsDebugMsg( "USER_CRS_START_ID = " + QString::number( USER_CRS_START_ID ) );
01269   QgsDebugMsg( "Selection sql : " + mySql );
01270 
01271   //
01272   // Determine if this is a user projection or a system on
01273   // user projection defs all have srs_id >= 100000
01274   //
01275   if ( theSrsId >= USER_CRS_START_ID )
01276   {
01277     myDatabaseFileName = QgsApplication::qgisUserDbFilePath();
01278     QFileInfo myFileInfo;
01279     myFileInfo.setFile( myDatabaseFileName );
01280     if ( !myFileInfo.exists( ) ) //its unlikely that this condition will ever be reached
01281     {
01282       QgsDebugMsg( "users qgis.db not found" );
01283       return NULL;
01284     }
01285   }
01286   else //must be  a system projection then
01287   {
01288     myDatabaseFileName = QgsApplication::srsDbFilePath();
01289   }
01290   QgsDebugMsg( "db = " + myDatabaseFileName );
01291 
01292   sqlite3 *db;
01293   int rc;
01294   rc = openDb( myDatabaseFileName, &db );
01295   if ( rc )
01296   {
01297     return QString();
01298   }
01299   // prepare the sql statement
01300   const char *pzTail;
01301   sqlite3_stmt *ppStmt;
01302 
01303   rc = sqlite3_prepare( db, mySql.toUtf8(), mySql.toUtf8().length(), &ppStmt, &pzTail );
01304   // XXX Need to free memory from the error msg if one is set
01305 
01306   if ( rc == SQLITE_OK )
01307   {
01308     if ( sqlite3_step( ppStmt ) == SQLITE_ROW )
01309     {
01310       myProjString = QString::fromUtf8(( char* )sqlite3_column_text( ppStmt, 0 ) );
01311     }
01312   }
01313   // close the statement
01314   sqlite3_finalize( ppStmt );
01315   // close the database
01316   sqlite3_close( db );
01317 
01318   //Q_ASSERT(myProjString.length() > 0);
01319   return myProjString;
01320 }
01321 
01322 int QgsCoordinateReferenceSystem::openDb( QString path, sqlite3 **db, bool readonly )
01323 {
01324   QgsDebugMsgLevel( "path = " + path, 3 );
01325   int myResult = readonly
01326                  ? sqlite3_open_v2( path.toUtf8().data(), db, SQLITE_OPEN_READONLY, NULL )
01327                  : sqlite3_open( path.toUtf8().data(), db );
01328 
01329   if ( myResult != SQLITE_OK )
01330   {
01331     QgsDebugMsg( "Can't open database: " + QString( sqlite3_errmsg( *db ) ) );
01332     // XXX This will likely never happen since on open, sqlite creates the
01333     //     database if it does not exist.
01334     // ... unfortunately it happens on Windows
01335     QgsMessageLog::logMessage( QObject::tr( "Could not open CRS database %1\nError(%2): %3" )
01336                                .arg( path )
01337                                .arg( myResult )
01338                                .arg( sqlite3_errmsg( *db ) ), QObject::tr( "CRS" ) );
01339   }
01340   return myResult;
01341 }
01342 
01343 void QgsCoordinateReferenceSystem::setCustomSrsValidation( CUSTOM_CRS_VALIDATION f )
01344 {
01345   mCustomSrsValidation = f;
01346 }
01347 
01348 CUSTOM_CRS_VALIDATION QgsCoordinateReferenceSystem::customSrsValidation()
01349 {
01350   return mCustomSrsValidation;
01351 }
01352 
01353 void QgsCoordinateReferenceSystem::debugPrint()
01354 {
01355   QgsDebugMsg( "***SpatialRefSystem***" );
01356   QgsDebugMsg( "* Valid : " + ( mIsValidFlag ? QString( "true" ) : QString( "false" ) ) );
01357   QgsDebugMsg( "* SrsId : " + QString::number( mSrsId ) );
01358   QgsDebugMsg( "* Proj4 : " + toProj4() );
01359   QgsDebugMsg( "* WKT   : " + toWkt() );
01360   QgsDebugMsg( "* Desc. : " + mDescription );
01361   if ( mapUnits() == QGis::Meters )
01362   {
01363     QgsDebugMsg( "* Units : meters" );
01364   }
01365   else if ( mapUnits() == QGis::Feet )
01366   {
01367     QgsDebugMsg( "* Units : feet" );
01368   }
01369   else if ( mapUnits() == QGis::Degrees )
01370   {
01371     QgsDebugMsg( "* Units : degrees" );
01372   }
01373 }
01374 
01375 void QgsCoordinateReferenceSystem::setValidationHint( QString html )
01376 {
01377   mValidationHint = html;
01378 }
01379 
01380 QString QgsCoordinateReferenceSystem::validationHint()
01381 {
01382   return mValidationHint;
01383 }
01384 
01387 
01388 bool QgsCoordinateReferenceSystem::saveAsUserCRS()
01389 {
01390   if ( ! mIsValidFlag )
01391   {
01392     QgsDebugMsg( "Can't save an invalid CRS!" );
01393     return false;
01394   }
01395 
01396   QString mySql;
01397   QString myName = QString( " * %1 (%2)" )
01398                    .arg( QObject::tr( "Generated CRS", "A CRS automatically generated from layer info get this prefix for description" ) )
01399                    .arg( toProj4() );
01400 
01401   //if this is the first record we need to ensure that its srs_id is 10000. For
01402   //any rec after that sqlite3 will take care of the autonumering
01403   //this was done to support sqlite 3.0 as it does not yet support
01404   //the autoinc related system tables.
01405   if ( getRecordCount() == 0 )
01406   {
01407     mySql = "insert into tbl_srs (srs_id,description,projection_acronym,ellipsoid_acronym,parameters,is_geo) values ("
01408             + QString::number( USER_CRS_START_ID )
01409             + "," + quotedValue( myName )
01410             + "," + quotedValue( projectionAcronym() )
01411             + "," + quotedValue( ellipsoidAcronym() )
01412             + "," + quotedValue( toProj4() )
01413             + ",0)"; // <-- is_geo shamelessly hard coded for now
01414   }
01415   else
01416   {
01417     mySql = "insert into tbl_srs (description,projection_acronym,ellipsoid_acronym,parameters,is_geo) values ("
01418             + quotedValue( myName )
01419             + "," + quotedValue( projectionAcronym() )
01420             + "," + quotedValue( ellipsoidAcronym() )
01421             + "," + quotedValue( toProj4() )
01422             + ",0)"; // <-- is_geo shamelessly hard coded for now
01423   }
01424   sqlite3      *myDatabase;
01425   const char   *myTail;
01426   sqlite3_stmt *myPreparedStatement;
01427   int           myResult;
01428   //check the db is available
01429   myResult = sqlite3_open( QgsApplication::qgisUserDbFilePath().toUtf8().data(), &myDatabase );
01430   if ( myResult != SQLITE_OK )
01431   {
01432     QgsDebugMsg( QString( "Can't open or create database %1: %2" )
01433                  .arg( QgsApplication::qgisUserDbFilePath() )
01434                  .arg( sqlite3_errmsg( myDatabase ) ) );
01435     return false;
01436   }
01437   QgsDebugMsg( QString( "Update or insert sql \n%1" ).arg( mySql ) );
01438   myResult = sqlite3_prepare( myDatabase, mySql.toUtf8(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
01439   sqlite3_step( myPreparedStatement );
01440 
01441   QgsMessageLog::logMessage( QObject::tr( "Saved user CRS [%1]" ).arg( toProj4() ), QObject::tr( "CRS" ) );
01442 
01443   // XXX Need to free memory from the error msg if one is set
01444   return myResult == SQLITE_OK;
01445 }
01446 
01447 long QgsCoordinateReferenceSystem::getRecordCount()
01448 {
01449   sqlite3      *myDatabase;
01450   const char   *myTail;
01451   sqlite3_stmt *myPreparedStatement;
01452   int           myResult;
01453   long          myRecordCount = 0;
01454   //check the db is available
01455   myResult = sqlite3_open_v2( QgsApplication::qgisUserDbFilePath().toUtf8().data(), &myDatabase, SQLITE_OPEN_READONLY, NULL );
01456   if ( myResult != SQLITE_OK )
01457   {
01458     QgsDebugMsg( QString( "Can't open database: %1" ).arg( sqlite3_errmsg( myDatabase ) ) );
01459     return 0;
01460   }
01461   // Set up the query to retrieve the projection information needed to populate the ELLIPSOID list
01462   QString mySql = "select count(*) from tbl_srs";
01463   myResult = sqlite3_prepare( myDatabase, mySql.toUtf8(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
01464   // XXX Need to free memory from the error msg if one is set
01465   if ( myResult == SQLITE_OK )
01466   {
01467     if ( sqlite3_step( myPreparedStatement ) == SQLITE_ROW )
01468     {
01469       QString myRecordCountString = QString::fromUtf8(( char * )sqlite3_column_text( myPreparedStatement, 0 ) );
01470       myRecordCount = myRecordCountString.toLong();
01471     }
01472   }
01473   // close the sqlite3 statement
01474   sqlite3_finalize( myPreparedStatement );
01475   sqlite3_close( myDatabase );
01476   return myRecordCount;
01477 }
01478 
01479 QString QgsCoordinateReferenceSystem::quotedValue( QString value )
01480 {
01481   value.replace( "'", "''" );
01482   return value.prepend( "'" ).append( "'" );
01483 }
01484 
01485 int QgsCoordinateReferenceSystem::syncDb()
01486 {
01487   int updated = 0, errors = 0;
01488 
01489   sqlite3 *database;
01490   if ( sqlite3_open( QgsApplication::srsDbFilePath().toUtf8().constData(), &database ) != SQLITE_OK )
01491   {
01492     qCritical( "Could not open database: %s [%s]\n", QgsApplication::srsDbFilePath().toLocal8Bit().constData(), sqlite3_errmsg( database ) );
01493     return -1;
01494   }
01495 
01496   if ( sqlite3_exec( database, "BEGIN TRANSACTION", 0, 0, 0 ) != SQLITE_OK )
01497   {
01498     qCritical( "Could not begin transaction: %s [%s]\n", QgsApplication::srsDbFilePath().toLocal8Bit().constData(), sqlite3_errmsg( database ) );
01499     return -1;
01500   }
01501 
01502   const char *tail;
01503   sqlite3_stmt *select;
01504   QString sql = "select auth_name,auth_id,parameters from tbl_srs WHERE auth_name IS NOT NULL AND auth_id IS NOT NULL order by deprecated";
01505   if ( sqlite3_prepare( database, sql.toAscii(), sql.size(), &select, &tail ) != SQLITE_OK )
01506   {
01507     qCritical( "Could not prepare: %s [%s]\n", sql.toAscii().constData(), sqlite3_errmsg( database ) );
01508     sqlite3_close( database );
01509     return -1;
01510   }
01511 
01512   OGRSpatialReferenceH crs = OSRNewSpatialReference( NULL );
01513 
01514   while ( sqlite3_step( select ) == SQLITE_ROW )
01515   {
01516     const char *auth_name = ( const char * ) sqlite3_column_text( select, 0 );
01517     const char *auth_id   = ( const char * ) sqlite3_column_text( select, 1 );
01518     const char *params    = ( const char * ) sqlite3_column_text( select, 2 );
01519 
01520     QString proj4;
01521 
01522     if ( QString( auth_name ).compare( "epsg", Qt::CaseInsensitive ) == 0 )
01523     {
01524       OGRErr ogrErr = OSRSetFromUserInput( crs, QString( "epsg:%1" ).arg( auth_id ).toAscii() );
01525 
01526       if ( ogrErr == OGRERR_NONE )
01527       {
01528         char *output = 0;
01529 
01530         if ( OSRExportToProj4( crs, &output ) == OGRERR_NONE )
01531         {
01532           proj4 = output;
01533           proj4 = proj4.trimmed();
01534         }
01535         else
01536         {
01537           QgsDebugMsg( QString( "could not retrieve proj.4 string for epsg:%1 from OGR" ).arg( auth_id ) );
01538         }
01539 
01540         if ( output )
01541           CPLFree( output );
01542       }
01543     }
01544 #if !defined(PJ_VERSION) || PJ_VERSION!=470
01545     // 4.7.0 has a bug that crashes after 16 consecutive pj_init_plus with different strings
01546     else
01547     {
01548       QString input = QString( "+init=%1:%2" ).arg( QString( auth_name ).toLower() ).arg( auth_id );
01549       projPJ pj = pj_init_plus( input.toAscii() );
01550       if ( !pj )
01551       {
01552         input = QString( "+init=%1:%2" ).arg( QString( auth_name ).toUpper() ).arg( auth_id );
01553         pj = pj_init_plus( input.toAscii() );
01554       }
01555 
01556       if ( pj )
01557       {
01558         char *def = pj_get_def( pj, 0 );
01559         if ( def )
01560         {
01561           proj4 = def;
01562           pj_dalloc( def );
01563 
01564           input.prepend( ' ' ).append( ' ' );
01565           if ( proj4.startsWith( input ) )
01566           {
01567             proj4 = proj4.mid( input.size() );
01568             proj4 = proj4.trimmed();
01569           }
01570         }
01571         else
01572         {
01573           QgsDebugMsg( QString( "could not retrieve proj string for %1 from PROJ" ).arg( input ) );
01574         }
01575       }
01576       else
01577       {
01578         QgsDebugMsgLevel( QString( "could not retrieve crs for %1 from PROJ" ).arg( input ), 3 );
01579       }
01580 
01581       pj_free( pj );
01582     }
01583 #endif
01584 
01585     if ( proj4.isEmpty() )
01586     {
01587       continue;
01588     }
01589 
01590     if ( proj4 != params )
01591     {
01592       char *errMsg = NULL;
01593       sql = QString( "UPDATE tbl_srs SET parameters=%1 WHERE auth_name=%2 AND auth_id=%3" )
01594             .arg( quotedValue( proj4 ) )
01595             .arg( quotedValue( auth_name ) )
01596             .arg( quotedValue( auth_id ) );
01597 
01598       if ( sqlite3_exec( database, sql.toUtf8(), 0, 0, &errMsg ) != SQLITE_OK )
01599       {
01600         qCritical( "Could not execute: %s [%s/%s]\n",
01601                    sql.toLocal8Bit().constData(),
01602                    sqlite3_errmsg( database ),
01603                    errMsg ? errMsg : "(unknown error)" );
01604         errors++;
01605       }
01606       else
01607       {
01608         updated++;
01609         QgsDebugMsgLevel( QString( "SQL: %1\n OLD:%2\n NEW:%3" ).arg( sql ).arg( params ).arg( proj4 ), 3 );
01610       }
01611 
01612       if ( errMsg )
01613         sqlite3_free( errMsg );
01614     }
01615   }
01616 
01617   OSRDestroySpatialReference( crs );
01618 
01619   sqlite3_finalize( select );
01620 
01621   if ( sqlite3_exec( database, "COMMIT", 0, 0, 0 ) != SQLITE_OK )
01622   {
01623     qCritical( "Could not commit transaction: %s [%s]\n", QgsApplication::srsDbFilePath().toLocal8Bit().constData(), sqlite3_errmsg( database ) );
01624     return -1;
01625   }
01626 
01627   sqlite3_close( database );
01628 
01629   if ( errors > 0 )
01630     return -errors;
01631   else
01632     return updated;
01633 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines