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