QGIS API Documentation  2.2.0-Valmiera
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsprojectionselector.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  * qgsprojectionselector.cpp *
3  * Copyright (C) 2005 by Tim Sutton *
5  * *
6  * This program is free software; you can redistribute it and/or modify *
7  * it under the terms of the GNU General Public License as published by *
8  * the Free Software Foundation; either version 2 of the License, or *
9  * (at your option) any later version. *
10  ***************************************************************************/
11 #include <qgsprojectionselector.h>
12 
13 //standard includes
14 #include <sqlite3.h>
15 
16 //qgis includes
17 #include "qgis.h" //magic numbers here
18 #include "qgsapplication.h"
19 #include "qgslogger.h"
21 
22 //qt includes
23 #include <QFileInfo>
24 #include <QHeaderView>
25 #include <QResizeEvent>
26 #include <QMessageBox>
27 #include <QSettings>
28 
29 QgsProjectionSelector::QgsProjectionSelector( QWidget* parent, const char *name, Qt::WFlags fl )
30  : QWidget( parent, fl )
31  , mProjListDone( false )
32  , mUserProjListDone( false )
33  , mRecentProjListDone( false )
34  , mSearchColumn( NONE )
35  , mSkipFirstRecent( true )
36 {
37  Q_UNUSED( name );
38  setupUi( this );
39 
40  // Get the full path name to the sqlite3 spatial reference database.
42 
43  lstCoordinateSystems->header()->setResizeMode( AUTHID_COLUMN, QHeaderView::Stretch );
44  lstCoordinateSystems->header()->resizeSection( QGIS_CRS_ID_COLUMN, 0 );
45  lstCoordinateSystems->header()->setResizeMode( QGIS_CRS_ID_COLUMN, QHeaderView::Fixed );
46 
47  // Hide (internal) ID column
48  lstCoordinateSystems->setColumnHidden( QGIS_CRS_ID_COLUMN, true );
49 
50  lstRecent->header()->setResizeMode( AUTHID_COLUMN, QHeaderView::Stretch );
51  lstRecent->header()->resizeSection( QGIS_CRS_ID_COLUMN, 0 );
52  lstRecent->header()->setResizeMode( QGIS_CRS_ID_COLUMN, QHeaderView::Fixed );
53 
54  // Hide (internal) ID column
55  lstRecent->setColumnHidden( QGIS_CRS_ID_COLUMN, true );
56 
57  // Read settings from persistent storage
58  QSettings settings;
59  mRecentProjections = settings.value( "/UI/recentProjections" ).toStringList();
60  /*** The reading (above) of internal id from persistent storage should be removed sometime in the future */
61  /*** This is kept now for backwards compatibility */
62 
63  QStringList projectionsProj4 = settings.value( "/UI/recentProjectionsProj4" ).toStringList();
64  QStringList projectionsAuthId = settings.value( "/UI/recentProjectionsAuthId" ).toStringList();
65  if ( projectionsAuthId.size() >= mRecentProjections.size() )
66  {
67  // We had saved state with AuthId and Proj4. Use that instead
68  // to find out the crs id
69  QgsDebugMsg( "Use popular projection list from AuthId/Proj4 saved state" );
70  mRecentProjections.clear();
71  for ( int i = 0; i < projectionsAuthId.size(); i++ )
72  {
73  // Create a crs from the EPSG
75  crs.createFromOgcWmsCrs( projectionsAuthId.at( i ) );
76  if ( ! crs.isValid() )
77  {
78  // Couldn't create from EPSG, try the Proj4 string instead
79  if ( i >= projectionsProj4.size() || !crs.createFromProj4( projectionsProj4.at( i ) ) )
80  {
81  // No? Skip this entry
82  continue;
83  }
84  //If the CRS can be created but do not correspond to a CRS in the database, skip it (for example a deleted custom CRS)
85  if ( crs.srsid() == 0 )
86  {
87  continue;
88  }
89  }
90  mRecentProjections << QString::number( crs.srsid() );
91  }
92  }
93 }
94 
96 {
97  // Push current projection to front, only if set
98  long crsId = selectedCrsId();
99  if ( crsId == 0 )
100  return;
101 
102  // Save persistent list of projects
103  mRecentProjections.removeAll( QString::number( crsId ) );
104  mRecentProjections.prepend( QString::number( crsId ) );
105  // Prune size of list
106  while ( mRecentProjections.size() > 8 )
107  {
108  mRecentProjections.removeLast();
109  }
110 
111  // Save to file *** Should be removed sometims in the future ***
112  QSettings settings;
113  settings.setValue( "/UI/recentProjections", mRecentProjections );
114 
115  // Convert to EPSG and proj4, and save those values also
116 
117  QStringList projectionsProj4;
118  QStringList projectionsAuthId;
119  for ( int i = 0; i < mRecentProjections.size(); i++ )
120  {
121  // Create a crs from the crsId
123  if ( ! crs.isValid() )
124  {
125  // No? Skip this entry
126  continue;
127  }
128  projectionsProj4 << crs.toProj4();
129  projectionsAuthId << crs.authid();
130  }
131  settings.setValue( "/UI/recentProjectionsProj4", projectionsProj4 );
132  settings.setValue( "/UI/recentProjectionsAuthId", projectionsAuthId );
133 }
134 
135 void QgsProjectionSelector::resizeEvent( QResizeEvent * theEvent )
136 {
137  lstCoordinateSystems->header()->resizeSection( NAME_COLUMN, theEvent->size().width() - 240 );
138  lstCoordinateSystems->header()->resizeSection( AUTHID_COLUMN, 240 );
139  lstCoordinateSystems->header()->resizeSection( QGIS_CRS_ID_COLUMN, 0 );
140 
141  lstRecent->header()->resizeSection( NAME_COLUMN, theEvent->size().width() - 240 );
142  lstRecent->header()->resizeSection( AUTHID_COLUMN, 240 );
143  lstRecent->header()->resizeSection( QGIS_CRS_ID_COLUMN, 0 );
144 }
145 
146 void QgsProjectionSelector::showEvent( QShowEvent * theEvent )
147 {
148  // ensure the projection list view is actually populated
149  // before we show this widget
152 
153  if ( !mRecentProjListDone )
154  {
155  for ( int i = mRecentProjections.size() - 1; i >= 0; i-- )
156  insertRecent( mRecentProjections.at( i ).toLong() );
157  mRecentProjListDone = true;
158  }
159 
160  // apply deferred selection
161  applySelection();
162 
163  // Pass up the inheritance hierarchy
164  QWidget::showEvent( theEvent );
165 }
166 
167 QString QgsProjectionSelector::ogcWmsCrsFilterAsSqlExpression( QSet<QString> * crsFilter )
168 {
169  QString sqlExpression = "1"; // it's "SQL" for "true"
170  QMap<QString, QStringList> authParts;
171 
172  if ( !crsFilter )
173  return sqlExpression;
174 
175  /*
176  Ref: WMS 1.3.0, section 6.7.3 "Layer CRS":
177 
178  Every Layer CRS has an identifier that is a character string. Two types of
179  Layer CRS identifiers are permitted: "label" and "URL" identifiers:
180 
181  Label: The identifier includes a namespace prefix, a colon, a numeric or
182  string code, and in some instances a comma followed by additional
183  parameters. This International Standard defines three namespaces:
184  CRS, EpsgCrsId and AUTO2 [...]
185 
186  URL: The identifier is a fully-qualified Uniform Resource Locator that
187  references a publicly-accessible file containing a definition of the CRS
188  that is compliant with ISO 19111.
189  */
190 
191  // iterate through all incoming CRSs
192 
193  foreach ( QString auth_id, crsFilter->values() )
194  {
195  QStringList parts = auth_id.split( ":" );
196 
197  if ( parts.size() < 2 )
198  continue;
199 
200  authParts[ parts.at( 0 ).toUpper()].append( parts.at( 1 ).toUpper() );
201  }
202 
203  if ( authParts.isEmpty() )
204  return sqlExpression;
205 
206  if ( authParts.size() > 0 )
207  {
208  QString prefix = " AND (";
209  foreach ( QString auth_name, authParts.keys() )
210  {
211  sqlExpression += QString( "%1(upper(auth_name)='%2' AND upper(auth_id) IN ('%3'))" )
212  .arg( prefix )
213  .arg( auth_name )
214  .arg( authParts[auth_name].join( "','" ) );
215  prefix = " OR ";
216  }
217  sqlExpression += ")";
218  }
219 
220  QgsDebugMsg( "exiting with '" + sqlExpression + "'." );
221 
222  return sqlExpression;
223 }
224 
226 {
227  applySelection( NAME_COLUMN, theCRSName );
228 }
229 
231 {
232  applySelection( QGIS_CRS_ID_COLUMN, QString::number( theCRSID ) );
233 }
234 
236 {
238 }
239 
240 void QgsProjectionSelector::applySelection( int column, QString value )
241 {
242  if ( !mProjListDone || !mUserProjListDone )
243  {
244  // defer selection until loaded
245  mSearchColumn = column;
246  mSearchValue = value;
247  return;
248  }
249 
250  if ( column == NONE )
251  {
252  // invoked deferred selection
253  column = mSearchColumn;
254  value = mSearchValue;
255 
257  mSearchValue.clear();
258  }
259 
260  if ( column == NONE )
261  return;
262 
263  QList<QTreeWidgetItem*> nodes = lstCoordinateSystems->findItems( value, Qt::MatchExactly | Qt::MatchRecursive, column );
264  if ( nodes.count() > 0 )
265  {
266  QgsDebugMsg( QString( "found %1,%2" ).arg( column ).arg( value ) );
267  lstCoordinateSystems->setCurrentItem( nodes.first() );
268  }
269  else
270  {
271  QgsDebugMsg( QString( "nothing found for %1,%2" ).arg( column ).arg( value ) );
272  // unselect the selected item to avoid confusing the user
273  lstCoordinateSystems->clearSelection();
274  lstRecent->clearSelection();
275  teProjection->setText( "" );
276  teSelected->setText( "" );
277  }
278 }
279 
281 {
282  if ( !mProjListDone || !mUserProjListDone )
283  return;
284 
285  QList<QTreeWidgetItem*> nodes = lstCoordinateSystems->findItems( QString::number( theCrsId ), Qt::MatchExactly | Qt::MatchRecursive, QGIS_CRS_ID_COLUMN );
286  if ( nodes.count() == 0 )
287  return;
288 
289  lstRecent->insertTopLevelItem( 0, new QTreeWidgetItem( lstRecent, QStringList()
290  << nodes.first()->text( NAME_COLUMN )
291  << nodes.first()->text( AUTHID_COLUMN )
292  << nodes.first()->text( QGIS_CRS_ID_COLUMN ) ) );
293 }
294 
295 //note this line just returns the projection name!
297 {
298  // return the selected wkt name from the list view
299  QTreeWidgetItem *lvi = lstCoordinateSystems->currentItem();
300  return lvi ? lvi->text( NAME_COLUMN ) : QString::null;
301 }
302 
303 // Returns the whole proj4 string for the selected projection node
305 {
306  // Only return the projection if there is a node in the tree
307  // selected that has an srid. This prevents error if the user
308  // selects a top-level node rather than an actual coordinate
309  // system
310  //
311  // Get the selected node
312  QTreeWidgetItem *item = lstCoordinateSystems->currentItem();
313  if ( !item || item->text( QGIS_CRS_ID_COLUMN ).isEmpty() )
314  return "";
315 
316  QString srsId = item->text( QGIS_CRS_ID_COLUMN );
317 
318  QgsDebugMsg( "srsId = " + srsId );
319  QgsDebugMsg( "USER_CRS_START_ID = " + QString::number( USER_CRS_START_ID ) );
320 
321  //
322  // Determine if this is a user projection or a system on
323  // user projection defs all have srs_id >= 100000
324  //
325  QString databaseFileName;
326  if ( srsId.toLong() >= USER_CRS_START_ID )
327  {
328  databaseFileName = QgsApplication::qgisUserDbFilePath();
329  if ( !QFileInfo( databaseFileName ).exists() ) //its unlikely that this condition will ever be reached
330  return QString( "" );
331  }
332  else //must be a system projection then
333  {
334  databaseFileName = mSrsDatabaseFileName;
335  }
336 
337  QgsDebugMsg( "db = " + databaseFileName );
338 
339  sqlite3 *database;
340  int rc = sqlite3_open_v2( databaseFileName.toUtf8().data(), &database, SQLITE_OPEN_READONLY, NULL );
341  if ( rc )
342  {
343  showDBMissingWarning( databaseFileName );
344  return "";
345  }
346 
347  // prepare the sql statement
348  const char *tail;
349  sqlite3_stmt *stmt;
350  QString sql = QString( "select parameters from tbl_srs where srs_id=%1" ).arg( srsId );
351 
352  QgsDebugMsg( "Selection sql: " + sql );
353 
354  rc = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail );
355  // XXX Need to free memory from the error msg if one is set
356  QString projString;
357  if ( rc == SQLITE_OK && sqlite3_step( stmt ) == SQLITE_ROW )
358  {
359  projString = QString::fromUtf8(( char * )sqlite3_column_text( stmt, 0 ) );
360  }
361 
362  // close the statement
363  sqlite3_finalize( stmt );
364  // close the database
365  sqlite3_close( database );
366 
367  Q_ASSERT( !projString.isEmpty() );
368 
369  return projString;
370 }
371 
372 QString QgsProjectionSelector::getSelectedExpression( QString expression )
373 {
374  // Only return the attribute if there is a node in the tree
375  // selected that has an srs_id. This prevents error if the user
376  // selects a top-level node rather than an actual coordinate
377  // system
378  //
379  // Get the selected node and make sure it is a srs andx
380  // not a top-level projection node
381  QTreeWidgetItem *lvi = lstCoordinateSystems->currentItem();
382  if ( !lvi || lvi->text( QGIS_CRS_ID_COLUMN ).isEmpty() )
383  return 0;
384 
385  //
386  // Determine if this is a user projection or a system on
387  // user projection defs all have srs_id >= 100000
388  //
389  QString databaseFileName;
390  if ( lvi->text( QGIS_CRS_ID_COLUMN ).toLong() >= USER_CRS_START_ID )
391  {
392  databaseFileName = QgsApplication::qgisUserDbFilePath();
393  if ( !QFileInfo( databaseFileName ).exists() )
394  {
395  return 0;
396  }
397  }
398  else
399  {
400  databaseFileName = mSrsDatabaseFileName;
401  }
402 
403  //
404  // set up the database
405  // XXX We could probabaly hold the database open for the life of this object,
406  // assuming that it will never be used anywhere else. Given the low overhead,
407  // opening it each time seems to be a reasonable approach at this time.
408  sqlite3 *database;
409  int rc = sqlite3_open_v2( databaseFileName.toUtf8().data(), &database, SQLITE_OPEN_READONLY, NULL );
410  if ( rc )
411  {
412  showDBMissingWarning( databaseFileName );
413  return 0;
414  }
415 
416  // prepare the sql statement
417  const char *tail;
418  sqlite3_stmt *stmt;
419  QString sql = QString( "select %1 from tbl_srs where srs_id=%2" )
420  .arg( expression )
421  .arg( lvi->text( QGIS_CRS_ID_COLUMN ) );
422 
423  QgsDebugMsg( QString( "Finding selected attribute using : %1" ).arg( sql ) );
424  rc = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail );
425  // XXX Need to free memory from the error msg if one is set
426  QString attributeValue;
427  if ( rc == SQLITE_OK && sqlite3_step( stmt ) == SQLITE_ROW )
428  {
429  // get the first row of the result set
430  attributeValue = QString::fromUtf8(( char * )sqlite3_column_text( stmt, 0 ) );
431  }
432 
433  // close the statement
434  sqlite3_finalize( stmt );
435  // close the database
436  sqlite3_close( database );
437 
438  // return the srs
439  return attributeValue;
440 }
441 
442 
444 {
445  return getSelectedExpression( "srid" ).toLong();
446 }
447 
448 
450 {
451  int srid = getSelectedExpression( "srs_id" ).toLong();
452  if ( srid >= USER_CRS_START_ID )
453  return QString( "USER:%1" ).arg( srid );
454  else
455  return getSelectedExpression( "upper(auth_name||':'||auth_id)" );
456 }
457 
458 
460 {
461  QTreeWidgetItem* item = lstCoordinateSystems->currentItem();
462 
463  if ( item && !item->text( QGIS_CRS_ID_COLUMN ).isEmpty() )
464  return lstCoordinateSystems->currentItem()->text( QGIS_CRS_ID_COLUMN ).toLong();
465  else
466  return 0;
467 }
468 
469 
470 void QgsProjectionSelector::setOgcWmsCrsFilter( QSet<QString> crsFilter )
471 {
472  mCrsFilter = crsFilter;
473  mProjListDone = false;
474  mUserProjListDone = false;
475  lstCoordinateSystems->clear();
476 }
477 
478 void QgsProjectionSelector::loadUserCrsList( QSet<QString> *crsFilter )
479 {
480  if ( mUserProjListDone )
481  return;
482 
483  QgsDebugMsg( "Fetching user projection list..." );
484 
485  // convert our Coordinate Reference System filter into the SQL expression
486  QString sqlFilter = ogcWmsCrsFilterAsSqlExpression( crsFilter );
487 
488  // User defined coordinate system node
489  // Make in an italic font to distinguish them from real projections
490  mUserProjList = new QTreeWidgetItem( lstCoordinateSystems, QStringList( tr( "User Defined Coordinate Systems" ) ) );
491 
492  QFont fontTemp = mUserProjList->font( 0 );
493  fontTemp.setItalic( true );
494  fontTemp.setBold( true );
495  mUserProjList->setFont( 0, fontTemp );
496  mUserProjList->setIcon( 0, QIcon( QgsApplication::activeThemePath() + "user.png" ) );
497 
498  //determine where the user proj database lives for this user. If none is found an empty
499  //now only will be shown
500  QString databaseFileName = QgsApplication::qgisUserDbFilePath();
501  // first we look for ~/.qgis/qgis.db
502  // if it doesnt exist we copy it in from the global resources dir
503 
504  //return straight away if the user has not created any custom projections
505  if ( !QFileInfo( databaseFileName ).exists( ) )
506  {
507  QgsDebugMsg( "Users qgis.db not found...skipping" );
508  mUserProjListDone = true;
509  return;
510  }
511 
512  sqlite3 *database;
513  const char *tail;
514  sqlite3_stmt *stmt;
515  //check the db is available
516  int result = sqlite3_open_v2( databaseFileName.toUtf8().constData(), &database, SQLITE_OPEN_READONLY, NULL );
517  if ( result )
518  {
519  // XXX This will likely never happen since on open, sqlite creates the
520  // database if it does not exist. But we checked earlier for its existance
521  // and aborted in that case. This is because we may be runnig from read only
522  // media such as live cd and don't want to force trying to create a db.
523  showDBMissingWarning( databaseFileName );
524  return;
525  }
526 
527  // Set up the query to retrieve the projection information needed to populate the list
528  QString sql = QString( "select description, srs_id from vw_srs where %1" ).arg( sqlFilter );
529 
530  result = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail );
531  // XXX Need to free memory from the error msg if one is set
532  if ( result == SQLITE_OK )
533  {
534  QTreeWidgetItem *newItem;
535  while ( sqlite3_step( stmt ) == SQLITE_ROW )
536  {
537  newItem = new QTreeWidgetItem( mUserProjList, QStringList( QString::fromUtf8(( char * )sqlite3_column_text( stmt, 0 ) ) ) );
538  // EpsgCrsId for user projections is not always defined in some dbases.
539  // It's also not written from customprojections dialog.
540  // display the epsg (field 2) in the second column of the list view
541  // newItem->setText( EPSG_COLUMN, QString::fromUtf8(( char * )sqlite3_column_text( stmt, 2 ) ) );
542  // display the qgis srs_id (field 1) in the third column of the list view
543  newItem->setText( QGIS_CRS_ID_COLUMN, QString::fromUtf8(( char * )sqlite3_column_text( stmt, 1 ) ) );
544  newItem->setText( AUTHID_COLUMN, QString( "USER:%1" ).arg( QString::fromUtf8(( char * )sqlite3_column_text( stmt, 1 ) ).toInt() ) );
545  }
546  }
547  // close the sqlite3 statement
548  sqlite3_finalize( stmt );
549  sqlite3_close( database );
550 
551  mUserProjListDone = true;
552 }
553 
554 void QgsProjectionSelector::loadCrsList( QSet<QString> *crsFilter )
555 {
556  if ( mProjListDone )
557  return;
558 
559  // convert our Coordinate Reference System filter into the SQL expression
560  QString sqlFilter = ogcWmsCrsFilterAsSqlExpression( crsFilter );
561 
562  // Create the top-level nodes for the list view of projections
563  // Make in an italic font to distinguish them from real projections
564  //
565  // Geographic coordinate system node
566  mGeoList = new QTreeWidgetItem( lstCoordinateSystems, QStringList( tr( "Geographic Coordinate Systems" ) ) );
567 
568  QFont fontTemp = mGeoList->font( 0 );
569  fontTemp.setItalic( true );
570  fontTemp.setBold( true );
571  mGeoList->setFont( 0, fontTemp );
572  mGeoList->setIcon( 0, QIcon( QgsApplication::activeThemePath() + "geographic.png" ) );
573 
574  // Projected coordinate system node
575  mProjList = new QTreeWidgetItem( lstCoordinateSystems, QStringList( tr( "Projected Coordinate Systems" ) ) );
576 
577  fontTemp = mProjList->font( 0 );
578  fontTemp.setItalic( true );
579  fontTemp.setBold( true );
580  mProjList->setFont( 0, fontTemp );
581  mProjList->setIcon( 0, QIcon( QgsApplication::activeThemePath() + "transformed.png" ) );
582 
583  //bail out in case the projections db does not exist
584  //this is necessary in case the pc is running linux with a
585  //read only filesystem because otherwise sqlite will try
586  //to create the db file on the fly
587 
588  if ( !QFileInfo( mSrsDatabaseFileName ).exists() )
589  {
590  mProjListDone = true;
591  return;
592  }
593 
594  // open the database containing the spatial reference data
595  sqlite3 *database;
596  int rc = sqlite3_open_v2( mSrsDatabaseFileName.toUtf8().data(), &database, SQLITE_OPEN_READONLY, NULL );
597  if ( rc )
598  {
599  // XXX This will likely never happen since on open, sqlite creates the
600  // database if it does not exist.
602  return;
603  }
604  // prepare the sql statement
605  const char *tail;
606  sqlite3_stmt *stmt;
607  // get total count of records in the projection table
608  QString sql = "select count(*) from tbl_srs";
609 
610  rc = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail );
611  Q_ASSERT( rc == SQLITE_OK );
612  sqlite3_step( stmt );
613  sqlite3_finalize( stmt );
614 
615  // Set up the query to retrieve the projection information needed to populate the list
616  //note I am giving the full field names for clarity here and in case someone
617  //changes the underlying view TS
618  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" )
619  .arg( sqlFilter );
620 
621  rc = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail );
622  // XXX Need to free memory from the error msg if one is set
623  if ( rc == SQLITE_OK )
624  {
625  QTreeWidgetItem *newItem;
626  // Cache some stuff to speed up creating of the list of projected
627  // spatial reference systems
628  QString previousSrsType( "" );
629  QTreeWidgetItem* previousSrsTypeNode = 0;
630 
631  while ( sqlite3_step( stmt ) == SQLITE_ROW )
632  {
633  // check to see if the srs is geographic
634  int isGeo = sqlite3_column_int( stmt, 3 );
635  if ( isGeo )
636  {
637  // this is a geographic coordinate system
638  // Add it to the tree (field 0)
639  newItem = new QTreeWidgetItem( mGeoList, QStringList( QString::fromUtf8(( char * )sqlite3_column_text( stmt, 0 ) ) ) );
640 
641  // display the authority name (field 2) in the second column of the list view
642  newItem->setText( AUTHID_COLUMN, QString::fromUtf8(( char * )sqlite3_column_text( stmt, 2 ) ) );
643 
644  // display the qgis srs_id (field 1) in the third column of the list view
645  newItem->setText( QGIS_CRS_ID_COLUMN, QString::fromUtf8(( char * )sqlite3_column_text( stmt, 1 ) ) );
646  }
647  else
648  {
649  // This is a projected srs
650  QTreeWidgetItem *node;
651  QString srsType = QString::fromUtf8(( char* )sqlite3_column_text( stmt, 4 ) );
652  // Find the node for this type and add the projection to it
653  // If the node doesn't exist, create it
654  if ( srsType == previousSrsType )
655  {
656  node = previousSrsTypeNode;
657  }
658  else
659  { // Different from last one, need to search
660  QList<QTreeWidgetItem*> nodes = lstCoordinateSystems->findItems( srsType, Qt::MatchExactly | Qt::MatchRecursive, NAME_COLUMN );
661  if ( nodes.count() == 0 )
662  {
663  // the node doesn't exist -- create it
664  // Make in an italic font to distinguish them from real projections
665  node = new QTreeWidgetItem( mProjList, QStringList( srsType ) );
666  QFont fontTemp = node->font( 0 );
667  fontTemp.setItalic( true );
668  node->setFont( 0, fontTemp );
669  }
670  else
671  {
672  node = nodes.first();
673  }
674  // Update the cache.
675  previousSrsType = srsType;
676  previousSrsTypeNode = node;
677  }
678  // add the item, setting the projection name in the first column of the list view
679  newItem = new QTreeWidgetItem( node, QStringList( QString::fromUtf8(( char * )sqlite3_column_text( stmt, 0 ) ) ) );
680  // display the authority id (field 2) in the second column of the list view
681  newItem->setText( AUTHID_COLUMN, QString::fromUtf8(( char * )sqlite3_column_text( stmt, 2 ) ) );
682  // display the qgis srs_id (field 1) in the third column of the list view
683  newItem->setText( QGIS_CRS_ID_COLUMN, QString::fromUtf8(( char * )sqlite3_column_text( stmt, 1 ) ) );
684  // expand also parent node
685  newItem->parent()->setExpanded( true );
686  }
687 
688  // display the qgis deprecated in the user data of the item
689  newItem->setData( 0, Qt::UserRole, QString::fromUtf8(( char * )sqlite3_column_text( stmt, 6 ) ) );
690  newItem->setHidden( cbxHideDeprecated->isChecked() );
691  }
692  mProjList->setExpanded( true );
693  }
694 
695  // close the sqlite3 statement
696  sqlite3_finalize( stmt );
697  // close the database
698  sqlite3_close( database );
699 
700  mProjListDone = true;
701 }
702 
703 // New coordinate system selected from the list
704 void QgsProjectionSelector::on_lstCoordinateSystems_currentItemChanged( QTreeWidgetItem *current, QTreeWidgetItem * )
705 {
706  QgsDebugMsg( "Entered." );
707 
708  if ( !current )
709  {
710  QgsDebugMsg( "no current item" );
711  return;
712  }
713 
714  lstCoordinateSystems->scrollToItem( current );
715 
716  // If the item has children, it's not an end node in the tree, and
717  // hence is just a grouping thingy, not an actual CRS.
718  if ( current->childCount() == 0 )
719  {
720  // Found a real CRS
721  emit sridSelected( QString::number( selectedCrsId() ) );
722 
723  teProjection->setText( selectedProj4String() );
724  teSelected->setText( selectedName() );
725 
726  QList<QTreeWidgetItem*> nodes = lstRecent->findItems( current->text( QGIS_CRS_ID_COLUMN ), Qt::MatchExactly, QGIS_CRS_ID_COLUMN );
727  if ( nodes.count() > 0 )
728  {
729  QgsDebugMsg( QString( "found srs %1 in recent" ).arg( current->text( QGIS_CRS_ID_COLUMN ) ) );
730  lstRecent->setCurrentItem( nodes.first() );
731  }
732  else
733  {
734  QgsDebugMsg( QString( "srs %1 not recent" ).arg( current->text( QGIS_CRS_ID_COLUMN ) ) );
735  lstRecent->clearSelection();
736  lstCoordinateSystems->setFocus( Qt::OtherFocusReason );
737  }
738  }
739  else
740  {
741  // Not an CRS - remove the highlight so the user doesn't get too confused
742  current->setSelected( false );
743  teProjection->setText( "" );
744  teSelected->setText( "" );
745  lstRecent->clearSelection();
746  }
747 }
748 
749 void QgsProjectionSelector::on_lstRecent_currentItemChanged( QTreeWidgetItem *current, QTreeWidgetItem * )
750 {
751  QgsDebugMsg( "Entered." );
752 
753  if ( mSkipFirstRecent )
754  {
755  mSkipFirstRecent = false;
756  return;
757  }
758 
759  if ( !current )
760  {
761  QgsDebugMsg( "no current item" );
762  return;
763  }
764 
765  lstRecent->scrollToItem( current );
766 
767  QList<QTreeWidgetItem*> nodes = lstCoordinateSystems->findItems( current->text( QGIS_CRS_ID_COLUMN ), Qt::MatchExactly | Qt::MatchRecursive, QGIS_CRS_ID_COLUMN );
768  if ( nodes.count() > 0 )
769  lstCoordinateSystems->setCurrentItem( nodes.first() );
770 }
771 
772 void QgsProjectionSelector::hideDeprecated( QTreeWidgetItem *item )
773 {
774  if ( item->data( 0, Qt::UserRole ).toBool() )
775  {
776  item->setHidden( cbxHideDeprecated->isChecked() );
777  if ( item->isSelected() && item->isHidden() )
778  {
779  item->setSelected( false );
780  teProjection->setText( "" );
781  teSelected->setText( "" );
782  }
783  }
784 
785  for ( int i = 0; i < item->childCount(); i++ )
786  hideDeprecated( item->child( i ) );
787 }
788 
790 {
791  for ( int i = 0; i < lstCoordinateSystems->topLevelItemCount(); i++ )
792  hideDeprecated( lstCoordinateSystems->topLevelItem( i ) );
793 }
794 
795 void QgsProjectionSelector::on_leSearch_textChanged( const QString & theFilterTxt )
796 {
797  QString filterTxt = theFilterTxt;
798  filterTxt.replace( QRegExp( "\\s+" ), ".*" );
799  QRegExp re( filterTxt, Qt::CaseInsensitive );
800 
801  // filter recent crs's
802  QTreeWidgetItemIterator itr( lstRecent );
803  while ( *itr )
804  {
805  if (( *itr )->childCount() == 0 ) // it's an end node aka a projection
806  {
807  if (( *itr )->text( NAME_COLUMN ).contains( re )
808  || ( *itr )->text( AUTHID_COLUMN ).contains( re )
809  )
810  {
811  ( *itr )->setHidden( false );
812  QTreeWidgetItem * parent = ( *itr )->parent();
813  while ( parent )
814  {
815  parent->setExpanded( true );
816  parent->setHidden( false );
817  parent = parent->parent();
818  }
819  }
820  else
821  {
822  ( *itr )->setHidden( true );
823  }
824  }
825  else
826  {
827  ( *itr )->setHidden( true );
828  }
829  ++itr;
830  }
831 
832  // filter crs's
833  QTreeWidgetItemIterator it( lstCoordinateSystems );
834  while ( *it )
835  {
836  if (( *it )->childCount() == 0 ) // it's an end node aka a projection
837  {
838  if (( *it )->text( NAME_COLUMN ).contains( re )
839  || ( *it )->text( AUTHID_COLUMN ).contains( re )
840  )
841  {
842  ( *it )->setHidden( false );
843  QTreeWidgetItem * parent = ( *it )->parent();
844  while ( parent )
845  {
846  parent->setExpanded( true );
847  parent->setHidden( false );
848  parent = parent->parent();
849  }
850  }
851  else
852  {
853  ( *it )->setHidden( true );
854  }
855  }
856  else
857  {
858  ( *it )->setHidden( true );
859  }
860  ++it;
861  }
862 }
863 
864 
866 {
867  long srsId = 0;
868 
869  //
870  // Now perform the actual search
871  //
872 
873  sqlite3 *database;
874  const char *tail;
875  sqlite3_stmt *stmt;
876  int result;
877 
878  // first we search the users db as any srsid there will be definition be greater than in sys db
879 
880  //check the db is available
881  QString databaseFileName = QgsApplication::qgisUserDbFilePath();
882  if ( QFileInfo( databaseFileName ).exists() ) //only bother trying to open if the file exists
883  {
884  result = sqlite3_open_v2( databaseFileName.toUtf8().data(), &database, SQLITE_OPEN_READONLY, NULL );
885  if ( result )
886  {
887  // XXX This will likely never happen since on open, sqlite creates the
888  // database if it does not exist. But we checked earlier for its existance
889  // and aborted in that case. This is because we may be runnig from read only
890  // media such as live cd and don't want to force trying to create a db.
891  showDBMissingWarning( databaseFileName );
892  return 0;
893  }
894 
895  result = sqlite3_prepare( database, theSql.toUtf8(), theSql.toUtf8().length(), &stmt, &tail );
896  // XXX Need to free memory from the error msg if one is set
897  if ( result == SQLITE_OK && sqlite3_step( stmt ) == SQLITE_ROW )
898  {
899  QString srsIdString = QString::fromUtf8(( char * )sqlite3_column_text( stmt, 0 ) );
900  srsId = srsIdString.toLong();
901  // close the sqlite3 statement
902  sqlite3_finalize( stmt );
903  sqlite3_close( database );
904  return srsId;
905  }
906  }
907  else
908  {
909  //only bother looking in srs.db if it wasnt found above
910  result = sqlite3_open_v2( mSrsDatabaseFileName.toUtf8().data(), &database, SQLITE_OPEN_READONLY, NULL );
911  if ( result )
912  {
913  QgsDebugMsg( QString( "Can't open * user * database: %1" ).arg( sqlite3_errmsg( database ) ) );
914  //no need for assert because user db may not have been created yet
915  return 0;
916  }
917  }
918 
919  result = sqlite3_prepare( database, theSql.toUtf8(), theSql.toUtf8().length(), &stmt, &tail );
920  // XXX Need to free memory from the error msg if one is set
921  if ( result == SQLITE_OK && sqlite3_step( stmt ) == SQLITE_ROW )
922  {
923  QString srsIdString = QString::fromUtf8(( char * )sqlite3_column_text( stmt, 0 ) );
924  srsId = srsIdString.toLong();
925  }
926 
927  // close the sqlite3 statement
928  sqlite3_finalize( stmt );
929  sqlite3_close( database );
930 
931  return srsId;
932 }
933 
935 {
936  sqlite3 *database;
937  const char *tail;
938  sqlite3_stmt *stmt;
939 
940  int result = sqlite3_open_v2( mSrsDatabaseFileName.toUtf8().data(), &database, SQLITE_OPEN_READONLY, NULL );
941  if ( result )
942  {
943  QgsDebugMsg( QString( "Can't open * user * database: %1" ).arg( sqlite3_errmsg( database ) ) );
944  //no need for assert because user db may not have been created yet
945  return QStringList();
946  }
947 
948  QString theSql = "select distinct auth_name from tbl_srs";
949  result = sqlite3_prepare( database, theSql.toUtf8(), theSql.toUtf8().length(), &stmt, &tail );
950 
951  QStringList authorities;
952  if ( result == SQLITE_OK )
953  {
954  while ( sqlite3_step( stmt ) == SQLITE_ROW )
955  {
956  authorities << QString::fromUtf8(( char * )sqlite3_column_text( stmt, 0 ) );
957  }
958 
959  }
960 
961  // close the sqlite3 statement
962  sqlite3_finalize( stmt );
963  sqlite3_close( database );
964 
965  return authorities;
966 }
967 
977 const QString QgsProjectionSelector::sqlSafeString( const QString theSQL )
978 {
979  QString retval = theSQL;
980  retval.replace( "\\", "\\\\" );
981  retval.replace( '\"', "\\\"" );
982  retval.replace( "\'", "\\'" );
983  retval.replace( "%", "\\%" );
984  return retval;
985 }
986 
987 void QgsProjectionSelector::showDBMissingWarning( const QString theFileName )
988 {
989 
990  QMessageBox::critical( this, tr( "Resource Location Error" ),
991  tr( "Error reading database file from: \n %1\n"
992  "Because of this the projection selector will not work..." )
993  .arg( theFileName ) );
994 }