QGIS API Documentation  2.9.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules 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 }
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.
void resizeEvent(QResizeEvent *theEvent) override
Used to manage column sizes.
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
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
QgsProjectionSelector(QWidget *parent, const char *name="", Qt::WindowFlags fl=0)
void setSelectedCrsId(long theCRSID)
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...
void setSelectedCrsName(QString theCRSName)
void on_lstRecent_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *prev)
QString selectedAuthId()
Gets the current authority-style projection identifier.
static QStringList recentProjections()
Returns a list of recently used projections.
void setSelectedAuthId(QString authId)
void on_lstCoordinateSystems_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *prev)
void sridSelected(QString theSRID)
void on_leSearch_textChanged(const QString &)
void pushProjectionToFront()
mark selected projection for push to front
struct sqlite3 sqlite3
const int USER_CRS_START_ID
Magick number that determines whether a projection crsid is a system (srs.db) or user (~/...
Definition: qgis.h:400
void loadCrsList(QSet< QString > *crsFilter=0)
Populate the proj tree view with system projection names...
Class for storing a coordinate reference system (CRS)
static const QString srsDbFilePath()
Returns the path to the srs.db file.
void initialized()
Notify others that the widget is now fully initialized, including deferred selection of projection...
void showEvent(QShowEvent *theEvent) override
Used to ensure the projection list view is actually populated.
static const QString qgisUserDbFilePath()
Returns the path to the user qgis.db file.
QString toProj4() const
Get the Proj Proj4 string representation of this srs.
#define tr(sourceText)
long selectedCrsId()
Gets the current QGIS projection identfier.