QGIS/src/gui/qgsprojectionselector.cpp
mhugent 8f938dd20a Merge pull request #1267 from mwa/fix_8298_crs_select
Fix #8298 ignored first selection in projection selector
2014-03-31 08:03:12 +02:00

1007 lines
33 KiB
C++

/***************************************************************************
* qgsprojectionselector.cpp *
* Copyright (C) 2005 by Tim Sutton *
* tim@linfiniti.com *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
***************************************************************************/
#include <qgsprojectionselector.h>
//standard includes
#include <sqlite3.h>
//qgis includes
#include "qgis.h" //magic numbers here
#include "qgsapplication.h"
#include "qgslogger.h"
#include "qgscoordinatereferencesystem.h"
//qt includes
#include <QFileInfo>
#include <QHeaderView>
#include <QResizeEvent>
#include <QMessageBox>
#include <QSettings>
QgsProjectionSelector::QgsProjectionSelector( QWidget* parent, const char *name, Qt::WindowFlags fl )
: QWidget( parent, fl )
, mProjListDone( false )
, mUserProjListDone( false )
, mRecentProjListDone( false )
, mSearchColumn( NONE )
, mPushProjectionToFront( false )
{
Q_UNUSED( name );
setupUi( this );
if ( qobject_cast<QDialog*>( parent ) )
{
// mark selected projection for push to front if parent dialog is accepted
connect( parent, SIGNAL( accepted() ), this, SLOT( pushProjectionToFront() ) );
}
// 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 );
// Read settings from persistent storage
QSettings settings;
mRecentProjections = settings.value( "/UI/recentProjections" ).toStringList();
/*** The reading (above) of internal id from persistent storage should be removed sometime in the future */
/*** This is kept now for backwards compatibility */
QStringList projectionsProj4 = settings.value( "/UI/recentProjectionsProj4" ).toStringList();
QStringList projectionsAuthId = settings.value( "/UI/recentProjectionsAuthId" ).toStringList();
if ( projectionsAuthId.size() >= mRecentProjections.size() )
{
// We had saved state with AuthId and Proj4. Use that instead
// to find out the crs id
QgsDebugMsg( "Use popular projection list from AuthId/Proj4 saved state" );
mRecentProjections.clear();
for ( int i = 0; i < projectionsAuthId.size(); i++ )
{
// Create a crs from the EPSG
QgsCoordinateReferenceSystem crs;
crs.createFromOgcWmsCrs( projectionsAuthId.at( i ) );
if ( ! crs.isValid() )
{
// Couldn't create from EPSG, try the Proj4 string instead
if ( i >= projectionsProj4.size() || !crs.createFromProj4( projectionsProj4.at( i ) ) )
{
// No? Skip this entry
continue;
}
//If the CRS can be created but do not correspond to a CRS in the database, skip it (for example a deleted custom CRS)
if ( crs.srsid() == 0 )
{
continue;
}
}
mRecentProjections << QString::number( crs.srsid() );
}
}
}
QgsProjectionSelector::~QgsProjectionSelector()
{
if ( !mPushProjectionToFront )
{
return;
}
// Push current projection to front, only if set
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.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
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() )
{
// No? Skip this entry
continue;
}
projectionsProj4 << crs.toProj4();
projectionsAuthId << crs.authid();
}
settings.setValue( "/UI/recentProjectionsProj4", projectionsProj4 );
settings.setValue( "/UI/recentProjectionsAuthId", projectionsAuthId );
}
void QgsProjectionSelector::resizeEvent( QResizeEvent * theEvent )
{
lstCoordinateSystems->header()->resizeSection( NAME_COLUMN, theEvent->size().width() - 240 );
lstCoordinateSystems->header()->resizeSection( AUTHID_COLUMN, 240 );
lstCoordinateSystems->header()->resizeSection( QGIS_CRS_ID_COLUMN, 0 );
lstRecent->header()->resizeSection( NAME_COLUMN, theEvent->size().width() - 240 );
lstRecent->header()->resizeSection( AUTHID_COLUMN, 240 );
lstRecent->header()->resizeSection( QGIS_CRS_ID_COLUMN, 0 );
}
void QgsProjectionSelector::showEvent( QShowEvent * theEvent )
{
// ensure the projection list view is actually populated
// before we show this widget
loadCrsList( &mCrsFilter );
loadUserCrsList( &mCrsFilter );
if ( !mRecentProjListDone )
{
for ( int i = mRecentProjections.size() - 1; i >= 0; i-- )
insertRecent( mRecentProjections.at( i ).toLong() );
mRecentProjListDone = true;
}
// apply deferred selection
applySelection();
// Pass up the inheritance hierarchy
QWidget::showEvent( theEvent );
}
QString QgsProjectionSelector::ogcWmsCrsFilterAsSqlExpression( QSet<QString> * crsFilter )
{
QString sqlExpression = "1"; // it's "SQL" for "true"
QMap<QString, QStringList> authParts;
if ( !crsFilter )
return sqlExpression;
/*
Ref: WMS 1.3.0, section 6.7.3 "Layer CRS":
Every Layer CRS has an identifier that is a character string. Two types of
Layer CRS identifiers are permitted: "label" and "URL" identifiers:
Label: The identifier includes a namespace prefix, a colon, a numeric or
string code, and in some instances a comma followed by additional
parameters. This International Standard defines three namespaces:
CRS, EpsgCrsId and AUTO2 [...]
URL: The identifier is a fully-qualified Uniform Resource Locator that
references a publicly-accessible file containing a definition of the CRS
that is compliant with ISO 19111.
*/
// iterate through all incoming CRSs
foreach ( QString auth_id, crsFilter->values() )
{
QStringList parts = auth_id.split( ":" );
if ( parts.size() < 2 )
continue;
authParts[ parts.at( 0 ).toUpper()].append( parts.at( 1 ).toUpper() );
}
if ( authParts.isEmpty() )
return sqlExpression;
if ( authParts.size() > 0 )
{
QString prefix = " AND (";
foreach ( QString auth_name, authParts.keys() )
{
sqlExpression += QString( "%1(upper(auth_name)='%2' AND upper(auth_id) IN ('%3'))" )
.arg( prefix )
.arg( auth_name )
.arg( authParts[auth_name].join( "','" ) );
prefix = " OR ";
}
sqlExpression += ")";
}
QgsDebugMsg( "exiting with '" + sqlExpression + "'." );
return sqlExpression;
}
void QgsProjectionSelector::setSelectedCrsName( QString theCRSName )
{
applySelection( NAME_COLUMN, theCRSName );
}
void QgsProjectionSelector::setSelectedCrsId( long theCRSID )
{
applySelection( QGIS_CRS_ID_COLUMN, QString::number( theCRSID ) );
}
void QgsProjectionSelector::setSelectedAuthId( QString id )
{
applySelection( AUTHID_COLUMN, id );
}
void QgsProjectionSelector::applySelection( int column, QString value )
{
if ( !mProjListDone || !mUserProjListDone )
{
// defer selection until loaded
mSearchColumn = column;
mSearchValue = value;
return;
}
if ( column == NONE )
{
// invoked deferred selection
column = mSearchColumn;
value = mSearchValue;
mSearchColumn = NONE;
mSearchValue.clear();
}
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() );
}
else
{
QgsDebugMsg( QString( "nothing found for %1,%2" ).arg( column ).arg( value ) );
// unselect the selected item to avoid confusing the user
lstCoordinateSystems->clearSelection();
lstRecent->clearSelection();
teProjection->setText( "" );
teSelected->setText( "" );
}
}
void QgsProjectionSelector::insertRecent( long theCrsId )
{
if ( !mProjListDone || !mUserProjListDone )
return;
QList<QTreeWidgetItem*> nodes = lstCoordinateSystems->findItems( QString::number( theCrsId ), Qt::MatchExactly | Qt::MatchRecursive, QGIS_CRS_ID_COLUMN );
if ( nodes.count() == 0 )
return;
lstRecent->insertTopLevelItem( 0, new QTreeWidgetItem( lstRecent, QStringList()
<< nodes.first()->text( NAME_COLUMN )
<< nodes.first()->text( AUTHID_COLUMN )
<< nodes.first()->text( QGIS_CRS_ID_COLUMN ) ) );
}
//note this line just returns the projection name!
QString QgsProjectionSelector::selectedName()
{
// return the selected wkt name from the list view
QTreeWidgetItem *lvi = lstCoordinateSystems->currentItem();
return lvi ? lvi->text( NAME_COLUMN ) : QString::null;
}
// Returns the whole proj4 string for the selected projection node
QString QgsProjectionSelector::selectedProj4String()
{
// Only return the projection if there is a node in the tree
// selected that has an srid. This prevents error if the user
// selects a top-level node rather than an actual coordinate
// system
//
// Get the selected node
QTreeWidgetItem *item = lstCoordinateSystems->currentItem();
if ( !item || item->text( QGIS_CRS_ID_COLUMN ).isEmpty() )
return "";
QString srsId = item->text( QGIS_CRS_ID_COLUMN );
QgsDebugMsg( "srsId = " + srsId );
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
//
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( "db = " + databaseFileName );
sqlite3 *database;
int rc = sqlite3_open_v2( databaseFileName.toUtf8().data(), &database, SQLITE_OPEN_READONLY, NULL );
if ( rc )
{
showDBMissingWarning( databaseFileName );
return "";
}
// 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 )
{
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 )
{
// Only return the attribute if there is a node in the tree
// selected that has an srs_id. This prevents error if the user
// selects a top-level node rather than an actual coordinate
// system
//
// Get the selected node and make sure it is a srs andx
// not a top-level projection node
QTreeWidgetItem *lvi = lstCoordinateSystems->currentItem();
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 )
{
databaseFileName = QgsApplication::qgisUserDbFilePath();
if ( !QFileInfo( databaseFileName ).exists() )
{
return 0;
}
}
else
{
databaseFileName = 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 *database;
int rc = sqlite3_open_v2( databaseFileName.toUtf8().data(), &database, SQLITE_OPEN_READONLY, NULL );
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::selectedPostgresSrId()
{
return getSelectedExpression( "srid" ).toLong();
}
QString QgsProjectionSelector::selectedAuthId()
{
int srid = getSelectedExpression( "srs_id" ).toLong();
if ( srid >= USER_CRS_START_ID )
return QString( "USER:%1" ).arg( srid );
else
return getSelectedExpression( "upper(auth_name||':'||auth_id)" );
}
long QgsProjectionSelector::selectedCrsId()
{
QTreeWidgetItem* item = lstCoordinateSystems->currentItem();
if ( item && !item->text( QGIS_CRS_ID_COLUMN ).isEmpty() )
return lstCoordinateSystems->currentItem()->text( QGIS_CRS_ID_COLUMN ).toLong();
else
return 0;
}
void QgsProjectionSelector::setOgcWmsCrsFilter( QSet<QString> crsFilter )
{
mCrsFilter = crsFilter;
mProjListDone = false;
mUserProjListDone = false;
lstCoordinateSystems->clear();
}
void QgsProjectionSelector::loadUserCrsList( QSet<QString> *crsFilter )
{
if ( mUserProjListDone )
return;
QgsDebugMsg( "Fetching user projection list..." );
// convert our Coordinate Reference System filter into the SQL expression
QString sqlFilter = ogcWmsCrsFilterAsSqlExpression( crsFilter );
// User defined coordinate system node
// Make in an italic font to distinguish them from real projections
mUserProjList = new QTreeWidgetItem( lstCoordinateSystems, QStringList( tr( "User Defined Coordinate Systems" ) ) );
QFont fontTemp = mUserProjList->font( 0 );
fontTemp.setItalic( true );
fontTemp.setBold( true );
mUserProjList->setFont( 0, fontTemp );
mUserProjList->setIcon( 0, QIcon( QgsApplication::activeThemePath() + "user.png" ) );
//determine where the user proj database lives for this user. If none is found an empty
//now only will be shown
QString databaseFileName = QgsApplication::qgisUserDbFilePath();
// first we look for ~/.qgis/qgis.db
// if it doesnt exist we copy it in from the global resources dir
//return straight away if the user has not created any custom projections
if ( !QFileInfo( databaseFileName ).exists( ) )
{
QgsDebugMsg( "Users qgis.db not found...skipping" );
mUserProjListDone = true;
return;
}
sqlite3 *database;
const char *tail;
sqlite3_stmt *stmt;
//check the db is available
int result = sqlite3_open_v2( databaseFileName.toUtf8().constData(), &database, SQLITE_OPEN_READONLY, NULL );
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( databaseFileName );
return;
}
// Set up the query to retrieve the projection information needed to populate the list
QString sql = QString( "select description, srs_id from vw_srs where %1" ).arg( sqlFilter );
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 ( result == SQLITE_OK )
{
QTreeWidgetItem *newItem;
while ( sqlite3_step( stmt ) == SQLITE_ROW )
{
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( 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( stmt, 1 ) ) );
newItem->setText( AUTHID_COLUMN, QString( "USER:%1" ).arg( QString::fromUtf8(( char * )sqlite3_column_text( stmt, 1 ) ).toInt() ) );
}
}
// close the sqlite3 statement
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 );
// Create the top-level nodes for the list view of projections
// Make in an italic font to distinguish them from real projections
//
// Geographic coordinate system node
mGeoList = new QTreeWidgetItem( lstCoordinateSystems, QStringList( tr( "Geographic Coordinate Systems" ) ) );
QFont fontTemp = mGeoList->font( 0 );
fontTemp.setItalic( true );
fontTemp.setBold( true );
mGeoList->setFont( 0, fontTemp );
mGeoList->setIcon( 0, QIcon( QgsApplication::activeThemePath() + "geographic.png" ) );
// Projected coordinate system node
mProjList = new QTreeWidgetItem( lstCoordinateSystems, QStringList( tr( "Projected Coordinate Systems" ) ) );
fontTemp = mProjList->font( 0 );
fontTemp.setItalic( true );
fontTemp.setBold( true );
mProjList->setFont( 0, fontTemp );
mProjList->setIcon( 0, QIcon( QgsApplication::activeThemePath() + "transformed.png" ) );
//bail out in case the projections db does not exist
//this is necessary in case the pc is running linux with a
//read only filesystem because otherwise sqlite will try
//to create the db file on the fly
if ( !QFileInfo( mSrsDatabaseFileName ).exists() )
{
mProjListDone = true;
return;
}
// open the database containing the spatial reference data
sqlite3 *database;
int rc = sqlite3_open_v2( mSrsDatabaseFileName.toUtf8().data(), &database, SQLITE_OPEN_READONLY, NULL );
if ( rc )
{
// XXX This will likely never happen since on open, sqlite creates the
// database if it does not exist.
showDBMissingWarning( mSrsDatabaseFileName );
return;
}
// prepare the sql statement
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( 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( 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 = 0;
while ( sqlite3_step( stmt ) == SQLITE_ROW )
{
// check to see if the srs is geographic
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( 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( 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( stmt, 1 ) ) );
}
else
{
// This is a projected srs
QTreeWidgetItem *node;
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 )
{
node = previousSrsTypeNode;
}
else
{ // Different from last one, need to search
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 );
}
else
{
node = nodes.first();
}
// Update the cache.
previousSrsType = srsType;
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( 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( 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( 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( stmt, 6 ) ) );
newItem->setHidden( cbxHideDeprecated->isChecked() );
}
mProjList->setExpanded( true );
}
// close the sqlite3 statement
sqlite3_finalize( stmt );
// close the database
sqlite3_close( database );
mProjListDone = true;
}
// New coordinate system selected from the list
void QgsProjectionSelector::on_lstCoordinateSystems_currentItemChanged( QTreeWidgetItem *current, QTreeWidgetItem * )
{
QgsDebugMsg( "Entered." );
if ( !current )
{
QgsDebugMsg( "no current item" );
return;
}
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 ( current->childCount() == 0 )
{
// Found a real CRS
emit sridSelected( QString::number( selectedCrsId() ) );
teProjection->setText( selectedProj4String() );
teSelected->setText( selectedName() );
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();
lstCoordinateSystems->setFocus( Qt::OtherFocusReason );
}
}
else
{
// Not an CRS - remove the highlight so the user doesn't get too confused
current->setSelected( false );
teProjection->setText( "" );
teSelected->setText( "" );
lstRecent->clearSelection();
}
}
void QgsProjectionSelector::on_lstRecent_currentItemChanged( QTreeWidgetItem *current, QTreeWidgetItem * )
{
QgsDebugMsg( "Entered." );
if ( !current )
{
QgsDebugMsg( "no current item" );
return;
}
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() )
{
item->setHidden( cbxHideDeprecated->isChecked() );
if ( item->isSelected() && item->isHidden() )
{
item->setSelected( false );
teProjection->setText( "" );
teSelected->setText( "" );
}
}
for ( int i = 0; i < item->childCount(); i++ )
hideDeprecated( item->child( i ) );
}
void QgsProjectionSelector::on_cbxHideDeprecated_stateChanged()
{
for ( int i = 0; i < lstCoordinateSystems->topLevelItemCount(); i++ )
hideDeprecated( lstCoordinateSystems->topLevelItem( i ) );
}
void QgsProjectionSelector::on_leSearch_textChanged( const QString & theFilterTxt )
{
QString filterTxt = theFilterTxt;
filterTxt.replace( QRegExp( "\\s+" ), ".*" );
QRegExp re( filterTxt, Qt::CaseInsensitive );
// filter recent crs's
QTreeWidgetItemIterator itr( lstRecent );
while ( *itr )
{
if (( *itr )->childCount() == 0 ) // it's an end node aka a projection
{
if (( *itr )->text( NAME_COLUMN ).contains( re )
|| ( *itr )->text( AUTHID_COLUMN ).contains( re )
)
{
( *itr )->setHidden( false );
QTreeWidgetItem * parent = ( *itr )->parent();
while ( parent )
{
parent->setExpanded( true );
parent->setHidden( false );
parent = parent->parent();
}
}
else
{
( *itr )->setHidden( true );
}
}
else
{
( *itr )->setHidden( true );
}
++itr;
}
// filter crs's
QTreeWidgetItemIterator it( lstCoordinateSystems );
while ( *it )
{
if (( *it )->childCount() == 0 ) // it's an end node aka a projection
{
if (( *it )->text( NAME_COLUMN ).contains( re )
|| ( *it )->text( AUTHID_COLUMN ).contains( re )
)
{
( *it )->setHidden( false );
QTreeWidgetItem * parent = ( *it )->parent();
while ( parent )
{
parent->setExpanded( true );
parent->setHidden( false );
parent = parent->parent();
}
}
else
{
( *it )->setHidden( true );
}
}
else
{
( *it )->setHidden( true );
}
++it;
}
}
void QgsProjectionSelector::pushProjectionToFront()
{
// set flag to push selected projection to front in destructor
mPushProjectionToFront = true;
}
long QgsProjectionSelector::getLargestCRSIDMatch( QString theSql )
{
long srsId = 0;
//
// Now perform the actual search
//
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 databaseFileName = QgsApplication::qgisUserDbFilePath();
if ( QFileInfo( databaseFileName ).exists() ) //only bother trying to open if the file exists
{
result = sqlite3_open_v2( databaseFileName.toUtf8().data(), &database, SQLITE_OPEN_READONLY, NULL );
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( databaseFileName );
return 0;
}
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 )
{
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;
}
}
else
{
//only bother looking in srs.db if it wasnt found above
result = sqlite3_open_v2( mSrsDatabaseFileName.toUtf8().data(), &database, SQLITE_OPEN_READONLY, NULL );
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;
}
}
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 )
{
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;
}
QStringList QgsProjectionSelector::authorities()
{
sqlite3 *database;
const char *tail;
sqlite3_stmt *stmt;
int result = sqlite3_open_v2( mSrsDatabaseFileName.toUtf8().data(), &database, SQLITE_OPEN_READONLY, NULL );
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 QStringList();
}
QString theSql = "select distinct auth_name from tbl_srs";
result = sqlite3_prepare( database, theSql.toUtf8(), theSql.toUtf8().length(), &stmt, &tail );
QStringList authorities;
if ( result == SQLITE_OK )
{
while ( sqlite3_step( stmt ) == SQLITE_ROW )
{
authorities << QString::fromUtf8(( char * )sqlite3_column_text( stmt, 0 ) );
}
}
// close the sqlite3 statement
sqlite3_finalize( stmt );
sqlite3_close( database );
return authorities;
}
/*!linfinity qtcreator qgis
* \brief Make the string safe for use in SQL statements.
* This involves escaping single quotes, double quotes, backslashes,
* and optionally, percentage symbols. Percentage symbols are used
* as wildcards sometimes and so when using the string as part of the
* LIKE phrase of a select statement, should be escaped.
* \arg const QString in The input string to make safe.
* \return The string made safe for SQL statements.
*/
const QString QgsProjectionSelector::sqlSafeString( const QString theSQL )
{
QString retval = theSQL;
retval.replace( "\\", "\\\\" );
retval.replace( '\"', "\\\"" );
retval.replace( "\'", "\\'" );
retval.replace( "%", "\\%" );
return retval;
}
void QgsProjectionSelector::showDBMissingWarning( const QString theFileName )
{
QMessageBox::critical( this, tr( "Resource Location Error" ),
tr( "Error reading database file from: \n %1\n"
"Because of this the projection selector will not work..." )
.arg( theFileName ) );
}