Quantum GIS API Documentation  1.7.4
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 /* $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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines