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