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