QGIS API Documentation  3.14.0-Pi (9f7028fd23)
qgsprojectionselectiontreewidget.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  ***************************************************************************/
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 #include "qgssettings.h"
23 #include "qgsrectangle.h"
24 
25 //qt includes
26 #include <QFileInfo>
27 #include <QHeaderView>
28 #include <QResizeEvent>
29 #include <QMessageBox>
30 
32  : QWidget( parent )
33 {
34  setupUi( this );
35 
36  QFont f = teProjection->font();
37  f.setPointSize( f.pointSize() - 2 );
38  teProjection->setFont( f );
39 
40  leSearch->setShowSearchIcon( true );
41 
42  connect( lstCoordinateSystems, &QTreeWidget::itemDoubleClicked, this, &QgsProjectionSelectionTreeWidget::lstCoordinateSystems_itemDoubleClicked );
43  connect( lstRecent, &QTreeWidget::itemDoubleClicked, this, &QgsProjectionSelectionTreeWidget::lstRecent_itemDoubleClicked );
44  connect( lstCoordinateSystems, &QTreeWidget::currentItemChanged, this, &QgsProjectionSelectionTreeWidget::lstCoordinateSystems_currentItemChanged );
45  connect( lstRecent, &QTreeWidget::currentItemChanged, this, &QgsProjectionSelectionTreeWidget::lstRecent_currentItemChanged );
46  connect( cbxHideDeprecated, &QCheckBox::stateChanged, this, &QgsProjectionSelectionTreeWidget::updateFilter );
47  connect( leSearch, &QgsFilterLineEdit::textChanged, this, &QgsProjectionSelectionTreeWidget::updateFilter );
48 
49  mAreaCanvas->setVisible( mShowMap );
50 
51  if ( QDialog *dlg = qobject_cast<QDialog *>( parent ) )
52  {
53  // mark selected projection for push to front if parent dialog is accepted
54  connect( dlg, &QDialog::accepted, this, &QgsProjectionSelectionTreeWidget::pushProjectionToFront );
55  }
56 
57  // Get the full path name to the sqlite3 spatial reference database.
58  mSrsDatabaseFileName = QgsApplication::srsDatabaseFilePath();
59 
60  lstCoordinateSystems->header()->setSectionResizeMode( AuthidColumn, QHeaderView::Stretch );
61  lstCoordinateSystems->header()->resizeSection( QgisCrsIdColumn, 0 );
62  lstCoordinateSystems->header()->setSectionResizeMode( QgisCrsIdColumn, QHeaderView::Fixed );
63 
64  // Hide (internal) ID column
65  lstCoordinateSystems->setColumnHidden( QgisCrsIdColumn, true );
66 
67  lstRecent->header()->setSectionResizeMode( AuthidColumn, QHeaderView::Stretch );
68  lstRecent->header()->resizeSection( QgisCrsIdColumn, 0 );
69  lstRecent->header()->setSectionResizeMode( QgisCrsIdColumn, QHeaderView::Fixed );
70 
71  // Hide (internal) ID column
72  lstRecent->setColumnHidden( QgisCrsIdColumn, true );
73 
75 
76  mCheckBoxNoProjection->setHidden( true );
77  mCheckBoxNoProjection->setEnabled( false );
78  connect( mCheckBoxNoProjection, &QCheckBox::toggled, this, [ = ]
79  {
80  if ( !mBlockSignals )
81  emit crsSelected();
82  } );
83  connect( mCheckBoxNoProjection, &QCheckBox::toggled, this, [ = ]( bool checked )
84  {
85  if ( mCheckBoxNoProjection->isEnabled() )
86  {
87  mFrameProjections->setDisabled( checked );
88  }
89  } );
90 }
91 
93 {
94  if ( !mPushProjectionToFront )
95  {
96  return;
97  }
98 
99  // Push current projection to front, only if set
100  long crsId = selectedCrsId();
101  if ( crsId == 0 )
102  return;
103 
105 }
106 
108 {
109  lstCoordinateSystems->header()->resizeSection( NameColumn, event->size().width() - 240 );
110  lstCoordinateSystems->header()->resizeSection( AuthidColumn, 240 );
111  lstCoordinateSystems->header()->resizeSection( QgisCrsIdColumn, 0 );
112 
113  lstRecent->header()->resizeSection( NameColumn, event->size().width() - 240 );
114  lstRecent->header()->resizeSection( AuthidColumn, 240 );
115  lstRecent->header()->resizeSection( QgisCrsIdColumn, 0 );
116 }
117 
119 {
120  if ( mInitialized )
121  return;
122 
123  // ensure the projection list view is actually populated
124  // before we show this widget
125  loadCrsList( &mCrsFilter );
126  loadUserCrsList( &mCrsFilter );
127 
128  if ( !mRecentProjListDone )
129  {
130  for ( const QgsCoordinateReferenceSystem &crs : qgis::as_const( mRecentProjections ) )
131  insertRecent( crs );
132  mRecentProjListDone = true;
133  }
134 
135  // apply deferred selection
136  mBlockSignals = true; // we've already emitted the signal, when the deferred crs was first set
137  applySelection();
138  mBlockSignals = false;
139 
140  emit initialized();
141 
142  // Pass up the inheritance hierarchy
143  QWidget::showEvent( event );
144  mInitialized = true;
145 }
146 
147 QString QgsProjectionSelectionTreeWidget::ogcWmsCrsFilterAsSqlExpression( QSet<QString> *crsFilter )
148 {
149  QString sqlExpression = QStringLiteral( "1" ); // it's "SQL" for "true"
150  QMap<QString, QStringList> authParts;
151 
152  if ( !crsFilter )
153  return sqlExpression;
154 
155  /*
156  Ref: WMS 1.3.0, section 6.7.3 "Layer CRS":
157 
158  Every Layer CRS has an identifier that is a character string. Two types of
159  Layer CRS identifiers are permitted: "label" and "URL" identifiers:
160 
161  Label: The identifier includes a namespace prefix, a colon, a numeric or
162  string code, and in some instances a comma followed by additional
163  parameters. This International Standard defines three namespaces:
164  CRS, EpsgCrsId and AUTO2 [...]
165 
166  URL: The identifier is a fully-qualified Uniform Resource Locator that
167  references a publicly-accessible file containing a definition of the CRS
168  that is compliant with ISO 19111.
169  */
170 
171  // iterate through all incoming CRSs
172 
173  const auto authIds { *crsFilter };
174  for ( const QString &auth_id : authIds )
175  {
176  QStringList parts = auth_id.split( ':' );
177 
178  if ( parts.size() < 2 )
179  continue;
180 
181  authParts[ parts.at( 0 ).toUpper()].append( parts.at( 1 ).toUpper() );
182  }
183 
184  if ( authParts.isEmpty() )
185  return sqlExpression;
186 
187  if ( !authParts.isEmpty() )
188  {
189  QString prefix = QStringLiteral( " AND (" );
190  for ( auto it = authParts.constBegin(); it != authParts.constEnd(); ++it )
191  {
192  sqlExpression += QStringLiteral( "%1(upper(auth_name)='%2' AND upper(auth_id) IN ('%3'))" )
193  .arg( prefix,
194  it.key(),
195  it.value().join( QStringLiteral( "','" ) ) );
196  prefix = QStringLiteral( " OR " );
197  }
198  sqlExpression += ')';
199  }
200 
201  QgsDebugMsgLevel( "exiting with '" + sqlExpression + "'.", 4 );
202 
203  return sqlExpression;
204 }
205 
206 void QgsProjectionSelectionTreeWidget::applySelection( int column, QString value )
207 {
208  if ( !mProjListDone || !mUserProjListDone )
209  {
210  // defer selection until loaded
211  mSearchColumn = column;
212  mSearchValue = value;
213  return;
214  }
215 
216  if ( column == QgsProjectionSelectionTreeWidget::None )
217  {
218  // invoked deferred selection
219  column = mSearchColumn;
220  value = mSearchValue;
221 
222  mSearchColumn = QgsProjectionSelectionTreeWidget::None;
223  mSearchValue.clear();
224  }
225 
226  if ( column == QgsProjectionSelectionTreeWidget::None )
227  return;
228 
229  QList<QTreeWidgetItem *> nodes = lstCoordinateSystems->findItems( value, Qt::MatchExactly | Qt::MatchRecursive, column );
230  if ( !nodes.isEmpty() )
231  {
232  QgsDebugMsgLevel( QStringLiteral( "found %1,%2" ).arg( column ).arg( value ), 4 );
233  lstCoordinateSystems->setCurrentItem( nodes.first() );
234  }
235  else
236  {
237  QgsDebugMsgLevel( QStringLiteral( "nothing found for %1,%2" ).arg( column ).arg( value ), 4 );
238  // deselect the selected item to avoid confusing the user
239  lstCoordinateSystems->clearSelection();
240  lstRecent->clearSelection();
241  teProjection->clear();
242  }
243 }
244 
245 void QgsProjectionSelectionTreeWidget::insertRecent( const QgsCoordinateReferenceSystem &crs )
246 {
247  if ( !mProjListDone || !mUserProjListDone )
248  return;
249 
250  QList<QTreeWidgetItem *> nodes = lstCoordinateSystems->findItems( QString::number( crs.srsid() ), Qt::MatchExactly | Qt::MatchRecursive, QgisCrsIdColumn );
251  if ( nodes.isEmpty() )
252  return;
253 
254  lstRecent->insertTopLevelItem( 0, new QTreeWidgetItem( lstRecent, QStringList()
255  << nodes.first()->text( NameColumn )
256  << nodes.first()->text( AuthidColumn )
257  << nodes.first()->text( QgisCrsIdColumn ) ) );
258 }
259 
260 //note this line just returns the projection name!
261 QString QgsProjectionSelectionTreeWidget::selectedName()
262 {
263  // return the selected wkt name from the list view
264  QTreeWidgetItem *lvi = lstCoordinateSystems->currentItem();
265  return lvi ? lvi->text( NameColumn ) : QString();
266 }
267 
269 {
270  if ( !crs.isValid() )
271  {
272  mCheckBoxNoProjection->setChecked( true );
273  }
274  else
275  {
276  bool changed = false;
277  if ( !mInitialized )
278  {
279  changed = mDeferredLoadCrs != crs;
280  mDeferredLoadCrs = crs;
281  }
282  mBlockSignals = true;
283  mCheckBoxNoProjection->setChecked( false );
284  mBlockSignals = false;
285 
286  if ( !crs.authid().isEmpty() )
287  applySelection( AuthidColumn, crs.authid() );
288  else
289  loadUnknownCrs( crs );
290  if ( changed )
291  {
292  emit crsSelected();
293  }
294  }
295 }
296 
298 {
299  mAreaCanvas->setCanvasRect( rect );
300 }
301 
303 {
304  return mAreaCanvas->canvasRect();
305 }
306 
307 QString QgsProjectionSelectionTreeWidget::getSelectedExpression( const QString &expression ) const
308 {
309  // Only return the attribute if there is a node in the tree
310  // selected that has an srs_id. This prevents error if the user
311  // selects a top-level node rather than an actual coordinate
312  // system
313  //
314  // Get the selected node and make sure it is a srs andx
315  // not a top-level projection node
316  QTreeWidgetItem *lvi = lstCoordinateSystems->currentItem();
317  if ( !lvi || lvi->text( QgisCrsIdColumn ).isEmpty() )
318  return QString();
319 
320  //
321  // Determine if this is a user projection or a system on
322  // user projection defs all have srs_id >= 100000
323  //
324  QString databaseFileName;
325  if ( lvi->text( QgisCrsIdColumn ).toLong() >= USER_CRS_START_ID )
326  {
327  databaseFileName = QgsApplication::qgisUserDatabaseFilePath();
328  if ( !QFileInfo::exists( databaseFileName ) )
329  {
330  return QString();
331  }
332  }
333  else
334  {
335  databaseFileName = mSrsDatabaseFileName;
336  }
337 
338  //
339  // set up the database
340  // XXX We could probably hold the database open for the life of this object,
341  // assuming that it will never be used anywhere else. Given the low overhead,
342  // opening it each time seems to be a reasonable approach at this time.
343  sqlite3 *database = nullptr;
344  int rc = sqlite3_open_v2( databaseFileName.toUtf8().constData(), &database, SQLITE_OPEN_READONLY, nullptr );
345  if ( rc )
346  {
347  QgsMessageLog::logMessage( tr( "Resource Location Error" ), tr( "Error reading database file from: \n %1\n"
348  "Because of this the projection selector will not work…" ).arg( databaseFileName ),
349  Qgis::Critical );
350  return QString();
351  }
352 
353  // prepare the sql statement
354  const char *tail = nullptr;
355  sqlite3_stmt *stmt = nullptr;
356  QString sql = QStringLiteral( "select %1 from tbl_srs where srs_id=%2" )
357  .arg( expression,
358  lvi->text( QgisCrsIdColumn ) );
359 
360  QgsDebugMsgLevel( QStringLiteral( "Finding selected attribute using : %1" ).arg( sql ), 4 );
361  rc = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail );
362  // XXX Need to free memory from the error msg if one is set
363  QString attributeValue;
364  if ( rc == SQLITE_OK && sqlite3_step( stmt ) == SQLITE_ROW )
365  {
366  // get the first row of the result set
367  attributeValue = QString::fromUtf8( ( char * )sqlite3_column_text( stmt, 0 ) );
368  }
369 
370  // close the statement
371  sqlite3_finalize( stmt );
372  // close the database
373  sqlite3_close( database );
374 
375  // return the srs
376  return attributeValue;
377 }
378 
380 {
381  if ( mCheckBoxNoProjection->isEnabled() && mCheckBoxNoProjection->isChecked() )
383 
384  if ( !mInitialized && mDeferredLoadCrs.isValid() )
385  return mDeferredLoadCrs;
386 
387  const QString srsIdString = getSelectedExpression( QStringLiteral( "srs_id" ) );
388  if ( !srsIdString.isEmpty() )
389  {
390  int srid = srsIdString.toLong();
391  if ( srid >= USER_CRS_START_ID )
392  return QgsCoordinateReferenceSystem::fromOgcWmsCrs( QStringLiteral( "USER:%1" ).arg( srid ) );
393  else
394  return QgsCoordinateReferenceSystem::fromOgcWmsCrs( getSelectedExpression( QStringLiteral( "upper(auth_name||':'||auth_id)" ) ) );
395  }
396  else
397  {
398  // custom CRS
399  QTreeWidgetItem *lvi = lstCoordinateSystems->currentItem();
400  if ( lvi && lvi->data( 0, RoleWkt ).isValid() )
401  return QgsCoordinateReferenceSystem::fromWkt( lvi->data( 0, RoleWkt ).toString() );
402  else if ( lvi && lvi->data( 0, RoleProj ).isValid() )
403  return QgsCoordinateReferenceSystem::fromProj( lvi->data( 0, RoleProj ).toString() );
404  else
406  }
407 }
408 
410 {
411  mCheckBoxNoProjection->setVisible( show );
412  mCheckBoxNoProjection->setEnabled( show );
413  if ( show )
414  {
415  mFrameProjections->setDisabled( mCheckBoxNoProjection->isChecked() );
416  }
417 }
418 
420 {
421  mShowMap = show;
422  mAreaCanvas->setVisible( show );
423 }
424 
426 {
427  return !mCheckBoxNoProjection->isHidden();
428 }
429 
431 {
432  return mShowMap;
433 }
434 
436 {
437  QTreeWidgetItem *item = lstCoordinateSystems->currentItem();
438  if ( mCheckBoxNoProjection->isChecked() )
439  return true;
440  else if ( !mInitialized && mDeferredLoadCrs.isValid() )
441  return true;
442  else
443  return item && ( !item->text( QgisCrsIdColumn ).isEmpty() || item->data( 0, RoleWkt ).isValid() );
444 }
445 
446 long QgsProjectionSelectionTreeWidget::selectedCrsId()
447 {
448  QTreeWidgetItem *item = lstCoordinateSystems->currentItem();
449 
450  if ( item && !item->text( QgisCrsIdColumn ).isEmpty() )
451  return lstCoordinateSystems->currentItem()->text( QgisCrsIdColumn ).toLong();
452  else
453  return 0;
454 }
455 
456 
457 void QgsProjectionSelectionTreeWidget::setOgcWmsCrsFilter( const QSet<QString> &crsFilter )
458 {
459  mCrsFilter = crsFilter;
460  mProjListDone = false;
461  mUserProjListDone = false;
462  lstCoordinateSystems->clear();
463 }
464 
465 void QgsProjectionSelectionTreeWidget::loadUserCrsList( QSet<QString> *crsFilter )
466 {
467  if ( mUserProjListDone )
468  return;
469 
470  QgsDebugMsgLevel( QStringLiteral( "Fetching user projection list..." ), 4 );
471 
472  // convert our Coordinate Reference System filter into the SQL expression
473  QString sqlFilter = ogcWmsCrsFilterAsSqlExpression( crsFilter );
474 
475  // User defined coordinate system node
476  // Make in an italic font to distinguish them from real projections
477  mUserProjList = new QTreeWidgetItem( lstCoordinateSystems, QStringList( tr( "User Defined Coordinate Systems" ) ) );
478 
479  QFont fontTemp = mUserProjList->font( 0 );
480  fontTemp.setItalic( true );
481  fontTemp.setBold( true );
482  mUserProjList->setFont( 0, fontTemp );
483  mUserProjList->setIcon( 0, QgsApplication::getThemeIcon( QStringLiteral( "/user.svg" ) ) );
484 
485  //determine where the user proj database lives for this user. If none is found an empty
486  //now only will be shown
487  QString databaseFileName = QgsApplication::qgisUserDatabaseFilePath();
488  // first we look for ~/.qgis/qgis.db
489  // if it doesn't exist we copy it in from the global resources dir
490 
491  //return straight away if the user has not created any custom projections
492  if ( !QFileInfo::exists( databaseFileName ) )
493  {
494  QgsDebugMsg( QStringLiteral( "Users qgis.db not found...skipping" ) );
495  mUserProjListDone = true;
496  return;
497  }
498 
499  sqlite3 *database = nullptr;
500  const char *tail = nullptr;
501  sqlite3_stmt *stmt = nullptr;
502  //check the db is available
503  int result = sqlite3_open_v2( databaseFileName.toUtf8().constData(), &database, SQLITE_OPEN_READONLY, nullptr );
504  if ( result )
505  {
506  // XXX This will likely never happen since on open, sqlite creates the
507  // database if it does not exist. But we checked earlier for its existence
508  // and aborted in that case. This is because we may be running from read only
509  // media such as live cd and don't want to force trying to create a db.
510  showDBMissingWarning( databaseFileName );
511  return;
512  }
513 
514  // Set up the query to retrieve the projection information needed to populate the list
515  QString sql = QStringLiteral( "select description, srs_id from vw_srs where %1" ).arg( sqlFilter );
516 
517  result = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail );
518  // XXX Need to free memory from the error msg if one is set
519  if ( result == SQLITE_OK )
520  {
521  QTreeWidgetItem *newItem = nullptr;
522  while ( sqlite3_step( stmt ) == SQLITE_ROW )
523  {
524  newItem = new QTreeWidgetItem( mUserProjList, QStringList( QString::fromUtf8( ( char * )sqlite3_column_text( stmt, 0 ) ) ) );
525  // EpsgCrsId for user projections is not always defined in some dbases.
526  // It's also not written from customprojections dialog.
527  // display the epsg (field 2) in the second column of the list view
528  // newItem->setText( EPSG_COLUMN, QString::fromUtf8(( char * )sqlite3_column_text( stmt, 2 ) ) );
529  // display the qgis srs_id (field 1) in the third column of the list view
530  newItem->setText( QgisCrsIdColumn, QString::fromUtf8( ( char * )sqlite3_column_text( stmt, 1 ) ) );
531  newItem->setText( AuthidColumn, QStringLiteral( "USER:%1" ).arg( QString::fromUtf8( ( char * )sqlite3_column_text( stmt, 1 ) ).toInt() ) );
532  }
533  }
534  // close the sqlite3 statement
535  sqlite3_finalize( stmt );
536  sqlite3_close( database );
537 
538  mUserProjListDone = true;
539 }
540 
541 void QgsProjectionSelectionTreeWidget::loadCrsList( QSet<QString> *crsFilter )
542 {
543  if ( mProjListDone )
544  return;
545 
546  // convert our Coordinate Reference System filter into the SQL expression
547  QString sqlFilter = ogcWmsCrsFilterAsSqlExpression( crsFilter );
548 
549  // Create the top-level nodes for the list view of projections
550  // Make in an italic font to distinguish them from real projections
551  //
552  // Geographic coordinate system node
553  mGeoList = new QTreeWidgetItem( lstCoordinateSystems, QStringList( tr( "Geographic Coordinate Systems" ) ) );
554 
555  QFont fontTemp = mGeoList->font( 0 );
556  fontTemp.setItalic( true );
557  fontTemp.setBold( true );
558  mGeoList->setFont( 0, fontTemp );
559  mGeoList->setIcon( 0, QgsApplication::getThemeIcon( QStringLiteral( "/mIconProjectionEnabled.svg" ) ) );
560 
561  // Projected coordinate system node
562  mProjList = new QTreeWidgetItem( lstCoordinateSystems, QStringList( tr( "Projected Coordinate Systems" ) ) );
563 
564  fontTemp = mProjList->font( 0 );
565  fontTemp.setItalic( true );
566  fontTemp.setBold( true );
567  mProjList->setFont( 0, fontTemp );
568  mProjList->setIcon( 0, QgsApplication::getThemeIcon( QStringLiteral( "/transformed.svg" ) ) );
569 
570  //bail out in case the projections db does not exist
571  //this is necessary in case the pc is running linux with a
572  //read only filesystem because otherwise sqlite will try
573  //to create the db file on the fly
574 
575  if ( !QFileInfo::exists( mSrsDatabaseFileName ) )
576  {
577  mProjListDone = true;
578  return;
579  }
580 
581  // open the database containing the spatial reference data
582  sqlite3 *database = nullptr;
583  int rc = sqlite3_open_v2( mSrsDatabaseFileName.toUtf8().constData(), &database, SQLITE_OPEN_READONLY, nullptr );
584  if ( rc )
585  {
586  // XXX This will likely never happen since on open, sqlite creates the
587  // database if it does not exist.
588  showDBMissingWarning( mSrsDatabaseFileName );
589  return;
590  }
591 
592  const char *tail = nullptr;
593  sqlite3_stmt *stmt = nullptr;
594  // Set up the query to retrieve the projection information needed to populate the list
595  //note I am giving the full field names for clarity here and in case someone
596  //changes the underlying view TS
597  QString sql = QStringLiteral( "select description, srs_id, upper(auth_name||':'||auth_id), is_geo, name, parameters, deprecated from vw_srs where %1 order by name,description" )
598  .arg( sqlFilter );
599 
600  rc = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail );
601  // XXX Need to free memory from the error msg if one is set
602  if ( rc == SQLITE_OK )
603  {
604  QTreeWidgetItem *newItem = nullptr;
605  // Cache some stuff to speed up creating of the list of projected
606  // spatial reference systems
607  QString previousSrsType;
608  QTreeWidgetItem *previousSrsTypeNode = nullptr;
609 
610  while ( sqlite3_step( stmt ) == SQLITE_ROW )
611  {
612  // check to see if the srs is geographic
613  int isGeo = sqlite3_column_int( stmt, 3 );
614  if ( isGeo )
615  {
616  // this is a geographic coordinate system
617  // Add it to the tree (field 0)
618  newItem = new QTreeWidgetItem( mGeoList, QStringList( QString::fromUtf8( ( char * )sqlite3_column_text( stmt, 0 ) ) ) );
619 
620  // display the authority name (field 2) in the second column of the list view
621  newItem->setText( AuthidColumn, QString::fromUtf8( ( char * )sqlite3_column_text( stmt, 2 ) ) );
622 
623  // display the qgis srs_id (field 1) in the third column of the list view
624  newItem->setText( QgisCrsIdColumn, QString::fromUtf8( ( char * )sqlite3_column_text( stmt, 1 ) ) );
625  }
626  else
627  {
628  // This is a projected srs
629  QTreeWidgetItem *node = nullptr;
630  QString srsType = QString::fromUtf8( ( char * )sqlite3_column_text( stmt, 4 ) );
631  if ( srsType.isEmpty() )
632  srsType = tr( "Other" );
633 
634  // Find the node for this type and add the projection to it
635  // If the node doesn't exist, create it
636  if ( srsType == previousSrsType )
637  {
638  node = previousSrsTypeNode;
639  }
640  else
641  {
642  // Different from last one, need to search
643  QList<QTreeWidgetItem *> nodes = lstCoordinateSystems->findItems( srsType, Qt::MatchExactly | Qt::MatchRecursive, NameColumn );
644  if ( nodes.isEmpty() )
645  {
646  // the node doesn't exist -- create it
647  // Make in an italic font to distinguish them from real projections
648  node = new QTreeWidgetItem( mProjList, QStringList( srsType ) );
649  QFont fontTemp = node->font( 0 );
650  fontTemp.setItalic( true );
651  node->setFont( 0, fontTemp );
652  }
653  else
654  {
655  node = nodes.first();
656  }
657  // Update the cache.
658  previousSrsType = srsType;
659  previousSrsTypeNode = node;
660  }
661  // add the item, setting the projection name in the first column of the list view
662  newItem = new QTreeWidgetItem( node, QStringList( QString::fromUtf8( ( char * )sqlite3_column_text( stmt, 0 ) ) ) );
663  // display the authority id (field 2) in the second column of the list view
664  newItem->setText( AuthidColumn, QString::fromUtf8( ( char * )sqlite3_column_text( stmt, 2 ) ) );
665  // display the qgis srs_id (field 1) in the third column of the list view
666  newItem->setText( QgisCrsIdColumn, QString::fromUtf8( ( char * )sqlite3_column_text( stmt, 1 ) ) );
667  // expand also parent node
668  newItem->parent()->setExpanded( true );
669  }
670 
671  // display the qgis deprecated in the user data of the item
672  newItem->setData( 0, RoleDeprecated, QString::fromUtf8( ( char * )sqlite3_column_text( stmt, 6 ) ) );
673  newItem->setHidden( cbxHideDeprecated->isChecked() );
674  }
675  mProjList->setExpanded( true );
676  }
677 
678  // close the sqlite3 statement
679  sqlite3_finalize( stmt );
680  // close the database
681  sqlite3_close( database );
682 
683  mProjListDone = true;
684 }
685 
686 void QgsProjectionSelectionTreeWidget::loadUnknownCrs( const QgsCoordinateReferenceSystem &crs )
687 {
688  if ( !mUnknownList )
689  {
690  mUnknownList = new QTreeWidgetItem( lstCoordinateSystems, QStringList( tr( "Unknown Coordinate Systems" ) ) );
691  QFont fontTemp = mUnknownList->font( 0 );
692  fontTemp.setItalic( true );
693  fontTemp.setBold( true );
694  mUnknownList->setFont( 0, fontTemp );
695  mUnknownList->setIcon( 0, QgsApplication::getThemeIcon( QStringLiteral( "/user.svg" ) ) );
696  }
697 
698  QTreeWidgetItem *newItem = new QTreeWidgetItem( mUnknownList, QStringList( QObject::tr( "Unknown CRS" ) ) );
699  newItem->setData( 0, RoleWkt, crs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ) );
700  newItem->setData( 0, RoleProj, crs.toProj() );
701 
702  lstCoordinateSystems->setCurrentItem( newItem );
703 }
704 
705 // New coordinate system selected from the list
706 void QgsProjectionSelectionTreeWidget::lstCoordinateSystems_currentItemChanged( QTreeWidgetItem *current, QTreeWidgetItem * )
707 {
708  QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
709 
710  if ( !current )
711  {
712  QgsDebugMsgLevel( QStringLiteral( "no current item" ), 4 );
713  return;
714  }
715 
716  lstCoordinateSystems->scrollToItem( current );
717 
718  // If the item has children, it's not an end node in the tree, and
719  // hence is just a grouping thingy, not an actual CRS.
720  if ( current->childCount() == 0 )
721  {
722  // Found a real CRS
723  if ( !mBlockSignals )
724  emit crsSelected();
725 
726  updateBoundsPreview();
727 
728  const QString crsId = current->text( QgisCrsIdColumn );
729  if ( !crsId.isEmpty() )
730  {
731  QList<QTreeWidgetItem *> nodes = lstRecent->findItems( current->text( QgisCrsIdColumn ), Qt::MatchExactly, QgisCrsIdColumn );
732  if ( !nodes.isEmpty() )
733  {
734  QgsDebugMsgLevel( QStringLiteral( "found srs %1 in recent" ).arg( current->text( QgisCrsIdColumn ) ), 4 );
735  lstRecent->setCurrentItem( nodes.first() );
736  }
737  else
738  {
739  QgsDebugMsgLevel( QStringLiteral( "srs %1 not recent" ).arg( current->text( QgisCrsIdColumn ) ), 4 );
740  lstRecent->clearSelection();
741  lstCoordinateSystems->setFocus( Qt::OtherFocusReason );
742  }
743  }
744  else
745  {
746  lstRecent->clearSelection();
747  lstCoordinateSystems->setFocus( Qt::OtherFocusReason );
748  }
749  }
750  else
751  {
752  // Not a CRS - remove the highlight so the user doesn't get too confused
753  current->setSelected( false );
754  teProjection->clear();
755  lstRecent->clearSelection();
756  }
757 }
758 
759 void QgsProjectionSelectionTreeWidget::lstCoordinateSystems_itemDoubleClicked( QTreeWidgetItem *current, int column )
760 {
761  Q_UNUSED( column )
762 
763  QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
764 
765  if ( !current )
766  {
767  QgsDebugMsgLevel( QStringLiteral( "no current item" ), 4 );
768  return;
769  }
770 
771  // If the item has children, it's not an end node in the tree, and
772  // hence is just a grouping thingy, not an actual CRS.
773  if ( current->childCount() == 0 )
775 }
776 
777 void QgsProjectionSelectionTreeWidget::lstRecent_currentItemChanged( QTreeWidgetItem *current, QTreeWidgetItem * )
778 {
779  QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
780 
781  if ( !current )
782  {
783  QgsDebugMsgLevel( QStringLiteral( "no current item" ), 4 );
784  return;
785  }
786 
787  lstRecent->scrollToItem( current );
788 
789  QList<QTreeWidgetItem *> nodes = lstCoordinateSystems->findItems( current->text( QgisCrsIdColumn ), Qt::MatchExactly | Qt::MatchRecursive, QgisCrsIdColumn );
790  if ( !nodes.isEmpty() )
791  lstCoordinateSystems->setCurrentItem( nodes.first() );
792 }
793 
794 void QgsProjectionSelectionTreeWidget::lstRecent_itemDoubleClicked( QTreeWidgetItem *current, int column )
795 {
796  Q_UNUSED( column )
797 
798  QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
799 
800  if ( !current )
801  {
802  QgsDebugMsgLevel( QStringLiteral( "no current item" ), 4 );
803  return;
804  }
805 
806  QList<QTreeWidgetItem *> nodes = lstCoordinateSystems->findItems( current->text( QgisCrsIdColumn ), Qt::MatchExactly | Qt::MatchRecursive, QgisCrsIdColumn );
807  if ( !nodes.isEmpty() )
809 }
810 
811 void QgsProjectionSelectionTreeWidget::updateFilter()
812 {
813  QString filterTxtCopy = leSearch->text();
814  filterTxtCopy.replace( QRegExp( "\\s+" ), QStringLiteral( ".*" ) );
815  QRegExp re( filterTxtCopy, Qt::CaseInsensitive );
816 
817  const bool hideDeprecated = cbxHideDeprecated->isChecked();
818 
819  auto filterTreeWidget = [ = ]( QTreeWidget * tree )
820  {
821  QTreeWidgetItemIterator itr( tree );
822  while ( *itr )
823  {
824  if ( ( *itr )->childCount() == 0 ) // it's an end node aka a projection
825  {
826  if ( hideDeprecated && ( *itr )->data( 0, RoleDeprecated ).toBool() )
827  {
828  ( *itr )->setHidden( true );
829  if ( ( *itr )->isSelected() )
830  {
831  ( *itr )->setSelected( false );
832  teProjection->clear();
833  }
834  }
835  else if ( ( *itr )->text( NameColumn ).contains( re )
836  || ( *itr )->text( AuthidColumn ).contains( re )
837  )
838  {
839  ( *itr )->setHidden( false );
840  QTreeWidgetItem *parent = ( *itr )->parent();
841  while ( parent )
842  {
843  parent->setExpanded( true );
844  parent->setHidden( false );
845  parent = parent->parent();
846  }
847  }
848  else
849  {
850  ( *itr )->setHidden( true );
851  }
852  }
853  else
854  {
855  ( *itr )->setHidden( true );
856  }
857  ++itr;
858  }
859  };
860 
861  // filter recent crs's
862  filterTreeWidget( lstRecent );
863 
864  // filter crs's
865  filterTreeWidget( lstCoordinateSystems );
866 }
867 
868 
870 {
871  // set flag to push selected projection to front in destructor
872  mPushProjectionToFront = true;
873 }
874 
875 
876 long QgsProjectionSelectionTreeWidget::getLargestCrsIdMatch( const QString &sql )
877 {
878  long srsId = 0;
879 
880  //
881  // Now perform the actual search
882  //
883 
884  sqlite3 *database = nullptr;
885  const char *tail = nullptr;
886  sqlite3_stmt *stmt = nullptr;
887  int result;
888 
889  // first we search the users db as any srsid there will be definition be greater than in sys db
890 
891  //check the db is available
892  QString databaseFileName = QgsApplication::qgisUserDatabaseFilePath();
893  if ( QFileInfo::exists( databaseFileName ) ) //only bother trying to open if the file exists
894  {
895  result = sqlite3_open_v2( databaseFileName.toUtf8().constData(), &database, SQLITE_OPEN_READONLY, nullptr );
896  if ( result )
897  {
898  // XXX This will likely never happen since on open, sqlite creates the
899  // database if it does not exist. But we checked earlier for its existence
900  // and aborted in that case. This is because we may be running from read only
901  // media such as live cd and don't want to force trying to create a db.
902  showDBMissingWarning( databaseFileName );
903  return 0;
904  }
905 
906  result = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail );
907  // XXX Need to free memory from the error msg if one is set
908  if ( result == SQLITE_OK && sqlite3_step( stmt ) == SQLITE_ROW )
909  {
910  QString srsIdString = QString::fromUtf8( ( char * )sqlite3_column_text( stmt, 0 ) );
911  srsId = srsIdString.toLong();
912  // close the sqlite3 statement
913  sqlite3_finalize( stmt );
914  sqlite3_close( database );
915  return srsId;
916  }
917  }
918  else
919  {
920  //only bother looking in srs.db if it wasn't found above
921  result = sqlite3_open_v2( mSrsDatabaseFileName.toUtf8().constData(), &database, SQLITE_OPEN_READONLY, nullptr );
922  if ( result )
923  {
924  QgsDebugMsg( QStringLiteral( "Can't open * user * database: %1" ).arg( sqlite3_errmsg( database ) ) );
925  //no need for assert because user db may not have been created yet
926  return 0;
927  }
928  }
929 
930  result = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail );
931  // XXX Need to free memory from the error msg if one is set
932  if ( result == SQLITE_OK && sqlite3_step( stmt ) == SQLITE_ROW )
933  {
934  QString srsIdString = QString::fromUtf8( ( char * )sqlite3_column_text( stmt, 0 ) );
935  srsId = srsIdString.toLong();
936  }
937 
938  // close the sqlite3 statement
939  sqlite3_finalize( stmt );
940  sqlite3_close( database );
941 
942  return srsId;
943 }
944 
945 void QgsProjectionSelectionTreeWidget::updateBoundsPreview()
946 {
947  QTreeWidgetItem *lvi = lstCoordinateSystems->currentItem();
948  if ( !lvi || ( lvi->text( QgisCrsIdColumn ).isEmpty() && !lvi->data( 0, RoleWkt ).isValid() ) )
949  return;
950 
951  QgsCoordinateReferenceSystem currentCrs = crs();
952  if ( !currentCrs.isValid() )
953  return;
954 
955  QgsRectangle rect = currentCrs.bounds();
956  QString extentString = tr( "Extent not known" );
957  mAreaCanvas->setPreviewRect( rect );
958  if ( !qgsDoubleNear( rect.area(), 0.0 ) )
959  {
960  extentString = QStringLiteral( "%1, %2, %3, %4" )
961  .arg( rect.xMinimum(), 0, 'f', 2 )
962  .arg( rect.yMinimum(), 0, 'f', 2 )
963  .arg( rect.xMaximum(), 0, 'f', 2 )
964  .arg( rect.yMaximum(), 0, 'f', 2 );
965  }
966 
967  const QString extentHtml = QStringLiteral( "<dt><b>%1</b></dt><dd>%2</dd>" ).arg( tr( "Extent" ), extentString );
968  const QString wktString = tr( "<dt><b>%1</b></dt><dd><code>%2</code></dd>" ).arg( tr( "WKT" ), currentCrs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED, true ).replace( '\n', QStringLiteral( "<br>" ) ).replace( ' ', QStringLiteral( "&nbsp;" ) ) );
969  const QString proj4String = tr( "<dt><b>%1</b></dt><dd><code>%2</code></dd>" ).arg( tr( "Proj4" ), currentCrs.toProj() );
970 
971 #ifdef Q_OS_WIN
972  const int smallerPointSize = std::max( font().pointSize() - 1, 8 ); // bit less on windows, due to poor rendering of small point sizes
973 #else
974  const int smallerPointSize = std::max( font().pointSize() - 2, 6 );
975 #endif
976 
977  teProjection->setText( QStringLiteral( "<div style=\"font-size: %1pt\"><h3>%2</h3><dl>" ).arg( smallerPointSize ).arg( selectedName() ) + wktString + proj4String + extentHtml + QStringLiteral( "</dl></div>" ) );
978 }
979 
980 QStringList QgsProjectionSelectionTreeWidget::authorities()
981 {
982  sqlite3 *database = nullptr;
983  const char *tail = nullptr;
984  sqlite3_stmt *stmt = nullptr;
985 
986  int result = sqlite3_open_v2( mSrsDatabaseFileName.toUtf8().constData(), &database, SQLITE_OPEN_READONLY, nullptr );
987  if ( result )
988  {
989  QgsDebugMsg( QStringLiteral( "Can't open * user * database: %1" ).arg( sqlite3_errmsg( database ) ) );
990  //no need for assert because user db may not have been created yet
991  return QStringList();
992  }
993 
994  QString sql = QStringLiteral( "select distinct auth_name from tbl_srs" );
995  result = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail );
996 
997  QStringList authorities;
998  if ( result == SQLITE_OK )
999  {
1000  while ( sqlite3_step( stmt ) == SQLITE_ROW )
1001  {
1002  authorities << QString::fromUtf8( ( char * )sqlite3_column_text( stmt, 0 ) );
1003  }
1004 
1005  }
1006 
1007  // close the sqlite3 statement
1008  sqlite3_finalize( stmt );
1009  sqlite3_close( database );
1010 
1011  return authorities;
1012 }
1013 
1014 QString QgsProjectionSelectionTreeWidget::sqlSafeString( const QString &theSQL ) const
1015 {
1016  QString retval = theSQL;
1017  retval.replace( '\\', QLatin1String( "\\\\" ) );
1018  retval.replace( '\"', QLatin1String( "\\\"" ) );
1019  retval.replace( '\'', QLatin1String( "\\'" ) );
1020  retval.replace( '%', QLatin1String( "\\%" ) );
1021  return retval;
1022 }
1023 
1024 void QgsProjectionSelectionTreeWidget::showDBMissingWarning( const QString &fileName )
1025 {
1026 
1027  QMessageBox::critical( this, tr( "Resource Location Error" ),
1028  tr( "Error reading database file from: \n %1\n"
1029  "Because of this the projection selector will not work…" )
1030  .arg( fileName ) );
1031 }
QgsProjectionSelectionTreeWidget::projectionDoubleClicked
void projectionDoubleClicked()
Emitted when a projection is double clicked in the list.
QgsCoordinateReferenceSystem::pushRecentCoordinateReferenceSystem
static void pushRecentCoordinateReferenceSystem(const QgsCoordinateReferenceSystem &crs)
Pushes a recently used CRS to the top of the recent CRS list.
Definition: qgscoordinatereferencesystem.cpp:3596
qgsprojectionselectiontreewidget.h
QgsApplication::getThemeIcon
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
Definition: qgsapplication.cpp:605
QgsProjectionSelectionTreeWidget::setShowNoProjection
void setShowNoProjection(bool show)
Sets whether a "no/invalid" projection option should be shown.
Definition: qgsprojectionselectiontreewidget.cpp:409
USER_CRS_START_ID
const int USER_CRS_START_ID
Magick number that determines whether a projection crsid is a system (srs.db) or user (~/....
Definition: qgis.h:701
qgsrectangle.h
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
sqlite3
struct sqlite3 sqlite3
Definition: qgscoordinatereferencesystem.h:55
QgsCoordinateReferenceSystem::WKT_PREFERRED
@ WKT_PREFERRED
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
Definition: qgscoordinatereferencesystem.h:678
crs
const QgsCoordinateReferenceSystem & crs
Definition: qgswfsgetfeature.cpp:105
QgsCoordinateReferenceSystem::fromOgcWmsCrs
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
Definition: qgscoordinatereferencesystem.cpp:200
QgsCoordinateReferenceSystem::fromProj
static QgsCoordinateReferenceSystem fromProj(const QString &proj)
Creates a CRS from a proj style formatted string.
Definition: qgscoordinatereferencesystem.cpp:226
QgsRectangle::xMaximum
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
QgsRectangle::area
double area() const
Returns the area of the rectangle.
Definition: qgsrectangle.h:218
qgis.h
QgsProjectionSelectionTreeWidget::previewRect
QgsRectangle previewRect() const
The initial "preview" rectangle for the bounds overview map.
Definition: qgsprojectionselectiontreewidget.cpp:302
QgsProjectionSelectionTreeWidget::QgsProjectionSelectionTreeWidget
QgsProjectionSelectionTreeWidget(QWidget *parent=nullptr)
Constructor for QgsProjectionSelectionTreeWidget.
Definition: qgsprojectionselectiontreewidget.cpp:31
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsRectangle
Definition: qgsrectangle.h:41
QgsCoordinateReferenceSystem::bounds
QgsRectangle bounds() const
Returns the approximate bounds for the region the CRS is usable within.
Definition: qgscoordinatereferencesystem.cpp:1434
qgsapplication.h
QgsProjectionSelectionTreeWidget::setShowBoundsMap
void setShowBoundsMap(bool show)
Sets whether to show the bounnds preview map.
Definition: qgsprojectionselectiontreewidget.cpp:419
QgsProjectionSelectionTreeWidget::crs
QgsCoordinateReferenceSystem crs() const
Returns the CRS currently selected in the widget.
Definition: qgsprojectionselectiontreewidget.cpp:379
QgsApplication::srsDatabaseFilePath
static QString srsDatabaseFilePath()
Returns the path to the srs.db file.
Definition: qgsapplication.cpp:998
QgsProjectionSelectionTreeWidget::showBoundsMap
bool showBoundsMap() const
Returns whether the bounds preview map is shown.
Definition: qgsprojectionselectiontreewidget.cpp:430
QgsProjectionSelectionTreeWidget::setCrs
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the initial crs to show within the dialog.
Definition: qgsprojectionselectiontreewidget.cpp:268
QgsCoordinateReferenceSystem::srsid
long srsid() const
Returns the internal CRS ID, if available.
Definition: qgscoordinatereferencesystem.cpp:1289
QgsProjectionSelectionTreeWidget::crsSelected
void crsSelected()
Emitted when a projection is selected in the widget.
qgsDoubleNear
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:315
QgsProjectionSelectionTreeWidget::resizeEvent
void resizeEvent(QResizeEvent *event) override
Definition: qgsprojectionselectiontreewidget.cpp:107
QgsCoordinateReferenceSystem::authid
QString authid() const
Returns the authority identifier for the CRS.
Definition: qgscoordinatereferencesystem.cpp:1299
QgsCoordinateReferenceSystem::toWkt
QString toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
Definition: qgscoordinatereferencesystem.cpp:1931
QgsCoordinateReferenceSystem::isValid
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Definition: qgscoordinatereferencesystem.cpp:902
QgsCoordinateReferenceSystem::toProj
QString toProj() const
Returns a Proj string representation of this CRS.
Definition: qgscoordinatereferencesystem.cpp:1398
QgsCoordinateReferenceSystem
Definition: qgscoordinatereferencesystem.h:206
QgsRectangle::yMaximum
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
QgsProjectionSelectionTreeWidget::showEvent
void showEvent(QShowEvent *event) override
Definition: qgsprojectionselectiontreewidget.cpp:118
QgsProjectionSelectionTreeWidget::pushProjectionToFront
void pushProjectionToFront()
Marks the current selected projection for push to front of recent projections list.
Definition: qgsprojectionselectiontreewidget.cpp:869
QgsProjectionSelectionTreeWidget::showNoProjection
bool showNoProjection() const
Returns whether the "no/invalid" projection option is shown.
Definition: qgsprojectionselectiontreewidget.cpp:425
QgsProjectionSelectionTreeWidget::~QgsProjectionSelectionTreeWidget
~QgsProjectionSelectionTreeWidget() override
Definition: qgsprojectionselectiontreewidget.cpp:92
qgssettings.h
QgsMessageLog::logMessage
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Definition: qgsmessagelog.cpp:27
QgsApplication::qgisUserDatabaseFilePath
static QString qgisUserDatabaseFilePath()
Returns the path to the user qgis.db file.
Definition: qgsapplication.cpp:978
QgsRectangle::yMinimum
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
Qgis::Critical
@ Critical
Definition: qgis.h:105
qgslogger.h
QgsProjectionSelectionTreeWidget::setOgcWmsCrsFilter
void setOgcWmsCrsFilter(const QSet< QString > &crsFilter)
filters this widget by the given CRSs
Definition: qgsprojectionselectiontreewidget.cpp:457
QgsProjectionSelectionTreeWidget::setPreviewRect
void setPreviewRect(const QgsRectangle &rect)
Sets the initial "preview" rectangle for the bounds overview map.
Definition: qgsprojectionselectiontreewidget.cpp:297
QgsProjectionSelectionTreeWidget::initialized
void initialized()
Notifies others that the widget is now fully initialized, including deferred selection of projection.
qgscoordinatereferencesystem.h
QgsCoordinateReferenceSystem::fromWkt
static QgsCoordinateReferenceSystem fromWkt(const QString &wkt)
Creates a CRS from a WKT spatial ref sys definition string.
Definition: qgscoordinatereferencesystem.cpp:233
QgsRectangle::xMinimum
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
QgsCoordinateReferenceSystem::recentCoordinateReferenceSystems
static QList< QgsCoordinateReferenceSystem > recentCoordinateReferenceSystems()
Returns a list of recently used CRS.
Definition: qgscoordinatereferencesystem.cpp:3565
QgsProjectionSelectionTreeWidget::hasValidSelection
bool hasValidSelection() const
Returns true if the current selection in the widget is a valid choice.
Definition: qgsprojectionselectiontreewidget.cpp:435
qgsmessagelog.h