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