Quantum GIS API Documentation
1.8
|
00001 /*************************************************************************** 00002 qgscoordinatereferencesystem.cpp 00003 00004 ------------------- 00005 begin : 2007 00006 copyright : (C) 2007 by Gary E. Sherman 00007 email : [email protected] 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 }