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