720 changes: 312 additions & 408 deletions src/gui/qgsprojectionselector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,53 +11,45 @@
#include <qgsprojectionselector.h>

//standard includes
#include <cassert>
#include <sqlite3.h>

//qgis includes
#include "qgis.h" //magic numbers here
#include "qgsapplication.h"
#include "qgslogger.h"
#include <qgscoordinatereferencesystem.h>
#include "qgscoordinatereferencesystem.h"

//qt includes
#include <QDir>
#include <QFileInfo>
#include <QTextStream>
#include <QHeaderView>
#include <QResizeEvent>
#include <QMessageBox>
#include <QSettings>

const int NAME_COLUMN = 0;
const int AUTHID_COLUMN = 1;
const int QGIS_CRS_ID_COLUMN = 2;

QgsProjectionSelector::QgsProjectionSelector( QWidget* parent, const char *name, Qt::WFlags fl )
: QWidget( parent, fl )
, mProjListDone( false )
, mUserProjListDone( false )
, mRecentProjListDone( false )
, mCRSNameSelectionPending( false )
, mCRSIDSelectionPending( false )
, mAuthIDSelectionPending( false )
, mSearchColumn( NONE )
{
Q_UNUSED( name );
setupUi( this );
connect( lstCoordinateSystems, SIGNAL( currentItemChanged( QTreeWidgetItem*, QTreeWidgetItem* ) ),
this, SLOT( coordinateSystemSelected( QTreeWidgetItem* ) ) );

// Get the full path name to the sqlite3 spatial reference database.
mSrsDatabaseFileName = QgsApplication::srsDbFilePath();

lstCoordinateSystems->header()->setResizeMode( AUTHID_COLUMN, QHeaderView::Stretch );
lstCoordinateSystems->header()->resizeSection( QGIS_CRS_ID_COLUMN, 0 );
lstCoordinateSystems->header()->setResizeMode( QGIS_CRS_ID_COLUMN, QHeaderView::Fixed );

// Hide (internal) ID column
lstCoordinateSystems->setColumnHidden( QGIS_CRS_ID_COLUMN, true );

lstRecent->header()->setResizeMode( AUTHID_COLUMN, QHeaderView::Stretch );
lstRecent->header()->resizeSection( QGIS_CRS_ID_COLUMN, 0 );
lstRecent->header()->setResizeMode( QGIS_CRS_ID_COLUMN, QHeaderView::Fixed );

// Hide (internal) ID column
lstRecent->setColumnHidden( QGIS_CRS_ID_COLUMN, true );

Expand Down Expand Up @@ -94,46 +86,44 @@ QgsProjectionSelector::QgsProjectionSelector( QWidget* parent, const char *name,
}
}


QgsProjectionSelector::~QgsProjectionSelector()
{
// Save persistent list of projects
QSettings settings;
long crsId;

// Push current projection to front, only if set
crsId = selectedCrsId();
if ( crsId )
long crsId = selectedCrsId();
if ( crsId == 0 )
return;

// Save persistent list of projects
mRecentProjections.removeAll( QString::number( crsId ) );
mRecentProjections.prepend( QString::number( crsId ) );
// Prune size of list
while ( mRecentProjections.size() > 8 )
{
mRecentProjections.removeAll( QString::number( crsId ) );
mRecentProjections.prepend( QString::number( crsId ) );
// Prune size of list
while ( mRecentProjections.size() > 8 )
{
mRecentProjections.removeLast();
}
// Save to file *** Should be removed sometims in the future ***
settings.setValue( "/UI/recentProjections", mRecentProjections );
mRecentProjections.removeLast();
}

// Save to file *** Should be removed sometims in the future ***
QSettings settings;
settings.setValue( "/UI/recentProjections", mRecentProjections );

// Convert to EPSG and proj4, and save those values also
// Convert to EPSG and proj4, and save those values also

QStringList projectionsProj4;
QStringList projectionsAuthId;
for ( int i = 0; i < mRecentProjections.size(); i++ )
QStringList projectionsProj4;
QStringList projectionsAuthId;
for ( int i = 0; i < mRecentProjections.size(); i++ )
{
// Create a crs from the crsId
QgsCoordinateReferenceSystem crs( mRecentProjections.at( i ).toLong(), QgsCoordinateReferenceSystem::InternalCrsId );
if ( ! crs.isValid() )
{
// Create a crs from the crsId
QgsCoordinateReferenceSystem crs( mRecentProjections.at( i ).toLong(), QgsCoordinateReferenceSystem::InternalCrsId );
if ( ! crs.isValid() )
{
// No? Skip this entry
continue;
}
projectionsProj4 << crs.toProj4();
projectionsAuthId << crs.authid();
// No? Skip this entry
continue;
}
settings.setValue( "/UI/recentProjectionsProj4", projectionsProj4 );
settings.setValue( "/UI/recentProjectionsAuthId", projectionsAuthId );
projectionsProj4 << crs.toProj4();
projectionsAuthId << crs.authid();
}
settings.setValue( "/UI/recentProjectionsProj4", projectionsProj4 );
settings.setValue( "/UI/recentProjectionsAuthId", projectionsAuthId );
}

void QgsProjectionSelector::resizeEvent( QResizeEvent * theEvent )
Expand All @@ -151,23 +141,8 @@ void QgsProjectionSelector::showEvent( QShowEvent * theEvent )
{
// ensure the projection list view is actually populated
// before we show this widget

if ( !mProjListDone )
{
loadCrsList( &mCrsFilter );
}

if ( !mUserProjListDone )
{
loadUserCrsList( &mCrsFilter );
}

// check if a paricular projection is waiting
// to be pre-selected, and if so, to select it now.
if ( mCRSNameSelectionPending || mCRSIDSelectionPending || mAuthIDSelectionPending )
{
applySelection();
}
loadCrsList( &mCrsFilter );
loadUserCrsList( &mCrsFilter );

if ( !mRecentProjListDone )
{
Expand All @@ -176,6 +151,9 @@ void QgsProjectionSelector::showEvent( QShowEvent * theEvent )
mRecentProjListDone = true;
}

// apply deferred selection
applySelection();

// Pass up the inheritance hierarchy
QWidget::showEvent( theEvent );
}
Expand All @@ -186,9 +164,8 @@ QString QgsProjectionSelector::ogcWmsCrsFilterAsSqlExpression( QSet<QString> * c
QMap<QString, QStringList> authParts;

if ( !crsFilter )
{
return sqlExpression;
}

/*
Ref: WMS 1.3.0, section 6.7.3 "Layer CRS":
Expand Down Expand Up @@ -239,38 +216,14 @@ QString QgsProjectionSelector::ogcWmsCrsFilterAsSqlExpression( QSet<QString> * c
return sqlExpression;
}


void QgsProjectionSelector::setSelectedCrsName( QString theCRSName )
{
mCRSNameSelection = theCRSName;
mCRSNameSelectionPending = true;
mCRSIDSelectionPending = false; // only one type can be pending at a time
mAuthIDSelectionPending = true;

if ( isVisible() )
{
applySelection();
}
// else we will wait for the projection selector to
// become visible (with the showEvent()) and set the
// selection there
applySelection( NAME_COLUMN, theCRSName );
}


void QgsProjectionSelector::setSelectedCrsId( long theCRSID )
{
mCRSIDSelection = theCRSID;
mCRSIDSelectionPending = true;
mCRSNameSelectionPending = false; // only one type can be pending at a time
mAuthIDSelectionPending = false;

if ( isVisible() )
{
applySelection();
}
// else we will wait for the projection selector to
// become visible (with the showEvent()) and set the
// selection there
applySelection( QGIS_CRS_ID_COLUMN, QString::number( theCRSID ) );
}

void QgsProjectionSelector::setSelectedEpsg( long id )
Expand All @@ -280,53 +233,41 @@ void QgsProjectionSelector::setSelectedEpsg( long id )

void QgsProjectionSelector::setSelectedAuthId( QString id )
{
mAuthIDSelection = id;
mCRSIDSelectionPending = false;
mAuthIDSelectionPending = true;
mCRSNameSelectionPending = false; // only one type can be pending at a time
applySelection( AUTHID_COLUMN, id );
}

void QgsProjectionSelector::applySelection()
void QgsProjectionSelector::applySelection( int column, QString value )
{
if ( !mProjListDone || !mUserProjListDone )
return;

QList<QTreeWidgetItem*> nodes;
if ( mCRSNameSelectionPending )
{
//get the srid given the wkt so we can pick the correct list item
QgsDebugMsg( "called with " + mCRSNameSelection );
nodes = lstCoordinateSystems->findItems( mCRSNameSelection, Qt::MatchExactly | Qt::MatchRecursive, 0 );

mCRSNameSelectionPending = false;
// defer selection until loaded
mSearchColumn = column;
mSearchValue = value;
return;
}

if ( mAuthIDSelectionPending )
if ( column == NONE )
{
//get the srid given the wkt so we can pick the correct list item
QgsDebugMsg( "called with " + mAuthIDSelection );
nodes = lstCoordinateSystems->findItems( mAuthIDSelection, Qt::MatchExactly | Qt::MatchRecursive, AUTHID_COLUMN );

mAuthIDSelectionPending = false;
// invoked deferred selection
column = mSearchColumn;
value = mSearchValue;
}

if ( mCRSIDSelectionPending )
{
QString myCRSIDString = QString::number( mCRSIDSelection );

nodes = lstCoordinateSystems->findItems( myCRSIDString, Qt::MatchExactly | Qt::MatchRecursive, QGIS_CRS_ID_COLUMN );

mCRSIDSelectionPending = false;
}
if ( column == NONE )
return;

QList<QTreeWidgetItem*> nodes = lstCoordinateSystems->findItems( value, Qt::MatchExactly | Qt::MatchRecursive, column );
if ( nodes.count() > 0 )
{
QgsDebugMsg( QString( "found %1,%2" ).arg( column ).arg( value ) );
lstCoordinateSystems->setCurrentItem( nodes.first() );
lstCoordinateSystems->scrollToItem( lstCoordinateSystems->currentItem(), QAbstractItemView::PositionAtCenter );
}
else // unselect the selected item to avoid confusing the user
else
{
QgsDebugMsg( "nothing found" );
// unselect the selected item to avoid confusing the user
lstCoordinateSystems->clearSelection();
lstRecent->clearSelection();
teProjection->setText( "" );
}
}
Expand All @@ -337,7 +278,6 @@ void QgsProjectionSelector::insertRecent( long theCrsId )
return;

QList<QTreeWidgetItem*> nodes = lstCoordinateSystems->findItems( QString::number( theCrsId ), Qt::MatchExactly | Qt::MatchRecursive, QGIS_CRS_ID_COLUMN );

if ( nodes.count() == 0 )
return;

Expand All @@ -352,15 +292,9 @@ QString QgsProjectionSelector::selectedName()
{
// return the selected wkt name from the list view
QTreeWidgetItem *lvi = lstCoordinateSystems->currentItem();
if ( lvi )
{
return lvi->text( 0 );
}
else
{
return QString::null;
}
return lvi ? lvi->text( NAME_COLUMN ) : QString::null;
}

// Returns the whole proj4 string for the selected projection node
QString QgsProjectionSelector::selectedProj4String()
{
Expand All @@ -370,87 +304,64 @@ QString QgsProjectionSelector::selectedProj4String()
// system
//
// Get the selected node
QTreeWidgetItem *myItem = lstCoordinateSystems->currentItem();
if ( myItem )
{
QTreeWidgetItem *item = lstCoordinateSystems->currentItem();
if ( !item || item->text( QGIS_CRS_ID_COLUMN ).isEmpty() )
return "";

if ( myItem->text( QGIS_CRS_ID_COLUMN ).length() > 0 )
{
QString myDatabaseFileName;
QString mySrsId = myItem->text( QGIS_CRS_ID_COLUMN );

QgsDebugMsg( "mySrsId = " + mySrsId );
QgsDebugMsg( "USER_CRS_START_ID = " + QString::number( USER_CRS_START_ID ) );
//
// Determine if this is a user projection or a system on
// user projection defs all have srs_id >= 100000
//
if ( mySrsId.toLong() >= USER_CRS_START_ID )
{
myDatabaseFileName = QgsApplication::qgisUserDbFilePath();
QFileInfo myFileInfo;
myFileInfo.setFile( myDatabaseFileName );
if ( !myFileInfo.exists( ) ) //its unlikely that this condition will ever be reached
{
QgsDebugMsg( "users qgis.db not found" );
return QString( "" );
}
else
{
QgsDebugMsg( "users qgis.db found" );
}
}
else //must be a system projection then
{
myDatabaseFileName = mSrsDatabaseFileName;
}
QgsDebugMsg( "db = " + myDatabaseFileName );
QString srsId = item->text( QGIS_CRS_ID_COLUMN );

QgsDebugMsg( "srsId = " + srsId );
QgsDebugMsg( "USER_CRS_START_ID = " + QString::number( USER_CRS_START_ID ) );

sqlite3 *db;
int rc;
rc = sqlite3_open( myDatabaseFileName.toUtf8().data(), &db );
if ( rc )
{
showDBMissingWarning( myDatabaseFileName );
return QString( "" );
}
// prepare the sql statement
const char *pzTail;
sqlite3_stmt *ppStmt;
QString sql = QString( "select parameters from tbl_srs where srs_id = %1" ).arg( mySrsId );
//
// Determine if this is a user projection or a system on
// user projection defs all have srs_id >= 100000
//
QString databaseFileName;
if ( srsId.toLong() >= USER_CRS_START_ID )
{
databaseFileName = QgsApplication::qgisUserDbFilePath();
if ( !QFileInfo( databaseFileName ).exists() ) //its unlikely that this condition will ever be reached
return QString( "" );
}
else //must be a system projection then
{
databaseFileName = mSrsDatabaseFileName;
}

QgsDebugMsg( "Selection sql: " + sql );
QgsDebugMsg( "db = " + databaseFileName );

rc = sqlite3_prepare( db, sql.toUtf8(), sql.toUtf8().length(), &ppStmt, &pzTail );
// XXX Need to free memory from the error msg if one is set
QString myProjString;
if ( rc == SQLITE_OK )
{
if ( sqlite3_step( ppStmt ) == SQLITE_ROW )
{
myProjString = QString::fromUtf8(( char * )sqlite3_column_text( ppStmt, 0 ) );
}
}
// close the statement
sqlite3_finalize( ppStmt );
// close the database
sqlite3_close( db );
assert( myProjString.length() > 0 );
return myProjString;
}
else
{
// No node is selected, return null
return QString( "" );
}
sqlite3 *database;
int rc = sqlite3_open( databaseFileName.toUtf8().data(), &database );
if ( rc )
{
showDBMissingWarning( databaseFileName );
return "";
}
else

// prepare the sql statement
const char *tail;
sqlite3_stmt *stmt;
QString sql = QString( "select parameters from tbl_srs where srs_id=%1" ).arg( srsId );

QgsDebugMsg( "Selection sql: " + sql );

rc = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail );
// XXX Need to free memory from the error msg if one is set
QString projString;
if ( rc == SQLITE_OK && sqlite3_step( stmt ) == SQLITE_ROW )
{
// No node is selected, return null
return QString( "" );
projString = QString::fromUtf8(( char * )sqlite3_column_text( stmt, 0 ) );
}

// close the statement
sqlite3_finalize( stmt );
// close the database
sqlite3_close( database );

Q_ASSERT( !projString.isEmpty() );

return projString;
}

QString QgsProjectionSelector::getSelectedExpression( QString expression )
Expand All @@ -460,77 +371,67 @@ QString QgsProjectionSelector::getSelectedExpression( QString expression )
// selects a top-level node rather than an actual coordinate
// system
//
// Get the selected node
// Get the selected node and make sure it is a srs andx
// not a top-level projection node
QTreeWidgetItem *lvi = lstCoordinateSystems->currentItem();
if ( lvi )
if ( !lvi || lvi->text( QGIS_CRS_ID_COLUMN ).isEmpty() )
return 0;

//
// Determine if this is a user projection or a system on
// user projection defs all have srs_id >= 100000
//
QString databaseFileName;
if ( lvi->text( QGIS_CRS_ID_COLUMN ).toLong() >= USER_CRS_START_ID )
{
// Make sure the selected node is a srs and not a top-level projection node
if ( lvi->text( QGIS_CRS_ID_COLUMN ).length() > 0 )
databaseFileName = QgsApplication::qgisUserDbFilePath();
if ( !QFileInfo( databaseFileName ).exists() )
{
QString myDatabaseFileName;
//
// Determine if this is a user projection or a system on
// user projection defs all have srs_id >= 100000
//
if ( lvi->text( QGIS_CRS_ID_COLUMN ).toLong() >= USER_CRS_START_ID )
{
myDatabaseFileName = QgsApplication::qgisUserDbFilePath();
QFileInfo myFileInfo;
myFileInfo.setFile( myDatabaseFileName );
if ( !myFileInfo.exists( ) )
{
QgsDebugMsg( " Projection selector : users qgis.db not found" );
return 0;
}
}
else //must be a system projection then
{
myDatabaseFileName = mSrsDatabaseFileName;
}
//
// set up the database
// XXX We could probabaly hold the database open for the life of this object,
// assuming that it will never be used anywhere else. Given the low overhead,
// opening it each time seems to be a reasonable approach at this time.
sqlite3 *db;
int rc;
rc = sqlite3_open( myDatabaseFileName.toUtf8().data(), &db );
if ( rc )
{
showDBMissingWarning( myDatabaseFileName );
return 0;
}
// prepare the sql statement
const char *pzTail;
sqlite3_stmt *ppStmt;
QString sql = QString( "select %1 from tbl_srs where srs_id=%2" )
.arg( expression )
.arg( lvi->text( QGIS_CRS_ID_COLUMN ) );

QgsDebugMsg( QString( "Finding selected attribute using : %1" ).arg( sql ) );
rc = sqlite3_prepare( db, sql.toUtf8(), sql.toUtf8().length(), &ppStmt, &pzTail );
// XXX Need to free memory from the error msg if one is set
QString myAttributeValue;
if ( rc == SQLITE_OK )
{
// get the first row of the result set
if ( sqlite3_step( ppStmt ) == SQLITE_ROW )
{
// get the attribute
myAttributeValue = QString::fromUtf8(( char * )sqlite3_column_text( ppStmt, 0 ) );
}
}
// close the statement
sqlite3_finalize( ppStmt );
// close the database
sqlite3_close( db );
// return the srs
return myAttributeValue;
return 0;
}
}
else
{
databaseFileName = mSrsDatabaseFileName;
}

// No node is selected, return null
return 0;
//
// set up the database
// XXX We could probabaly hold the database open for the life of this object,
// assuming that it will never be used anywhere else. Given the low overhead,
// opening it each time seems to be a reasonable approach at this time.
sqlite3 *database;
int rc = sqlite3_open( databaseFileName.toUtf8().data(), &database );
if ( rc )
{
showDBMissingWarning( databaseFileName );
return 0;
}

// prepare the sql statement
const char *tail;
sqlite3_stmt *stmt;
QString sql = QString( "select %1 from tbl_srs where srs_id=%2" )
.arg( expression )
.arg( lvi->text( QGIS_CRS_ID_COLUMN ) );

QgsDebugMsg( QString( "Finding selected attribute using : %1" ).arg( sql ) );
rc = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail );
// XXX Need to free memory from the error msg if one is set
QString attributeValue;
if ( rc == SQLITE_OK && sqlite3_step( stmt ) == SQLITE_ROW )
{
// get the first row of the result set
attributeValue = QString::fromUtf8(( char * )sqlite3_column_text( stmt, 0 ) );
}

// close the statement
sqlite3_finalize( stmt );
// close the database
sqlite3_close( database );

// return the srs
return attributeValue;
}

long QgsProjectionSelector::selectedEpsg()
Expand Down Expand Up @@ -566,14 +467,10 @@ long QgsProjectionSelector::selectedCrsId()
{
QTreeWidgetItem* item = lstCoordinateSystems->currentItem();

if ( item != NULL && item->text( QGIS_CRS_ID_COLUMN ).length() > 0 )
{
if ( item && !item->text( QGIS_CRS_ID_COLUMN ).isEmpty() )
return lstCoordinateSystems->currentItem()->text( QGIS_CRS_ID_COLUMN ).toLong();
}
else
{
return 0;
}
}


Expand All @@ -585,9 +482,11 @@ void QgsProjectionSelector::setOgcWmsCrsFilter( QSet<QString> crsFilter )
lstCoordinateSystems->clear();
}


void QgsProjectionSelector::loadUserCrsList( QSet<QString> * crsFilter )
void QgsProjectionSelector::loadUserCrsList( QSet<QString> *crsFilter )
{
if ( mUserProjListDone )
return;

QgsDebugMsg( "Fetching user projection list..." );

// convert our Coordinate Reference System filter into the SQL expression
Expand All @@ -605,64 +504,64 @@ void QgsProjectionSelector::loadUserCrsList( QSet<QString> * crsFilter )

//determine where the user proj database lives for this user. If none is found an empty
//now only will be shown
QString myDatabaseFileName = QgsApplication::qgisUserDbFilePath();
QString databaseFileName = QgsApplication::qgisUserDbFilePath();
// first we look for ~/.qgis/qgis.db
// if it doesnt exist we copy it in from the global resources dir
QFileInfo myFileInfo;
myFileInfo.setFile( myDatabaseFileName );

//return straight away if the user has not created any custom projections
if ( !myFileInfo.exists( ) )
if ( !QFileInfo( databaseFileName ).exists( ) )
{
QgsDebugMsg( "Users qgis.db not found...skipping" );

mUserProjListDone = true;
return;
}

sqlite3 *myDatabase;
const char *myTail;
sqlite3_stmt *myPreparedStatement;
int myResult;
sqlite3 *database;
const char *tail;
sqlite3_stmt *stmt;
//check the db is available
myResult = sqlite3_open( QString( myDatabaseFileName ).toUtf8().data(), &myDatabase );
if ( myResult )
int result = sqlite3_open( databaseFileName.toUtf8().constData(), &database );
if ( result )
{
// XXX This will likely never happen since on open, sqlite creates the
// database if it does not exist. But we checked earlier for its existance
// and aborted in that case. This is because we may be runnig from read only
// media such as live cd and don't want to force trying to create a db.
showDBMissingWarning( myDatabaseFileName );
showDBMissingWarning( databaseFileName );
return;
}

// Set up the query to retrieve the projection information needed to populate the list
QString mySql = QString( "select description, srs_id from vw_srs where %1" ).arg( sqlFilter );
QString sql = QString( "select description, srs_id from vw_srs where %1" ).arg( sqlFilter );

myResult = sqlite3_prepare( myDatabase, mySql.toUtf8(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
result = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail );
// XXX Need to free memory from the error msg if one is set
if ( myResult == SQLITE_OK )
if ( result == SQLITE_OK )
{
QTreeWidgetItem *newItem;
while ( sqlite3_step( myPreparedStatement ) == SQLITE_ROW )
while ( sqlite3_step( stmt ) == SQLITE_ROW )
{
newItem = new QTreeWidgetItem( mUserProjList, QStringList( QString::fromUtf8(( char * )sqlite3_column_text( myPreparedStatement, 0 ) ) ) );
newItem = new QTreeWidgetItem( mUserProjList, QStringList( QString::fromUtf8(( char * )sqlite3_column_text( stmt, 0 ) ) ) );
// EpsgCrsId for user projections is not always defined in some dbases.
// It's also not written from customprojections dialog.
// display the epsg (field 2) in the second column of the list view
// newItem->setText( EPSG_COLUMN, QString::fromUtf8(( char * )sqlite3_column_text( myPreparedStatement, 2 ) ) );
// newItem->setText( EPSG_COLUMN, QString::fromUtf8(( char * )sqlite3_column_text( stmt, 2 ) ) );
// display the qgis srs_id (field 1) in the third column of the list view
newItem->setText( QGIS_CRS_ID_COLUMN, QString::fromUtf8(( char * )sqlite3_column_text( myPreparedStatement, 1 ) ) );
newItem->setText( QGIS_CRS_ID_COLUMN, QString::fromUtf8(( char * )sqlite3_column_text( stmt, 1 ) ) );
}
}
// close the sqlite3 statement
sqlite3_finalize( myPreparedStatement );
sqlite3_close( myDatabase );
sqlite3_finalize( stmt );
sqlite3_close( database );

mUserProjListDone = true;
}

void QgsProjectionSelector::loadCrsList( QSet<QString> *crsFilter )
{
if ( mProjListDone )
return;

// convert our Coordinate Reference System filter into the SQL expression
QString sqlFilter = ogcWmsCrsFilterAsSqlExpression( crsFilter );

Expand Down Expand Up @@ -692,74 +591,70 @@ void QgsProjectionSelector::loadCrsList( QSet<QString> *crsFilter )
//read only filesystem because otherwise sqlite will try
//to create the db file on the fly

QFileInfo myFileInfo;
myFileInfo.setFile( mSrsDatabaseFileName );
if ( !myFileInfo.exists( ) )
if ( !QFileInfo( mSrsDatabaseFileName ).exists() )
{
mProjListDone = true;
return;
}

// open the database containing the spatial reference data
sqlite3 *db;
int rc;
rc = sqlite3_open( mSrsDatabaseFileName.toUtf8().data(), &db );
sqlite3 *database;
int rc = sqlite3_open( mSrsDatabaseFileName.toUtf8().data(), &database );
if ( rc )
{
// XXX This will likely never happen since on open, sqlite creates the
// database if it does not exist.
showDBMissingWarning( mSrsDatabaseFileName );
return ;
return;
}
// prepare the sql statement
const char *pzTail;
sqlite3_stmt *ppStmt;
const char *tail;
sqlite3_stmt *stmt;
// get total count of records in the projection table
QString sql = "select count(*) from tbl_srs";

rc = sqlite3_prepare( db, sql.toUtf8(), sql.toUtf8().length(), &ppStmt, &pzTail );
assert( rc == SQLITE_OK );
sqlite3_step( ppStmt );

sqlite3_finalize( ppStmt );
rc = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail );
Q_ASSERT( rc == SQLITE_OK );
sqlite3_step( stmt );
sqlite3_finalize( stmt );

// Set up the query to retrieve the projection information needed to populate the list
//note I am giving the full field names for clarity here and in case someone
//changes the underlying view TS
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" )
.arg( sqlFilter );

rc = sqlite3_prepare( db, sql.toUtf8(), sql.toUtf8().length(), &ppStmt, &pzTail );
rc = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail );
// XXX Need to free memory from the error msg if one is set
if ( rc == SQLITE_OK )
{
QTreeWidgetItem *newItem;
// Cache some stuff to speed up creating of the list of projected
// spatial reference systems
QString previousSrsType( "" );
QTreeWidgetItem* previousSrsTypeNode = NULL;
QTreeWidgetItem* previousSrsTypeNode = 0;

while ( sqlite3_step( ppStmt ) == SQLITE_ROW )
while ( sqlite3_step( stmt ) == SQLITE_ROW )
{
// check to see if the srs is geographic
int isGeo = sqlite3_column_int( ppStmt, 3 );
int isGeo = sqlite3_column_int( stmt, 3 );
if ( isGeo )
{
// this is a geographic coordinate system
// Add it to the tree (field 0)
newItem = new QTreeWidgetItem( mGeoList, QStringList( QString::fromUtf8(( char * )sqlite3_column_text( ppStmt, 0 ) ) ) );
newItem = new QTreeWidgetItem( mGeoList, QStringList( QString::fromUtf8(( char * )sqlite3_column_text( stmt, 0 ) ) ) );

// display the authority name (field 2) in the second column of the list view
newItem->setText( AUTHID_COLUMN, QString::fromUtf8(( char * )sqlite3_column_text( ppStmt, 2 ) ) );
newItem->setText( AUTHID_COLUMN, QString::fromUtf8(( char * )sqlite3_column_text( stmt, 2 ) ) );

// display the qgis srs_id (field 1) in the third column of the list view
newItem->setText( QGIS_CRS_ID_COLUMN, QString::fromUtf8(( char * )sqlite3_column_text( ppStmt, 1 ) ) );
newItem->setText( QGIS_CRS_ID_COLUMN, QString::fromUtf8(( char * )sqlite3_column_text( stmt, 1 ) ) );
}
else
{
// This is a projected srs
QTreeWidgetItem *node;
QString srsType = QString::fromUtf8(( char* )sqlite3_column_text( ppStmt, 4 ) );
QString srsType = QString::fromUtf8(( char* )sqlite3_column_text( stmt, 4 ) );
// Find the node for this type and add the projection to it
// If the node doesn't exist, create it
if ( srsType == previousSrsType )
Expand All @@ -768,13 +663,12 @@ void QgsProjectionSelector::loadCrsList( QSet<QString> *crsFilter )
}
else
{ // Different from last one, need to search
QList<QTreeWidgetItem*> nodes = lstCoordinateSystems->findItems( srsType, Qt::MatchExactly | Qt::MatchRecursive, 0 );
QList<QTreeWidgetItem*> nodes = lstCoordinateSystems->findItems( srsType, Qt::MatchExactly | Qt::MatchRecursive, NAME_COLUMN );
if ( nodes.count() == 0 )
{
// the node doesn't exist -- create it
// Make in an italic font to distinguish them from real projections
node = new QTreeWidgetItem( mProjList, QStringList( srsType ) );

QFont fontTemp = node->font( 0 );
fontTemp.setItalic( true );
node->setFont( 0, fontTemp );
Expand All @@ -788,53 +682,77 @@ void QgsProjectionSelector::loadCrsList( QSet<QString> *crsFilter )
previousSrsTypeNode = node;
}
// add the item, setting the projection name in the first column of the list view
newItem = new QTreeWidgetItem( node, QStringList( QString::fromUtf8(( char * )sqlite3_column_text( ppStmt, 0 ) ) ) );
newItem = new QTreeWidgetItem( node, QStringList( QString::fromUtf8(( char * )sqlite3_column_text( stmt, 0 ) ) ) );
// display the authority id (field 2) in the second column of the list view
newItem->setText( AUTHID_COLUMN, QString::fromUtf8(( char * )sqlite3_column_text( ppStmt, 2 ) ) );
newItem->setText( AUTHID_COLUMN, QString::fromUtf8(( char * )sqlite3_column_text( stmt, 2 ) ) );
// display the qgis srs_id (field 1) in the third column of the list view
newItem->setText( QGIS_CRS_ID_COLUMN, QString::fromUtf8(( char * )sqlite3_column_text( ppStmt, 1 ) ) );
newItem->setText( QGIS_CRS_ID_COLUMN, QString::fromUtf8(( char * )sqlite3_column_text( stmt, 1 ) ) );
// expand also parent node
newItem->parent()->setExpanded( true );
}

// display the qgis deprecated in the user data of the item
newItem->setData( 0, Qt::UserRole, QString::fromUtf8(( char * )sqlite3_column_text( ppStmt, 6 ) ) );
newItem->setData( 0, Qt::UserRole, QString::fromUtf8(( char * )sqlite3_column_text( stmt, 6 ) ) );
newItem->setHidden( cbxHideDeprecated->isChecked() );
}
mProjList->setExpanded( true );
}

// close the sqlite3 statement
sqlite3_finalize( ppStmt );
sqlite3_finalize( stmt );
// close the database
sqlite3_close( db );
sqlite3_close( database );

mProjListDone = true;
}

// New coordinate system selected from the list
void QgsProjectionSelector::coordinateSystemSelected( QTreeWidgetItem * theItem )
void QgsProjectionSelector::on_lstCoordinateSystems_currentItemChanged( QTreeWidgetItem *current, QTreeWidgetItem * )
{
QgsDebugMsg( "Entered." );
lstCoordinateSystems->scrollToItem( current );

// If the item has children, it's not an end node in the tree, and
// hence is just a grouping thingy, not an actual CRS.
if ( theItem && theItem->childCount() == 0 )
if ( current && current->childCount() == 0 )
{
// Found a real CRS
emit sridSelected( QString::number( selectedCrsId() ) );
QString myProjString = selectedProj4String();
lstCoordinateSystems->scrollToItem( theItem );
teProjection->setText( myProjString );

lstRecent->clearSelection();
teProjection->setText( selectedProj4String() );

QList<QTreeWidgetItem*> nodes = lstRecent->findItems( current->text( QGIS_CRS_ID_COLUMN ), Qt::MatchExactly, QGIS_CRS_ID_COLUMN );
if ( nodes.count() > 0 )
{
QgsDebugMsg( QString( "found srs %1 in recent" ).arg( current->text( QGIS_CRS_ID_COLUMN ) ) );
lstRecent->setCurrentItem( nodes.first() );
}
else
{
QgsDebugMsg( QString( "srs %1 not recent" ).arg( current->text( QGIS_CRS_ID_COLUMN ) ) );
lstRecent->clearSelection();
}
}
else
{
// Not an CRS - remove the highlight so the user doesn't get too confused
if ( theItem )
theItem->setSelected( false );
if ( current )
current->setSelected( false );
teProjection->setText( "" );
lstRecent->clearSelection();
}
}

void QgsProjectionSelector::on_lstRecent_currentItemChanged( QTreeWidgetItem *current, QTreeWidgetItem * )
{
QgsDebugMsg( "Entered." );
lstRecent->scrollToItem( current );

QList<QTreeWidgetItem*> nodes = lstCoordinateSystems->findItems( current->text( QGIS_CRS_ID_COLUMN ), Qt::MatchExactly | Qt::MatchRecursive, QGIS_CRS_ID_COLUMN );
if ( nodes.count() > 0 )
lstCoordinateSystems->setCurrentItem( nodes.first() );
}

void QgsProjectionSelector::hideDeprecated( QTreeWidgetItem *item )
{
if ( item->data( 0, Qt::UserRole ).toBool() )
Expand All @@ -857,12 +775,6 @@ void QgsProjectionSelector::on_cbxHideDeprecated_stateChanged()
hideDeprecated( lstCoordinateSystems->topLevelItem( i ) );
}

void QgsProjectionSelector::on_lstRecent_itemClicked( QTreeWidgetItem * item )
{
setSelectedCrsId( item->text( QGIS_CRS_ID_COLUMN ).toLong() );
item->setSelected( true );
}

void QgsProjectionSelector::on_leSearch_textChanged( const QString & theFilterTxt )
{
// filter recent crs's
Expand All @@ -877,7 +789,7 @@ void QgsProjectionSelector::on_leSearch_textChanged( const QString & theFilterTx
{
( *itr )->setHidden( false );
QTreeWidgetItem * parent = ( *itr )->parent();
while ( parent != NULL )
while ( parent )
{
parent->setExpanded( true );
parent->setHidden( false );
Expand All @@ -895,6 +807,7 @@ void QgsProjectionSelector::on_leSearch_textChanged( const QString & theFilterTx
}
++itr;
}

// filter crs's
QTreeWidgetItemIterator it( lstCoordinateSystems );
while ( *it )
Expand All @@ -907,7 +820,7 @@ void QgsProjectionSelector::on_leSearch_textChanged( const QString & theFilterTx
{
( *it )->setHidden( false );
QTreeWidgetItem * parent = ( *it )->parent();
while ( parent != NULL )
while ( parent )
{
parent->setExpanded( true );
parent->setHidden( false );
Expand All @@ -930,113 +843,104 @@ void QgsProjectionSelector::on_leSearch_textChanged( const QString & theFilterTx

long QgsProjectionSelector::getLargestCRSIDMatch( QString theSql )
{
long mySrsId = 0;
long srsId = 0;

//
// Now perform the actual search
//

sqlite3 *myDatabase;
const char *myTail;
sqlite3_stmt *myPreparedStatement;
int myResult;
sqlite3 *database;
const char *tail;
sqlite3_stmt *stmt;
int result;

// first we search the users db as any srsid there will be definition be greater than in sys db

//check the db is available
QString myDatabaseFileName = QgsApplication::qgisUserDbFilePath();
QFileInfo myFileInfo;
myFileInfo.setFile( myDatabaseFileName );
if ( myFileInfo.exists( ) ) //only bother trying to open if the file exists
QString databaseFileName = QgsApplication::qgisUserDbFilePath();
if ( QFileInfo( databaseFileName ).exists() ) //only bother trying to open if the file exists
{
myResult = sqlite3_open( myDatabaseFileName.toUtf8().data(), &myDatabase );
if ( myResult )
result = sqlite3_open( databaseFileName.toUtf8().data(), &database );
if ( result )
{
// XXX This will likely never happen since on open, sqlite creates the
// database if it does not exist. But we checked earlier for its existance
// and aborted in that case. This is because we may be runnig from read only
// media such as live cd and don't want to force trying to create a db.
showDBMissingWarning( myDatabaseFileName );
showDBMissingWarning( databaseFileName );
return 0;
}
else

result = sqlite3_prepare( database, theSql.toUtf8(), theSql.toUtf8().length(), &stmt, &tail );
// XXX Need to free memory from the error msg if one is set
if ( result == SQLITE_OK && sqlite3_step( stmt ) == SQLITE_ROW )
{
myResult = sqlite3_prepare( myDatabase, theSql.toUtf8(), theSql.toUtf8().length(), &myPreparedStatement, &myTail );
// XXX Need to free memory from the error msg if one is set
if ( myResult == SQLITE_OK )
{
myResult = sqlite3_step( myPreparedStatement );
if ( myResult == SQLITE_ROW )
{
QString mySrsIdString = QString::fromUtf8(( char * )sqlite3_column_text( myPreparedStatement, 0 ) );
mySrsId = mySrsIdString.toLong();
// close the sqlite3 statement
sqlite3_finalize( myPreparedStatement );
sqlite3_close( myDatabase );
return mySrsId;
}
}
QString srsIdString = QString::fromUtf8(( char * )sqlite3_column_text( stmt, 0 ) );
srsId = srsIdString.toLong();
// close the sqlite3 statement
sqlite3_finalize( stmt );
sqlite3_close( database );
return srsId;
}
}

//only bother looking in srs.db if it wasnt found above

myResult = sqlite3_open( mSrsDatabaseFileName.toUtf8().data(), &myDatabase );
if ( myResult )
else
{
QgsDebugMsg( QString( "Can't open * user * database: %1" ).arg( sqlite3_errmsg( myDatabase ) ) );
//no need for assert because user db may not have been created yet
return 0;
//only bother looking in srs.db if it wasnt found above
result = sqlite3_open( mSrsDatabaseFileName.toUtf8().data(), &database );
if ( result )
{
QgsDebugMsg( QString( "Can't open * user * database: %1" ).arg( sqlite3_errmsg( database ) ) );
//no need for assert because user db may not have been created yet
return 0;
}
}

myResult = sqlite3_prepare( myDatabase, theSql.toUtf8(), theSql.toUtf8().length(), &myPreparedStatement, &myTail );
result = sqlite3_prepare( database, theSql.toUtf8(), theSql.toUtf8().length(), &stmt, &tail );
// XXX Need to free memory from the error msg if one is set
if ( myResult == SQLITE_OK )
if ( result == SQLITE_OK && sqlite3_step( stmt ) == SQLITE_ROW )
{
myResult = sqlite3_step( myPreparedStatement );
if ( myResult == SQLITE_ROW )
{
QString mySrsIdString = QString::fromUtf8(( char * )sqlite3_column_text( myPreparedStatement, 0 ) );
mySrsId = mySrsIdString.toLong();
// close the sqlite3 statement
sqlite3_finalize( myPreparedStatement );
sqlite3_close( myDatabase );
}
QString srsIdString = QString::fromUtf8(( char * )sqlite3_column_text( stmt, 0 ) );
srsId = srsIdString.toLong();
}
return mySrsId;

// close the sqlite3 statement
sqlite3_finalize( stmt );
sqlite3_close( database );

return srsId;
}

QStringList QgsProjectionSelector::authorities()
{
sqlite3 *myDatabase;
const char *myTail;
sqlite3_stmt *myPreparedStatement;
int myResult;
sqlite3 *database;
const char *tail;
sqlite3_stmt *stmt;

myResult = sqlite3_open( mSrsDatabaseFileName.toUtf8().data(), &myDatabase );
if ( myResult )
int result = sqlite3_open( mSrsDatabaseFileName.toUtf8().data(), &database );
if ( result )
{
QgsDebugMsg( QString( "Can't open * user * database: %1" ).arg( sqlite3_errmsg( myDatabase ) ) );
QgsDebugMsg( QString( "Can't open * user * database: %1" ).arg( sqlite3_errmsg( database ) ) );
//no need for assert because user db may not have been created yet
return QStringList();
}

QString theSql = "select distinct auth_name from tbl_srs";
myResult = sqlite3_prepare( myDatabase, theSql.toUtf8(), theSql.toUtf8().length(), &myPreparedStatement, &myTail );
result = sqlite3_prepare( database, theSql.toUtf8(), theSql.toUtf8().length(), &stmt, &tail );

QStringList authorities;

if ( myResult == SQLITE_OK )
if ( result == SQLITE_OK )
{
while ( sqlite3_step( myPreparedStatement ) == SQLITE_ROW )
while ( sqlite3_step( stmt ) == SQLITE_ROW )
{
authorities << QString::fromUtf8(( char * )sqlite3_column_text( myPreparedStatement, 0 ) );
authorities << QString::fromUtf8(( char * )sqlite3_column_text( stmt, 0 ) );
}

// close the sqlite3 statement
sqlite3_finalize( myPreparedStatement );
sqlite3_close( myDatabase );
}

// close the sqlite3 statement
sqlite3_finalize( stmt );
sqlite3_close( database );

return authorities;
}

Expand All @@ -1051,12 +955,12 @@ QStringList QgsProjectionSelector::authorities()
*/
const QString QgsProjectionSelector::sqlSafeString( const QString theSQL )
{
QString myRetval = theSQL;
myRetval.replace( "\\", "\\\\" );
myRetval.replace( '\"', "\\\"" );
myRetval.replace( "\'", "\\'" );
myRetval.replace( "%", "\\%" );
return myRetval;
QString retval = theSQL;
retval.replace( "\\", "\\\\" );
retval.replace( '\"', "\\\"" );
retval.replace( "\'", "\\'" );
retval.replace( "%", "\\%" );
return retval;
}

void QgsProjectionSelector::showDBMissingWarning( const QString theFileName )
Expand Down
39 changes: 9 additions & 30 deletions src/gui/qgsprojectionselector.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@ class GUI_EXPORT QgsProjectionSelector: public QWidget, private Ui::QgsProjectio
{
Q_OBJECT
public:
QgsProjectionSelector( QWidget* parent,
const char * name = "",
Qt::WFlags fl = 0 );
QgsProjectionSelector( QWidget* parent, const char *name = "", Qt::WFlags fl = 0 );

~QgsProjectionSelector();

Expand All @@ -44,7 +42,7 @@ class GUI_EXPORT QgsProjectionSelector: public QWidget, private Ui::QgsProjectio
*
* \todo Should this be public?
*/
void loadUserCrsList( QSet<QString> * crsFilter = 0 );
void loadUserCrsList( QSet<QString> *crsFilter = 0 );

/**
* \brief Populate the proj tree view with system projection names...
Expand All @@ -55,8 +53,7 @@ class GUI_EXPORT QgsProjectionSelector: public QWidget, private Ui::QgsProjectio
*
* \todo Should this be public?
*/
void loadCrsList( QSet<QString> * crsFilter = 0 );

void loadCrsList( QSet<QString> *crsFilter = 0 );

/*!
* \brief Make the string safe for use in SQL statements.
Expand Down Expand Up @@ -113,7 +110,8 @@ class GUI_EXPORT QgsProjectionSelector: public QWidget, private Ui::QgsProjectio
* \warning This function's behaviour is undefined if it is called after the widget is shown.
*/
void setOgcWmsCrsFilter( QSet<QString> crsFilter );
void on_lstRecent_itemClicked( QTreeWidgetItem * );
void on_lstCoordinateSystems_currentItemChanged( QTreeWidgetItem *current, QTreeWidgetItem *prev );
void on_lstRecent_currentItemChanged( QTreeWidgetItem *current, QTreeWidgetItem *prev );
void on_cbxHideDeprecated_stateChanged();
void on_leSearch_textChanged( const QString & );

Expand Down Expand Up @@ -148,7 +146,7 @@ class GUI_EXPORT QgsProjectionSelector: public QWidget, private Ui::QgsProjectio
* does not scroll the list to the selection if the widget is not visible.
* Therefore you will typically want to use this in a showEvent().
*/
void applySelection();
void applySelection( int column = NONE, QString value = QString::null );

/**
* \brief gets an arbitrary sqlite3 expression from the selection
Expand Down Expand Up @@ -189,23 +187,9 @@ class GUI_EXPORT QgsProjectionSelector: public QWidget, private Ui::QgsProjectio
//! Has the Recent Projection List been populated?
bool mRecentProjListDone;

//! Is there a pending selection to be made by CRS Name?
bool mCRSNameSelectionPending;

//! Is there a pending selection to be made by CRS ID?
bool mCRSIDSelectionPending;

//! Is there a pending selection to be made by Authority ID?
bool mAuthIDSelectionPending;

//! The CRS Name that wants to be selected on this widget
QString mCRSNameSelection;

//! The CRS ID that wants to be selected on this widget
long mCRSIDSelection;

//! The Authority ID that wants to be selected on this widget
QString mAuthIDSelection;
enum columns { NAME_COLUMN, AUTHID_COLUMN, QGIS_CRS_ID_COLUMN, NONE };
int mSearchColumn;
QString mSearchValue;

//! The set of OGC WMS CRSs that want to be applied to this widget
QSet<QString> mCrsFilter;
Expand All @@ -217,11 +201,6 @@ class GUI_EXPORT QgsProjectionSelector: public QWidget, private Ui::QgsProjectio
void hideDeprecated( QTreeWidgetItem *item );

private slots:
/**private handler for when user selects a cs
*it will cause wktSelected and sridSelected events to be spawned
*/
void coordinateSystemSelected( QTreeWidgetItem* );

//! get list of authorities
QStringList authorities();

Expand Down