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