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