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