|
QGIS API Documentation
master-59fd5e0
|
00001 /*************************************************************************** 00002 * qgsprojectionselector.cpp * 00003 * Copyright (C) 2005 by Tim Sutton * 00004 * tim@linfiniti.com * 00005 * * 00006 * This program is free software; you can redistribute it and/or modify * 00007 * it under the terms of the GNU General Public License as published by * 00008 * the Free Software Foundation; either version 2 of the License, or * 00009 * (at your option) any later version. * 00010 ***************************************************************************/ 00011 #include <qgsprojectionselector.h> 00012 00013 //standard includes 00014 #include <sqlite3.h> 00015 00016 //qgis includes 00017 #include "qgis.h" //magic numbers here 00018 #include "qgsapplication.h" 00019 #include "qgslogger.h" 00020 #include "qgscoordinatereferencesystem.h" 00021 00022 //qt includes 00023 #include <QFileInfo> 00024 #include <QHeaderView> 00025 #include <QResizeEvent> 00026 #include <QMessageBox> 00027 #include <QSettings> 00028 00029 QgsProjectionSelector::QgsProjectionSelector( QWidget* parent, const char *name, Qt::WFlags fl ) 00030 : QWidget( parent, fl ) 00031 , mProjListDone( false ) 00032 , mUserProjListDone( false ) 00033 , mRecentProjListDone( false ) 00034 , mSearchColumn( NONE ) 00035 , mSkipFirstRecent( true ) 00036 { 00037 Q_UNUSED( name ); 00038 setupUi( this ); 00039 00040 // Get the full path name to the sqlite3 spatial reference database. 00041 mSrsDatabaseFileName = QgsApplication::srsDbFilePath(); 00042 00043 lstCoordinateSystems->header()->setResizeMode( AUTHID_COLUMN, QHeaderView::Stretch ); 00044 lstCoordinateSystems->header()->resizeSection( QGIS_CRS_ID_COLUMN, 0 ); 00045 lstCoordinateSystems->header()->setResizeMode( QGIS_CRS_ID_COLUMN, QHeaderView::Fixed ); 00046 00047 // Hide (internal) ID column 00048 lstCoordinateSystems->setColumnHidden( QGIS_CRS_ID_COLUMN, true ); 00049 00050 lstRecent->header()->setResizeMode( AUTHID_COLUMN, QHeaderView::Stretch ); 00051 lstRecent->header()->resizeSection( QGIS_CRS_ID_COLUMN, 0 ); 00052 lstRecent->header()->setResizeMode( QGIS_CRS_ID_COLUMN, QHeaderView::Fixed ); 00053 00054 // Hide (internal) ID column 00055 lstRecent->setColumnHidden( QGIS_CRS_ID_COLUMN, true ); 00056 00057 // Read settings from persistent storage 00058 QSettings settings; 00059 mRecentProjections = settings.value( "/UI/recentProjections" ).toStringList(); 00060 /*** The reading (above) of internal id from persistent storage should be removed sometime in the future */ 00061 /*** This is kept now for backwards compatibility */ 00062 00063 QStringList projectionsProj4 = settings.value( "/UI/recentProjectionsProj4" ).toStringList(); 00064 QStringList projectionsAuthId = settings.value( "/UI/recentProjectionsAuthId" ).toStringList(); 00065 if ( projectionsAuthId.size() >= mRecentProjections.size() ) 00066 { 00067 // We had saved state with AuthId and Proj4. Use that instead 00068 // to find out the crs id 00069 QgsDebugMsg( "Use popular projection list from AuthId/Proj4 saved state" ); 00070 mRecentProjections.clear(); 00071 for ( int i = 0; i < projectionsAuthId.size(); i++ ) 00072 { 00073 // Create a crs from the EPSG 00074 QgsCoordinateReferenceSystem crs; 00075 crs.createFromOgcWmsCrs( projectionsAuthId.at( i ) ); 00076 if ( ! crs.isValid() ) 00077 { 00078 // Couldn't create from EPSG, try the Proj4 string instead 00079 if ( ! crs.createFromProj4( projectionsProj4.at( i ) ) ) 00080 { 00081 // No? Skip this entry 00082 continue; 00083 } 00084 //If the CRS can be created but do not correspond to a CRS in the database, skip it (for example a deleted custom CRS) 00085 if ( crs.srsid() == 0 ) 00086 { 00087 continue; 00088 } 00089 } 00090 mRecentProjections << QString::number( crs.srsid() ); 00091 } 00092 } 00093 } 00094 00095 QgsProjectionSelector::~QgsProjectionSelector() 00096 { 00097 // Push current projection to front, only if set 00098 long crsId = selectedCrsId(); 00099 if ( crsId == 0 ) 00100 return; 00101 00102 // Save persistent list of projects 00103 mRecentProjections.removeAll( QString::number( crsId ) ); 00104 mRecentProjections.prepend( QString::number( crsId ) ); 00105 // Prune size of list 00106 while ( mRecentProjections.size() > 8 ) 00107 { 00108 mRecentProjections.removeLast(); 00109 } 00110 00111 // Save to file *** Should be removed sometims in the future *** 00112 QSettings settings; 00113 settings.setValue( "/UI/recentProjections", mRecentProjections ); 00114 00115 // Convert to EPSG and proj4, and save those values also 00116 00117 QStringList projectionsProj4; 00118 QStringList projectionsAuthId; 00119 for ( int i = 0; i < mRecentProjections.size(); i++ ) 00120 { 00121 // Create a crs from the crsId 00122 QgsCoordinateReferenceSystem crs( mRecentProjections.at( i ).toLong(), QgsCoordinateReferenceSystem::InternalCrsId ); 00123 if ( ! crs.isValid() ) 00124 { 00125 // No? Skip this entry 00126 continue; 00127 } 00128 projectionsProj4 << crs.toProj4(); 00129 projectionsAuthId << crs.authid(); 00130 } 00131 settings.setValue( "/UI/recentProjectionsProj4", projectionsProj4 ); 00132 settings.setValue( "/UI/recentProjectionsAuthId", projectionsAuthId ); 00133 } 00134 00135 void QgsProjectionSelector::resizeEvent( QResizeEvent * theEvent ) 00136 { 00137 lstCoordinateSystems->header()->resizeSection( NAME_COLUMN, theEvent->size().width() - 240 ); 00138 lstCoordinateSystems->header()->resizeSection( AUTHID_COLUMN, 240 ); 00139 lstCoordinateSystems->header()->resizeSection( QGIS_CRS_ID_COLUMN, 0 ); 00140 00141 lstRecent->header()->resizeSection( NAME_COLUMN, theEvent->size().width() - 240 ); 00142 lstRecent->header()->resizeSection( AUTHID_COLUMN, 240 ); 00143 lstRecent->header()->resizeSection( QGIS_CRS_ID_COLUMN, 0 ); 00144 } 00145 00146 void QgsProjectionSelector::showEvent( QShowEvent * theEvent ) 00147 { 00148 // ensure the projection list view is actually populated 00149 // before we show this widget 00150 loadCrsList( &mCrsFilter ); 00151 loadUserCrsList( &mCrsFilter ); 00152 00153 if ( !mRecentProjListDone ) 00154 { 00155 for ( int i = mRecentProjections.size() - 1; i >= 0; i-- ) 00156 insertRecent( mRecentProjections.at( i ).toLong() ); 00157 mRecentProjListDone = true; 00158 } 00159 00160 // apply deferred selection 00161 applySelection(); 00162 00163 // Pass up the inheritance hierarchy 00164 QWidget::showEvent( theEvent ); 00165 } 00166 00167 QString QgsProjectionSelector::ogcWmsCrsFilterAsSqlExpression( QSet<QString> * crsFilter ) 00168 { 00169 QString sqlExpression = "1"; // it's "SQL" for "true" 00170 QMap<QString, QStringList> authParts; 00171 00172 if ( !crsFilter ) 00173 return sqlExpression; 00174 00175 /* 00176 Ref: WMS 1.3.0, section 6.7.3 "Layer CRS": 00177 00178 Every Layer CRS has an identifier that is a character string. Two types of 00179 Layer CRS identifiers are permitted: "label" and "URL" identifiers: 00180 00181 Label: The identifier includes a namespace prefix, a colon, a numeric or 00182 string code, and in some instances a comma followed by additional 00183 parameters. This International Standard defines three namespaces: 00184 CRS, EpsgCrsId and AUTO2 [...] 00185 00186 URL: The identifier is a fully-qualified Uniform Resource Locator that 00187 references a publicly-accessible file containing a definition of the CRS 00188 that is compliant with ISO 19111. 00189 */ 00190 00191 // iterate through all incoming CRSs 00192 00193 foreach ( QString auth_id, crsFilter->values() ) 00194 { 00195 QStringList parts = auth_id.split( ":" ); 00196 00197 if ( parts.size() < 2 ) 00198 continue; 00199 00200 authParts[ parts.at( 0 ).toUpper()].append( parts.at( 1 ).toUpper() ); 00201 } 00202 00203 if ( authParts.isEmpty() ) 00204 return sqlExpression; 00205 00206 if ( authParts.size() > 0 ) 00207 { 00208 QString prefix = " AND ("; 00209 foreach ( QString auth_name, authParts.keys() ) 00210 { 00211 sqlExpression += QString( "%1(upper(auth_name)='%2' AND upper(auth_id) IN ('%3'))" ) 00212 .arg( prefix ) 00213 .arg( auth_name ) 00214 .arg( authParts[auth_name].join( "','" ) ); 00215 prefix = " OR "; 00216 } 00217 sqlExpression += ")"; 00218 } 00219 00220 QgsDebugMsg( "exiting with '" + sqlExpression + "'." ); 00221 00222 return sqlExpression; 00223 } 00224 00225 void QgsProjectionSelector::setSelectedCrsName( QString theCRSName ) 00226 { 00227 applySelection( NAME_COLUMN, theCRSName ); 00228 } 00229 00230 void QgsProjectionSelector::setSelectedCrsId( long theCRSID ) 00231 { 00232 applySelection( QGIS_CRS_ID_COLUMN, QString::number( theCRSID ) ); 00233 } 00234 00235 void QgsProjectionSelector::setSelectedAuthId( QString id ) 00236 { 00237 applySelection( AUTHID_COLUMN, id ); 00238 } 00239 00240 void QgsProjectionSelector::applySelection( int column, QString value ) 00241 { 00242 if ( !mProjListDone || !mUserProjListDone ) 00243 { 00244 // defer selection until loaded 00245 mSearchColumn = column; 00246 mSearchValue = value; 00247 return; 00248 } 00249 00250 if ( column == NONE ) 00251 { 00252 // invoked deferred selection 00253 column = mSearchColumn; 00254 value = mSearchValue; 00255 00256 mSearchColumn = NONE; 00257 mSearchValue.clear(); 00258 } 00259 00260 if ( column == NONE ) 00261 return; 00262 00263 QList<QTreeWidgetItem*> nodes = lstCoordinateSystems->findItems( value, Qt::MatchExactly | Qt::MatchRecursive, column ); 00264 if ( nodes.count() > 0 ) 00265 { 00266 QgsDebugMsg( QString( "found %1,%2" ).arg( column ).arg( value ) ); 00267 lstCoordinateSystems->setCurrentItem( nodes.first() ); 00268 } 00269 else 00270 { 00271 QgsDebugMsg( QString( "nothing found for %1,%2" ).arg( column ).arg( value ) ); 00272 // unselect the selected item to avoid confusing the user 00273 lstCoordinateSystems->clearSelection(); 00274 lstRecent->clearSelection(); 00275 teProjection->setText( "" ); 00276 teSelected->setText( "" ); 00277 } 00278 } 00279 00280 void QgsProjectionSelector::insertRecent( long theCrsId ) 00281 { 00282 if ( !mProjListDone || !mUserProjListDone ) 00283 return; 00284 00285 QList<QTreeWidgetItem*> nodes = lstCoordinateSystems->findItems( QString::number( theCrsId ), Qt::MatchExactly | Qt::MatchRecursive, QGIS_CRS_ID_COLUMN ); 00286 if ( nodes.count() == 0 ) 00287 return; 00288 00289 lstRecent->insertTopLevelItem( 0, new QTreeWidgetItem( lstRecent, QStringList() 00290 << nodes.first()->text( NAME_COLUMN ) 00291 << nodes.first()->text( AUTHID_COLUMN ) 00292 << nodes.first()->text( QGIS_CRS_ID_COLUMN ) ) ); 00293 } 00294 00295 //note this line just returns the projection name! 00296 QString QgsProjectionSelector::selectedName() 00297 { 00298 // return the selected wkt name from the list view 00299 QTreeWidgetItem *lvi = lstCoordinateSystems->currentItem(); 00300 return lvi ? lvi->text( NAME_COLUMN ) : QString::null; 00301 } 00302 00303 // Returns the whole proj4 string for the selected projection node 00304 QString QgsProjectionSelector::selectedProj4String() 00305 { 00306 // Only return the projection if there is a node in the tree 00307 // selected that has an srid. This prevents error if the user 00308 // selects a top-level node rather than an actual coordinate 00309 // system 00310 // 00311 // Get the selected node 00312 QTreeWidgetItem *item = lstCoordinateSystems->currentItem(); 00313 if ( !item || item->text( QGIS_CRS_ID_COLUMN ).isEmpty() ) 00314 return ""; 00315 00316 QString srsId = item->text( QGIS_CRS_ID_COLUMN ); 00317 00318 QgsDebugMsg( "srsId = " + srsId ); 00319 QgsDebugMsg( "USER_CRS_START_ID = " + QString::number( USER_CRS_START_ID ) ); 00320 00321 // 00322 // Determine if this is a user projection or a system on 00323 // user projection defs all have srs_id >= 100000 00324 // 00325 QString databaseFileName; 00326 if ( srsId.toLong() >= USER_CRS_START_ID ) 00327 { 00328 databaseFileName = QgsApplication::qgisUserDbFilePath(); 00329 if ( !QFileInfo( databaseFileName ).exists() ) //its unlikely that this condition will ever be reached 00330 return QString( "" ); 00331 } 00332 else //must be a system projection then 00333 { 00334 databaseFileName = mSrsDatabaseFileName; 00335 } 00336 00337 QgsDebugMsg( "db = " + databaseFileName ); 00338 00339 sqlite3 *database; 00340 int rc = sqlite3_open_v2( databaseFileName.toUtf8().data(), &database, SQLITE_OPEN_READONLY, NULL ); 00341 if ( rc ) 00342 { 00343 showDBMissingWarning( databaseFileName ); 00344 return ""; 00345 } 00346 00347 // prepare the sql statement 00348 const char *tail; 00349 sqlite3_stmt *stmt; 00350 QString sql = QString( "select parameters from tbl_srs where srs_id=%1" ).arg( srsId ); 00351 00352 QgsDebugMsg( "Selection sql: " + sql ); 00353 00354 rc = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail ); 00355 // XXX Need to free memory from the error msg if one is set 00356 QString projString; 00357 if ( rc == SQLITE_OK && sqlite3_step( stmt ) == SQLITE_ROW ) 00358 { 00359 projString = QString::fromUtf8(( char * )sqlite3_column_text( stmt, 0 ) ); 00360 } 00361 00362 // close the statement 00363 sqlite3_finalize( stmt ); 00364 // close the database 00365 sqlite3_close( database ); 00366 00367 Q_ASSERT( !projString.isEmpty() ); 00368 00369 return projString; 00370 } 00371 00372 QString QgsProjectionSelector::getSelectedExpression( QString expression ) 00373 { 00374 // Only return the attribute if there is a node in the tree 00375 // selected that has an srs_id. This prevents error if the user 00376 // selects a top-level node rather than an actual coordinate 00377 // system 00378 // 00379 // Get the selected node and make sure it is a srs andx 00380 // not a top-level projection node 00381 QTreeWidgetItem *lvi = lstCoordinateSystems->currentItem(); 00382 if ( !lvi || lvi->text( QGIS_CRS_ID_COLUMN ).isEmpty() ) 00383 return 0; 00384 00385 // 00386 // Determine if this is a user projection or a system on 00387 // user projection defs all have srs_id >= 100000 00388 // 00389 QString databaseFileName; 00390 if ( lvi->text( QGIS_CRS_ID_COLUMN ).toLong() >= USER_CRS_START_ID ) 00391 { 00392 databaseFileName = QgsApplication::qgisUserDbFilePath(); 00393 if ( !QFileInfo( databaseFileName ).exists() ) 00394 { 00395 return 0; 00396 } 00397 } 00398 else 00399 { 00400 databaseFileName = mSrsDatabaseFileName; 00401 } 00402 00403 // 00404 // set up the database 00405 // XXX We could probabaly hold the database open for the life of this object, 00406 // assuming that it will never be used anywhere else. Given the low overhead, 00407 // opening it each time seems to be a reasonable approach at this time. 00408 sqlite3 *database; 00409 int rc = sqlite3_open_v2( databaseFileName.toUtf8().data(), &database, SQLITE_OPEN_READONLY, NULL ); 00410 if ( rc ) 00411 { 00412 showDBMissingWarning( databaseFileName ); 00413 return 0; 00414 } 00415 00416 // prepare the sql statement 00417 const char *tail; 00418 sqlite3_stmt *stmt; 00419 QString sql = QString( "select %1 from tbl_srs where srs_id=%2" ) 00420 .arg( expression ) 00421 .arg( lvi->text( QGIS_CRS_ID_COLUMN ) ); 00422 00423 QgsDebugMsg( QString( "Finding selected attribute using : %1" ).arg( sql ) ); 00424 rc = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail ); 00425 // XXX Need to free memory from the error msg if one is set 00426 QString attributeValue; 00427 if ( rc == SQLITE_OK && sqlite3_step( stmt ) == SQLITE_ROW ) 00428 { 00429 // get the first row of the result set 00430 attributeValue = QString::fromUtf8(( char * )sqlite3_column_text( stmt, 0 ) ); 00431 } 00432 00433 // close the statement 00434 sqlite3_finalize( stmt ); 00435 // close the database 00436 sqlite3_close( database ); 00437 00438 // return the srs 00439 return attributeValue; 00440 } 00441 00442 00443 long QgsProjectionSelector::selectedPostgresSrId() 00444 { 00445 return getSelectedExpression( "srid" ).toLong(); 00446 } 00447 00448 00449 QString QgsProjectionSelector::selectedAuthId() 00450 { 00451 int srid = getSelectedExpression( "srs_id" ).toLong(); 00452 if ( srid >= USER_CRS_START_ID ) 00453 return QString( "USER:%1" ).arg( srid ); 00454 else 00455 return getSelectedExpression( "upper(auth_name||':'||auth_id)" ); 00456 } 00457 00458 00459 long QgsProjectionSelector::selectedCrsId() 00460 { 00461 QTreeWidgetItem* item = lstCoordinateSystems->currentItem(); 00462 00463 if ( item && !item->text( QGIS_CRS_ID_COLUMN ).isEmpty() ) 00464 return lstCoordinateSystems->currentItem()->text( QGIS_CRS_ID_COLUMN ).toLong(); 00465 else 00466 return 0; 00467 } 00468 00469 00470 void QgsProjectionSelector::setOgcWmsCrsFilter( QSet<QString> crsFilter ) 00471 { 00472 mCrsFilter = crsFilter; 00473 mProjListDone = false; 00474 mUserProjListDone = false; 00475 lstCoordinateSystems->clear(); 00476 } 00477 00478 void QgsProjectionSelector::loadUserCrsList( QSet<QString> *crsFilter ) 00479 { 00480 if ( mUserProjListDone ) 00481 return; 00482 00483 QgsDebugMsg( "Fetching user projection list..." ); 00484 00485 // convert our Coordinate Reference System filter into the SQL expression 00486 QString sqlFilter = ogcWmsCrsFilterAsSqlExpression( crsFilter ); 00487 00488 // User defined coordinate system node 00489 // Make in an italic font to distinguish them from real projections 00490 mUserProjList = new QTreeWidgetItem( lstCoordinateSystems, QStringList( tr( "User Defined Coordinate Systems" ) ) ); 00491 00492 QFont fontTemp = mUserProjList->font( 0 ); 00493 fontTemp.setItalic( true ); 00494 fontTemp.setBold( true ); 00495 mUserProjList->setFont( 0, fontTemp ); 00496 mUserProjList->setIcon( 0, QIcon( QgsApplication::activeThemePath() + "user.png" ) ); 00497 00498 //determine where the user proj database lives for this user. If none is found an empty 00499 //now only will be shown 00500 QString databaseFileName = QgsApplication::qgisUserDbFilePath(); 00501 // first we look for ~/.qgis/qgis.db 00502 // if it doesnt exist we copy it in from the global resources dir 00503 00504 //return straight away if the user has not created any custom projections 00505 if ( !QFileInfo( databaseFileName ).exists( ) ) 00506 { 00507 QgsDebugMsg( "Users qgis.db not found...skipping" ); 00508 mUserProjListDone = true; 00509 return; 00510 } 00511 00512 sqlite3 *database; 00513 const char *tail; 00514 sqlite3_stmt *stmt; 00515 //check the db is available 00516 int result = sqlite3_open_v2( databaseFileName.toUtf8().constData(), &database, SQLITE_OPEN_READONLY, NULL ); 00517 if ( result ) 00518 { 00519 // XXX This will likely never happen since on open, sqlite creates the 00520 // database if it does not exist. But we checked earlier for its existance 00521 // and aborted in that case. This is because we may be runnig from read only 00522 // media such as live cd and don't want to force trying to create a db. 00523 showDBMissingWarning( databaseFileName ); 00524 return; 00525 } 00526 00527 // Set up the query to retrieve the projection information needed to populate the list 00528 QString sql = QString( "select description, srs_id from vw_srs where %1" ).arg( sqlFilter ); 00529 00530 result = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail ); 00531 // XXX Need to free memory from the error msg if one is set 00532 if ( result == SQLITE_OK ) 00533 { 00534 QTreeWidgetItem *newItem; 00535 while ( sqlite3_step( stmt ) == SQLITE_ROW ) 00536 { 00537 newItem = new QTreeWidgetItem( mUserProjList, QStringList( QString::fromUtf8(( char * )sqlite3_column_text( stmt, 0 ) ) ) ); 00538 // EpsgCrsId for user projections is not always defined in some dbases. 00539 // It's also not written from customprojections dialog. 00540 // display the epsg (field 2) in the second column of the list view 00541 // newItem->setText( EPSG_COLUMN, QString::fromUtf8(( char * )sqlite3_column_text( stmt, 2 ) ) ); 00542 // display the qgis srs_id (field 1) in the third column of the list view 00543 newItem->setText( QGIS_CRS_ID_COLUMN, QString::fromUtf8(( char * )sqlite3_column_text( stmt, 1 ) ) ); 00544 newItem->setText( AUTHID_COLUMN, QString( "USER:%1" ).arg( QString::fromUtf8(( char * )sqlite3_column_text( stmt, 1 ) ).toInt() ) ); 00545 } 00546 } 00547 // close the sqlite3 statement 00548 sqlite3_finalize( stmt ); 00549 sqlite3_close( database ); 00550 00551 mUserProjListDone = true; 00552 } 00553 00554 void QgsProjectionSelector::loadCrsList( QSet<QString> *crsFilter ) 00555 { 00556 if ( mProjListDone ) 00557 return; 00558 00559 // convert our Coordinate Reference System filter into the SQL expression 00560 QString sqlFilter = ogcWmsCrsFilterAsSqlExpression( crsFilter ); 00561 00562 // Create the top-level nodes for the list view of projections 00563 // Make in an italic font to distinguish them from real projections 00564 // 00565 // Geographic coordinate system node 00566 mGeoList = new QTreeWidgetItem( lstCoordinateSystems, QStringList( tr( "Geographic Coordinate Systems" ) ) ); 00567 00568 QFont fontTemp = mGeoList->font( 0 ); 00569 fontTemp.setItalic( true ); 00570 fontTemp.setBold( true ); 00571 mGeoList->setFont( 0, fontTemp ); 00572 mGeoList->setIcon( 0, QIcon( QgsApplication::activeThemePath() + "geographic.png" ) ); 00573 00574 // Projected coordinate system node 00575 mProjList = new QTreeWidgetItem( lstCoordinateSystems, QStringList( tr( "Projected Coordinate Systems" ) ) ); 00576 00577 fontTemp = mProjList->font( 0 ); 00578 fontTemp.setItalic( true ); 00579 fontTemp.setBold( true ); 00580 mProjList->setFont( 0, fontTemp ); 00581 mProjList->setIcon( 0, QIcon( QgsApplication::activeThemePath() + "transformed.png" ) ); 00582 00583 //bail out in case the projections db does not exist 00584 //this is necessary in case the pc is running linux with a 00585 //read only filesystem because otherwise sqlite will try 00586 //to create the db file on the fly 00587 00588 if ( !QFileInfo( mSrsDatabaseFileName ).exists() ) 00589 { 00590 mProjListDone = true; 00591 return; 00592 } 00593 00594 // open the database containing the spatial reference data 00595 sqlite3 *database; 00596 int rc = sqlite3_open_v2( mSrsDatabaseFileName.toUtf8().data(), &database, SQLITE_OPEN_READONLY, NULL ); 00597 if ( rc ) 00598 { 00599 // XXX This will likely never happen since on open, sqlite creates the 00600 // database if it does not exist. 00601 showDBMissingWarning( mSrsDatabaseFileName ); 00602 return; 00603 } 00604 // prepare the sql statement 00605 const char *tail; 00606 sqlite3_stmt *stmt; 00607 // get total count of records in the projection table 00608 QString sql = "select count(*) from tbl_srs"; 00609 00610 rc = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail ); 00611 Q_ASSERT( rc == SQLITE_OK ); 00612 sqlite3_step( stmt ); 00613 sqlite3_finalize( stmt ); 00614 00615 // Set up the query to retrieve the projection information needed to populate the list 00616 //note I am giving the full field names for clarity here and in case someone 00617 //changes the underlying view TS 00618 sql = QString( "select description, srs_id, upper(auth_name||':'||auth_id), is_geo, name, parameters, deprecated from vw_srs where %1 order by name,description" ) 00619 .arg( sqlFilter ); 00620 00621 rc = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail ); 00622 // XXX Need to free memory from the error msg if one is set 00623 if ( rc == SQLITE_OK ) 00624 { 00625 QTreeWidgetItem *newItem; 00626 // Cache some stuff to speed up creating of the list of projected 00627 // spatial reference systems 00628 QString previousSrsType( "" ); 00629 QTreeWidgetItem* previousSrsTypeNode = 0; 00630 00631 while ( sqlite3_step( stmt ) == SQLITE_ROW ) 00632 { 00633 // check to see if the srs is geographic 00634 int isGeo = sqlite3_column_int( stmt, 3 ); 00635 if ( isGeo ) 00636 { 00637 // this is a geographic coordinate system 00638 // Add it to the tree (field 0) 00639 newItem = new QTreeWidgetItem( mGeoList, QStringList( QString::fromUtf8(( char * )sqlite3_column_text( stmt, 0 ) ) ) ); 00640 00641 // display the authority name (field 2) in the second column of the list view 00642 newItem->setText( AUTHID_COLUMN, QString::fromUtf8(( char * )sqlite3_column_text( stmt, 2 ) ) ); 00643 00644 // display the qgis srs_id (field 1) in the third column of the list view 00645 newItem->setText( QGIS_CRS_ID_COLUMN, QString::fromUtf8(( char * )sqlite3_column_text( stmt, 1 ) ) ); 00646 } 00647 else 00648 { 00649 // This is a projected srs 00650 QTreeWidgetItem *node; 00651 QString srsType = QString::fromUtf8(( char* )sqlite3_column_text( stmt, 4 ) ); 00652 // Find the node for this type and add the projection to it 00653 // If the node doesn't exist, create it 00654 if ( srsType == previousSrsType ) 00655 { 00656 node = previousSrsTypeNode; 00657 } 00658 else 00659 { // Different from last one, need to search 00660 QList<QTreeWidgetItem*> nodes = lstCoordinateSystems->findItems( srsType, Qt::MatchExactly | Qt::MatchRecursive, NAME_COLUMN ); 00661 if ( nodes.count() == 0 ) 00662 { 00663 // the node doesn't exist -- create it 00664 // Make in an italic font to distinguish them from real projections 00665 node = new QTreeWidgetItem( mProjList, QStringList( srsType ) ); 00666 QFont fontTemp = node->font( 0 ); 00667 fontTemp.setItalic( true ); 00668 node->setFont( 0, fontTemp ); 00669 } 00670 else 00671 { 00672 node = nodes.first(); 00673 } 00674 // Update the cache. 00675 previousSrsType = srsType; 00676 previousSrsTypeNode = node; 00677 } 00678 // add the item, setting the projection name in the first column of the list view 00679 newItem = new QTreeWidgetItem( node, QStringList( QString::fromUtf8(( char * )sqlite3_column_text( stmt, 0 ) ) ) ); 00680 // display the authority id (field 2) in the second column of the list view 00681 newItem->setText( AUTHID_COLUMN, QString::fromUtf8(( char * )sqlite3_column_text( stmt, 2 ) ) ); 00682 // display the qgis srs_id (field 1) in the third column of the list view 00683 newItem->setText( QGIS_CRS_ID_COLUMN, QString::fromUtf8(( char * )sqlite3_column_text( stmt, 1 ) ) ); 00684 // expand also parent node 00685 newItem->parent()->setExpanded( true ); 00686 } 00687 00688 // display the qgis deprecated in the user data of the item 00689 newItem->setData( 0, Qt::UserRole, QString::fromUtf8(( char * )sqlite3_column_text( stmt, 6 ) ) ); 00690 newItem->setHidden( cbxHideDeprecated->isChecked() ); 00691 } 00692 mProjList->setExpanded( true ); 00693 } 00694 00695 // close the sqlite3 statement 00696 sqlite3_finalize( stmt ); 00697 // close the database 00698 sqlite3_close( database ); 00699 00700 mProjListDone = true; 00701 } 00702 00703 // New coordinate system selected from the list 00704 void QgsProjectionSelector::on_lstCoordinateSystems_currentItemChanged( QTreeWidgetItem *current, QTreeWidgetItem * ) 00705 { 00706 QgsDebugMsg( "Entered." ); 00707 00708 if ( !current ) 00709 { 00710 QgsDebugMsg( "no current item" ); 00711 return; 00712 } 00713 00714 lstCoordinateSystems->scrollToItem( current ); 00715 00716 // If the item has children, it's not an end node in the tree, and 00717 // hence is just a grouping thingy, not an actual CRS. 00718 if ( current->childCount() == 0 ) 00719 { 00720 // Found a real CRS 00721 emit sridSelected( QString::number( selectedCrsId() ) ); 00722 00723 teProjection->setText( selectedProj4String() ); 00724 teSelected->setText( selectedName() ); 00725 00726 QList<QTreeWidgetItem*> nodes = lstRecent->findItems( current->text( QGIS_CRS_ID_COLUMN ), Qt::MatchExactly, QGIS_CRS_ID_COLUMN ); 00727 if ( nodes.count() > 0 ) 00728 { 00729 QgsDebugMsg( QString( "found srs %1 in recent" ).arg( current->text( QGIS_CRS_ID_COLUMN ) ) ); 00730 lstRecent->setCurrentItem( nodes.first() ); 00731 } 00732 else 00733 { 00734 QgsDebugMsg( QString( "srs %1 not recent" ).arg( current->text( QGIS_CRS_ID_COLUMN ) ) ); 00735 lstRecent->clearSelection(); 00736 lstCoordinateSystems->setFocus( Qt::OtherFocusReason ); 00737 } 00738 } 00739 else 00740 { 00741 // Not an CRS - remove the highlight so the user doesn't get too confused 00742 current->setSelected( false ); 00743 teProjection->setText( "" ); 00744 teSelected->setText( "" ); 00745 lstRecent->clearSelection(); 00746 } 00747 } 00748 00749 void QgsProjectionSelector::on_lstRecent_currentItemChanged( QTreeWidgetItem *current, QTreeWidgetItem * ) 00750 { 00751 QgsDebugMsg( "Entered." ); 00752 00753 if ( mSkipFirstRecent ) 00754 { 00755 mSkipFirstRecent = false; 00756 return; 00757 } 00758 00759 if ( !current ) 00760 { 00761 QgsDebugMsg( "no current item" ); 00762 return; 00763 } 00764 00765 lstRecent->scrollToItem( current ); 00766 00767 QList<QTreeWidgetItem*> nodes = lstCoordinateSystems->findItems( current->text( QGIS_CRS_ID_COLUMN ), Qt::MatchExactly | Qt::MatchRecursive, QGIS_CRS_ID_COLUMN ); 00768 if ( nodes.count() > 0 ) 00769 lstCoordinateSystems->setCurrentItem( nodes.first() ); 00770 } 00771 00772 void QgsProjectionSelector::hideDeprecated( QTreeWidgetItem *item ) 00773 { 00774 if ( item->data( 0, Qt::UserRole ).toBool() ) 00775 { 00776 item->setHidden( cbxHideDeprecated->isChecked() ); 00777 if ( item->isSelected() && item->isHidden() ) 00778 { 00779 item->setSelected( false ); 00780 teProjection->setText( "" ); 00781 teSelected->setText( "" ); 00782 } 00783 } 00784 00785 for ( int i = 0; i < item->childCount(); i++ ) 00786 hideDeprecated( item->child( i ) ); 00787 } 00788 00789 void QgsProjectionSelector::on_cbxHideDeprecated_stateChanged() 00790 { 00791 for ( int i = 0; i < lstCoordinateSystems->topLevelItemCount(); i++ ) 00792 hideDeprecated( lstCoordinateSystems->topLevelItem( i ) ); 00793 } 00794 00795 void QgsProjectionSelector::on_leSearch_textChanged( const QString & theFilterTxt ) 00796 { 00797 QString filterTxt = theFilterTxt; 00798 filterTxt.replace( QRegExp( "\\s+" ), ".*" ); 00799 QRegExp re( filterTxt, Qt::CaseInsensitive ); 00800 00801 // filter recent crs's 00802 QTreeWidgetItemIterator itr( lstRecent ); 00803 while ( *itr ) 00804 { 00805 if (( *itr )->childCount() == 0 ) // it's an end node aka a projection 00806 { 00807 if (( *itr )->text( NAME_COLUMN ).contains( re ) 00808 || ( *itr )->text( AUTHID_COLUMN ).contains( re ) 00809 ) 00810 { 00811 ( *itr )->setHidden( false ); 00812 QTreeWidgetItem * parent = ( *itr )->parent(); 00813 while ( parent ) 00814 { 00815 parent->setExpanded( true ); 00816 parent->setHidden( false ); 00817 parent = parent->parent(); 00818 } 00819 } 00820 else 00821 { 00822 ( *itr )->setHidden( true ); 00823 } 00824 } 00825 else 00826 { 00827 ( *itr )->setHidden( true ); 00828 } 00829 ++itr; 00830 } 00831 00832 // filter crs's 00833 QTreeWidgetItemIterator it( lstCoordinateSystems ); 00834 while ( *it ) 00835 { 00836 if (( *it )->childCount() == 0 ) // it's an end node aka a projection 00837 { 00838 if (( *it )->text( NAME_COLUMN ).contains( re ) 00839 || ( *it )->text( AUTHID_COLUMN ).contains( re ) 00840 ) 00841 { 00842 ( *it )->setHidden( false ); 00843 QTreeWidgetItem * parent = ( *it )->parent(); 00844 while ( parent ) 00845 { 00846 parent->setExpanded( true ); 00847 parent->setHidden( false ); 00848 parent = parent->parent(); 00849 } 00850 } 00851 else 00852 { 00853 ( *it )->setHidden( true ); 00854 } 00855 } 00856 else 00857 { 00858 ( *it )->setHidden( true ); 00859 } 00860 ++it; 00861 } 00862 } 00863 00864 00865 long QgsProjectionSelector::getLargestCRSIDMatch( QString theSql ) 00866 { 00867 long srsId = 0; 00868 00869 // 00870 // Now perform the actual search 00871 // 00872 00873 sqlite3 *database; 00874 const char *tail; 00875 sqlite3_stmt *stmt; 00876 int result; 00877 00878 // first we search the users db as any srsid there will be definition be greater than in sys db 00879 00880 //check the db is available 00881 QString databaseFileName = QgsApplication::qgisUserDbFilePath(); 00882 if ( QFileInfo( databaseFileName ).exists() ) //only bother trying to open if the file exists 00883 { 00884 result = sqlite3_open_v2( databaseFileName.toUtf8().data(), &database, SQLITE_OPEN_READONLY, NULL ); 00885 if ( result ) 00886 { 00887 // XXX This will likely never happen since on open, sqlite creates the 00888 // database if it does not exist. But we checked earlier for its existance 00889 // and aborted in that case. This is because we may be runnig from read only 00890 // media such as live cd and don't want to force trying to create a db. 00891 showDBMissingWarning( databaseFileName ); 00892 return 0; 00893 } 00894 00895 result = sqlite3_prepare( database, theSql.toUtf8(), theSql.toUtf8().length(), &stmt, &tail ); 00896 // XXX Need to free memory from the error msg if one is set 00897 if ( result == SQLITE_OK && sqlite3_step( stmt ) == SQLITE_ROW ) 00898 { 00899 QString srsIdString = QString::fromUtf8(( char * )sqlite3_column_text( stmt, 0 ) ); 00900 srsId = srsIdString.toLong(); 00901 // close the sqlite3 statement 00902 sqlite3_finalize( stmt ); 00903 sqlite3_close( database ); 00904 return srsId; 00905 } 00906 } 00907 else 00908 { 00909 //only bother looking in srs.db if it wasnt found above 00910 result = sqlite3_open_v2( mSrsDatabaseFileName.toUtf8().data(), &database, SQLITE_OPEN_READONLY, NULL ); 00911 if ( result ) 00912 { 00913 QgsDebugMsg( QString( "Can't open * user * database: %1" ).arg( sqlite3_errmsg( database ) ) ); 00914 //no need for assert because user db may not have been created yet 00915 return 0; 00916 } 00917 } 00918 00919 result = sqlite3_prepare( database, theSql.toUtf8(), theSql.toUtf8().length(), &stmt, &tail ); 00920 // XXX Need to free memory from the error msg if one is set 00921 if ( result == SQLITE_OK && sqlite3_step( stmt ) == SQLITE_ROW ) 00922 { 00923 QString srsIdString = QString::fromUtf8(( char * )sqlite3_column_text( stmt, 0 ) ); 00924 srsId = srsIdString.toLong(); 00925 } 00926 00927 // close the sqlite3 statement 00928 sqlite3_finalize( stmt ); 00929 sqlite3_close( database ); 00930 00931 return srsId; 00932 } 00933 00934 QStringList QgsProjectionSelector::authorities() 00935 { 00936 sqlite3 *database; 00937 const char *tail; 00938 sqlite3_stmt *stmt; 00939 00940 int result = sqlite3_open_v2( mSrsDatabaseFileName.toUtf8().data(), &database, SQLITE_OPEN_READONLY, NULL ); 00941 if ( result ) 00942 { 00943 QgsDebugMsg( QString( "Can't open * user * database: %1" ).arg( sqlite3_errmsg( database ) ) ); 00944 //no need for assert because user db may not have been created yet 00945 return QStringList(); 00946 } 00947 00948 QString theSql = "select distinct auth_name from tbl_srs"; 00949 result = sqlite3_prepare( database, theSql.toUtf8(), theSql.toUtf8().length(), &stmt, &tail ); 00950 00951 QStringList authorities; 00952 if ( result == SQLITE_OK ) 00953 { 00954 while ( sqlite3_step( stmt ) == SQLITE_ROW ) 00955 { 00956 authorities << QString::fromUtf8(( char * )sqlite3_column_text( stmt, 0 ) ); 00957 } 00958 00959 } 00960 00961 // close the sqlite3 statement 00962 sqlite3_finalize( stmt ); 00963 sqlite3_close( database ); 00964 00965 return authorities; 00966 } 00967 00977 const QString QgsProjectionSelector::sqlSafeString( const QString theSQL ) 00978 { 00979 QString retval = theSQL; 00980 retval.replace( "\\", "\\\\" ); 00981 retval.replace( '\"', "\\\"" ); 00982 retval.replace( "\'", "\\'" ); 00983 retval.replace( "%", "\\%" ); 00984 return retval; 00985 } 00986 00987 void QgsProjectionSelector::showDBMissingWarning( const QString theFileName ) 00988 { 00989 00990 QMessageBox::critical( this, tr( "Resource Location Error" ), 00991 tr( "Error reading database file from: \n %1\n" 00992 "Because of this the projection selector will not work..." ) 00993 .arg( theFileName ) ); 00994 }