Quantum GIS API Documentation  1.8
src/gui/qgsprojectionselector.cpp
Go to the documentation of this file.
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       }
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines