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 *
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::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 }
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
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 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:378
void loadCrsList(QSet< QString > *crsFilter=0)
Populate the proj tree view with system projection names...
QgsProjectionSelector(QWidget *parent, const char *name="", Qt::WFlags fl=0)
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.