QGIS API Documentation 3.32.0-Lima (311a8cb8a6)
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"
25#include "qgsdatums.h"
26#include "qgsprojoperation.h"
27#include "qgsstringutils.h"
28#include "qgsunittypes.h"
29
30//qt includes
31#include <QAction>
32#include <QToolButton>
33#include <QMenu>
34#include <QFileInfo>
35#include <QHeaderView>
36#include <QResizeEvent>
37#include <QMessageBox>
38#include <QRegularExpression>
39
41 : QWidget( parent )
42{
43 setupUi( this );
44
45 QFont f = teProjection->font();
46 f.setPointSize( f.pointSize() - 2 );
47 teProjection->setFont( f );
48
49 leSearch->setShowSearchIcon( true );
50
51 connect( lstCoordinateSystems, &QTreeWidget::itemDoubleClicked, this, &QgsProjectionSelectionTreeWidget::lstCoordinateSystems_itemDoubleClicked );
52 connect( lstRecent, &QTreeWidget::itemDoubleClicked, this, &QgsProjectionSelectionTreeWidget::lstRecent_itemDoubleClicked );
53 connect( lstCoordinateSystems, &QTreeWidget::currentItemChanged, this, &QgsProjectionSelectionTreeWidget::lstCoordinateSystems_currentItemChanged );
54 connect( lstRecent, &QTreeWidget::currentItemChanged, this, &QgsProjectionSelectionTreeWidget::lstRecent_currentItemChanged );
55 connect( cbxHideDeprecated, &QCheckBox::stateChanged, this, &QgsProjectionSelectionTreeWidget::updateFilter );
56 connect( leSearch, &QgsFilterLineEdit::textChanged, this, &QgsProjectionSelectionTreeWidget::updateFilter );
57
58 mAreaCanvas->setVisible( mShowMap );
59
60 // Get the full path name to the sqlite3 spatial reference database.
61 mSrsDatabaseFileName = QgsApplication::srsDatabaseFilePath();
62
63 lstCoordinateSystems->header()->setSectionResizeMode( AuthidColumn, QHeaderView::Stretch );
64 lstCoordinateSystems->header()->resizeSection( QgisCrsIdColumn, 0 );
65 lstCoordinateSystems->header()->setSectionResizeMode( QgisCrsIdColumn, QHeaderView::Fixed );
66
67 // Hide (internal) ID column
68 lstCoordinateSystems->setColumnHidden( QgisCrsIdColumn, true );
69 lstRecent->header()->setSectionResizeMode( AuthidColumn, QHeaderView::Stretch );
70 lstRecent->header()->resizeSection( QgisCrsIdColumn, 0 );
71 lstRecent->header()->setSectionResizeMode( QgisCrsIdColumn, QHeaderView::Fixed );
72 lstRecent->setColumnHidden( QgisCrsIdColumn, true );
73
74 // Clear Crs Column
75 lstRecent->header()->setMinimumSectionSize( 10 );
76 lstRecent->header()->setStretchLastSection( false );
77 lstRecent->header()->resizeSection( ClearColumn, 20 );
78
79 // Clear recent crs context menu
80 lstRecent->setContextMenuPolicy( Qt::CustomContextMenu );
81 connect( lstRecent, &QTreeWidget::customContextMenuRequested, this, [this]( const QPoint & pos )
82 {
83 // If list is empty, do nothing
84 if ( lstRecent->topLevelItemCount() == 0 )
85 return;
86 QMenu menu;
87 // Clear selected
88 QTreeWidgetItem *currentItem = lstRecent->itemAt( pos );
89 if ( currentItem )
90 {
91 QAction *clearSelected = menu.addAction( QgsApplication::getThemeIcon( "/mIconClearItem.svg" ), tr( "Remove selected CRS from recently used CRS" ) );
92 connect( clearSelected, &QAction::triggered, this, [this, currentItem ] { removeRecentCrsItem( currentItem ); } );
93 menu.addSeparator();
94 }
95 // Clear all
96 QAction *clearAll = menu.addAction( QgsApplication::getThemeIcon( "/console/iconClearConsole.svg" ), tr( "Clear all recently used CRS" ) );
97 connect( clearAll, &QAction::triggered, this, &QgsProjectionSelectionTreeWidget::clearRecentCrs );
98 menu.exec( lstRecent->viewport()->mapToGlobal( pos ) );
99 } );
100
101 // Install event fiter to catch delete key press on the recent crs list
102 lstRecent->installEventFilter( this );
103
105
106 mCheckBoxNoProjection->setHidden( true );
107 mCheckBoxNoProjection->setEnabled( false );
108 connect( mCheckBoxNoProjection, &QCheckBox::toggled, this, [ = ]
109 {
110 if ( !mBlockSignals )
111 {
112 emit crsSelected();
114 }
115 } );
116 connect( mCheckBoxNoProjection, &QCheckBox::toggled, this, [ = ]( bool checked )
117 {
118 if ( mCheckBoxNoProjection->isEnabled() )
119 {
120 mFrameProjections->setDisabled( checked );
121 }
122 } );
123
124 QgsSettings settings;
125 mSplitter->restoreState( settings.value( QStringLiteral( "Windows/ProjectionSelector/splitterState" ) ).toByteArray() );
126}
127
129{
130 QgsSettings settings;
131 settings.setValue( QStringLiteral( "Windows/ProjectionSelector/splitterState" ), mSplitter->saveState() );
132
133 // Push current projection to front, only if set
134 const QgsCoordinateReferenceSystem selectedCrs = crs();
135 if ( selectedCrs.isValid() )
137}
138
140{
141 lstCoordinateSystems->header()->resizeSection( NameColumn, event->size().width() - 240 );
142 lstCoordinateSystems->header()->resizeSection( AuthidColumn, 240 );
143 lstCoordinateSystems->header()->resizeSection( QgisCrsIdColumn, 0 );
144
145 lstRecent->header()->resizeSection( NameColumn, event->size().width() - 260 );
146 lstRecent->header()->resizeSection( AuthidColumn, 240 );
147 lstRecent->header()->resizeSection( QgisCrsIdColumn, 0 );
148 lstRecent->header()->resizeSection( ClearColumn, 20 );
149}
150
152{
153 if ( mInitialized )
154 return;
155
156 // ensure the projection list view is actually populated
157 // before we show this widget
158 loadCrsList( &mCrsFilter );
159 loadUserCrsList( &mCrsFilter );
160
161 if ( !mRecentProjListDone )
162 {
163 for ( const QgsCoordinateReferenceSystem &crs : std::as_const( mRecentProjections ) )
164 insertRecent( crs );
165 mRecentProjListDone = true;
166 }
167
168 // apply deferred selection
169 mBlockSignals = true; // we've already emitted the signal, when the deferred crs was first set
170 applySelection();
171 mBlockSignals = false;
172
173 emit initialized();
174
175 // Pass up the inheritance hierarchy
176 QWidget::showEvent( event );
177 mInitialized = true;
178}
179
180
181bool QgsProjectionSelectionTreeWidget::eventFilter( QObject *obj, QEvent *ev )
182{
183 if ( obj != lstRecent )
184 return false;
185
186 if ( ev->type() != QEvent::KeyPress )
187 return false;
188
189 QKeyEvent *keyEvent = static_cast<QKeyEvent *>( ev );
190 if ( keyEvent->matches( QKeySequence::Delete ) )
191 {
192 removeRecentCrsItem( lstRecent->currentItem() );
193 return true;
194 }
195
196 return false;
197}
198
199QString QgsProjectionSelectionTreeWidget::ogcWmsCrsFilterAsSqlExpression( QSet<QString> *crsFilter )
200{
201 QString sqlExpression = QStringLiteral( "1" ); // it's "SQL" for "true"
202 QMap<QString, QStringList> authParts;
203
204 if ( !crsFilter )
205 return sqlExpression;
206
207 /*
208 Ref: WMS 1.3.0, section 6.7.3 "Layer CRS":
209
210 Every Layer CRS has an identifier that is a character string. Two types of
211 Layer CRS identifiers are permitted: "label" and "URL" identifiers:
212
213 Label: The identifier includes a namespace prefix, a colon, a numeric or
214 string code, and in some instances a comma followed by additional
215 parameters. This International Standard defines three namespaces:
216 CRS, EpsgCrsId and AUTO2 [...]
217
218 URL: The identifier is a fully-qualified Uniform Resource Locator that
219 references a publicly-accessible file containing a definition of the CRS
220 that is compliant with ISO 19111.
221 */
222
223 // iterate through all incoming CRSs
224
225 const auto authIds { *crsFilter };
226 for ( const QString &auth_id : authIds )
227 {
228 QStringList parts = auth_id.split( ':' );
229
230 if ( parts.size() < 2 )
231 continue;
232
233 authParts[ parts.at( 0 ).toUpper()].append( parts.at( 1 ).toUpper() );
234 }
235
236 if ( authParts.isEmpty() )
237 return sqlExpression;
238
239 if ( !authParts.isEmpty() )
240 {
241 QString prefix = QStringLiteral( " AND (" );
242 for ( auto it = authParts.constBegin(); it != authParts.constEnd(); ++it )
243 {
244 sqlExpression += QStringLiteral( "%1(upper(auth_name)='%2' AND upper(auth_id) IN ('%3'))" )
245 .arg( prefix,
246 it.key(),
247 it.value().join( QLatin1String( "','" ) ) );
248 prefix = QStringLiteral( " OR " );
249 }
250 sqlExpression += ')';
251 }
252
253 QgsDebugMsgLevel( "exiting with '" + sqlExpression + "'.", 4 );
254
255 return sqlExpression;
256}
257
258void QgsProjectionSelectionTreeWidget::applySelection( int column, QString value )
259{
260 if ( !mProjListDone || !mUserProjListDone )
261 {
262 // defer selection until loaded
263 mSearchColumn = column;
264 mSearchValue = value;
265 return;
266 }
267
268 if ( column == QgsProjectionSelectionTreeWidget::None )
269 {
270 // invoked deferred selection
271 column = mSearchColumn;
272 value = mSearchValue;
273
274 mSearchColumn = QgsProjectionSelectionTreeWidget::None;
275 mSearchValue.clear();
276 }
277
278 if ( column == QgsProjectionSelectionTreeWidget::None )
279 return;
280
281 QList<QTreeWidgetItem *> nodes = lstCoordinateSystems->findItems( value, Qt::MatchExactly | Qt::MatchRecursive, column );
282 if ( !nodes.isEmpty() )
283 {
284 QgsDebugMsgLevel( QStringLiteral( "found %1,%2" ).arg( column ).arg( value ), 4 );
285 lstCoordinateSystems->setCurrentItem( nodes.first() );
286 }
287 else
288 {
289 QgsDebugMsgLevel( QStringLiteral( "nothing found for %1,%2" ).arg( column ).arg( value ), 4 );
290 // deselect the selected item to avoid confusing the user
291 lstCoordinateSystems->clearSelection();
292 lstRecent->clearSelection();
293 teProjection->clear();
294 }
295}
296
297void QgsProjectionSelectionTreeWidget::insertRecent( const QgsCoordinateReferenceSystem &crs )
298{
299 if ( !mProjListDone || !mUserProjListDone )
300 return;
301
302 QList<QTreeWidgetItem *> nodes = lstCoordinateSystems->findItems( QString::number( crs.srsid() ), Qt::MatchExactly | Qt::MatchRecursive, QgisCrsIdColumn );
303 if ( nodes.isEmpty() )
304 return;
305
306 QTreeWidgetItem *item = new QTreeWidgetItem( lstRecent, QStringList()
307 << nodes.first()->text( NameColumn )
308 << nodes.first()->text( AuthidColumn )
309 << nodes.first()->text( QgisCrsIdColumn ) );
310
311 // Insert clear button in the last column
312 QToolButton *clearButton = new QToolButton();
313 clearButton->setIcon( QgsApplication::getThemeIcon( "/mIconClearItem.svg" ) );
314 clearButton->setAutoRaise( true );
315 clearButton->setToolTip( tr( "Remove from recently used CRS" ) );
316 connect( clearButton, &QToolButton::clicked, this, [this, item] { removeRecentCrsItem( item ); } );
317 lstRecent->setItemWidget( item, ClearColumn, clearButton );
318
319
320 lstRecent->insertTopLevelItem( 0, item );
321
322}
323
324// note this line just returns the projection name!
325QString QgsProjectionSelectionTreeWidget::selectedName()
326{
327 // return the selected wkt name from the list view
328 QTreeWidgetItem *lvi = lstCoordinateSystems->currentItem();
329 return lvi ? lvi->text( NameColumn ) : QString();
330}
331
333{
334 if ( !crs.isValid() )
335 {
336 mCheckBoxNoProjection->setChecked( true );
337 }
338 else
339 {
340 bool changed = false;
341 if ( !mInitialized )
342 {
343 changed = mDeferredLoadCrs != crs;
344 mDeferredLoadCrs = crs;
345 }
346 mBlockSignals = true;
347 mCheckBoxNoProjection->setChecked( false );
348 mBlockSignals = false;
349
350 if ( !crs.authid().isEmpty() )
351 applySelection( AuthidColumn, crs.authid() );
352 else
353 loadUnknownCrs( crs );
354 if ( changed )
355 {
356 emit crsSelected();
358 }
359 }
360}
361
363{
364 mAreaCanvas->setCanvasRect( rect );
365}
366
368{
369 return mAreaCanvas->canvasRect();
370}
371
372QString QgsProjectionSelectionTreeWidget::expressionForItem( QTreeWidgetItem *item, const QString &expression ) const
373{
374 // Only return the attribute if the selected item that has an srs_id.
375 // This prevents error if the user selects a top-level node rather
376 // than an actual coordinate system
377 if ( !item || item->text( QgisCrsIdColumn ).isEmpty() )
378 return QString();
379
380
381 // Determine if this is a user projection or a system on
382 // user projection defs all have srs_id >= 100000
383 QString databaseFileName;
384 if ( item->text( QgisCrsIdColumn ).toLong() >= USER_CRS_START_ID )
385 {
386 databaseFileName = QgsApplication::qgisUserDatabaseFilePath();
387 if ( !QFileInfo::exists( databaseFileName ) )
388 {
389 return QString();
390 }
391 }
392 else
393 {
394 databaseFileName = mSrsDatabaseFileName;
395 }
396
397 // set up the database
398 // XXX We could probably hold the database open for the life of this object,
399 // assuming that it will never be used anywhere else. Given the low overhead,
400 // opening it each time seems to be a reasonable approach at this time.
401 sqlite3 *database = nullptr;
402 int rc = sqlite3_open_v2( databaseFileName.toUtf8().constData(), &database, SQLITE_OPEN_READONLY, nullptr );
403 if ( rc )
404 {
405 QgsMessageLog::logMessage( tr( "Resource Location Error" ), tr( "Error reading database file from: \n %1\n"
406 "Because of this the projection selector will not work…" ).arg( databaseFileName ),
407 Qgis::MessageLevel::Critical );
408 return QString();
409 }
410
411 // prepare the sql statement
412 const char *tail = nullptr;
413 sqlite3_stmt *stmt = nullptr;
414 QString sql = QStringLiteral( "select %1 from tbl_srs where srs_id=%2" )
415 .arg( expression,
416 item->text( QgisCrsIdColumn ) );
417
418 QgsDebugMsgLevel( QStringLiteral( "Finding selected attribute using : %1" ).arg( sql ), 4 );
419 rc = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail );
420 // XXX Need to free memory from the error msg if one is set
421 QString attributeValue;
422 if ( rc == SQLITE_OK && sqlite3_step( stmt ) == SQLITE_ROW )
423 {
424 // get the first row of the result set
425 attributeValue = QString::fromUtf8( ( char * )sqlite3_column_text( stmt, 0 ) );
426 }
427
428 // close the statement
429 sqlite3_finalize( stmt );
430 // close the database
431 sqlite3_close( database );
432
433 // return the srs
434 return attributeValue;
435}
436
437
438
439QgsCoordinateReferenceSystem QgsProjectionSelectionTreeWidget::crsForItem( QTreeWidgetItem *item ) const
440{
441 if ( mCheckBoxNoProjection->isEnabled() && mCheckBoxNoProjection->isChecked() )
443
444 if ( !mInitialized && mDeferredLoadCrs.isValid() )
445 return mDeferredLoadCrs;
446
447 const QString srsIdString = expressionForItem( item, QStringLiteral( "srs_id" ) );
448 if ( !srsIdString.isEmpty() )
449 {
450 int srid = srsIdString.toLong();
451 if ( srid >= USER_CRS_START_ID )
452 return QgsCoordinateReferenceSystem::fromOgcWmsCrs( QStringLiteral( "USER:%1" ).arg( srid ) );
453 else
454 return QgsCoordinateReferenceSystem::fromOgcWmsCrs( expressionForItem( item, QStringLiteral( "upper(auth_name||':'||auth_id)" ) ) );
455 }
456 else
457 {
458 // custom CRS
459 if ( item && item->data( 0, RoleWkt ).isValid() )
460 return QgsCoordinateReferenceSystem::fromWkt( item->data( 0, RoleWkt ).toString() );
461 else if ( item && item->data( 0, RoleProj ).isValid() )
462 return QgsCoordinateReferenceSystem::fromProj( item->data( 0, RoleProj ).toString() );
463 else
465 }
466}
467
469{
470 return crsForItem( lstCoordinateSystems->currentItem() );
471}
472
474{
475 mCheckBoxNoProjection->setVisible( show );
476 mCheckBoxNoProjection->setEnabled( show );
477 if ( show )
478 {
479 mFrameProjections->setDisabled( mCheckBoxNoProjection->isChecked() );
480 }
481}
482
484{
485 mShowMap = show;
486 mAreaCanvas->setVisible( show );
487}
488
490{
491 return !mCheckBoxNoProjection->isHidden();
492}
493
495{
496 mCheckBoxNoProjection->setText( text );
497}
498
500{
501 return mShowMap;
502}
503
505{
506 QTreeWidgetItem *item = lstCoordinateSystems->currentItem();
507 if ( mCheckBoxNoProjection->isChecked() )
508 return true;
509 else if ( !mInitialized && mDeferredLoadCrs.isValid() )
510 return true;
511 else
512 return item && ( !item->text( QgisCrsIdColumn ).isEmpty() || item->data( 0, RoleWkt ).isValid() );
513}
514
515long QgsProjectionSelectionTreeWidget::selectedCrsId()
516{
517 QTreeWidgetItem *item = lstCoordinateSystems->currentItem();
518
519 if ( item && !item->text( QgisCrsIdColumn ).isEmpty() )
520 return lstCoordinateSystems->currentItem()->text( QgisCrsIdColumn ).toLong();
521 else
522 return 0;
523}
524
525
526void QgsProjectionSelectionTreeWidget::setOgcWmsCrsFilter( const QSet<QString> &crsFilter )
527{
528 mCrsFilter = crsFilter;
529 mProjListDone = false;
530 mUserProjListDone = false;
531 lstCoordinateSystems->clear();
532}
533
534void QgsProjectionSelectionTreeWidget::loadUserCrsList( QSet<QString> *crsFilter )
535{
536 if ( mUserProjListDone )
537 return;
538
539 QgsDebugMsgLevel( QStringLiteral( "Fetching user projection list..." ), 4 );
540
541 // User defined coordinate system node
542 // Make in an italic font to distinguish them from real projections
543 mUserProjList = new QTreeWidgetItem( lstCoordinateSystems, QStringList( tr( "User Defined Coordinate Systems" ) ) );
544 mUserProjList->setFlags( mUserProjList->flags() & ~Qt::ItemIsSelectable );
545
546 QFont fontTemp = mUserProjList->font( 0 );
547 fontTemp.setItalic( true );
548 fontTemp.setBold( true );
549 mUserProjList->setFont( 0, fontTemp );
550 mUserProjList->setIcon( 0, QgsApplication::getThemeIcon( QStringLiteral( "/user.svg" ) ) );
551
552 const QList<QgsCoordinateReferenceSystemRegistry::UserCrsDetails> userCrsList = QgsApplication::coordinateReferenceSystemRegistry()->userCrsList();
553 for ( const QgsCoordinateReferenceSystemRegistry::UserCrsDetails &details : userCrsList )
554 {
555 const QString authid = QStringLiteral( "USER:%1" ).arg( details.id );
556 if ( crsFilter && !crsFilter->isEmpty() && !crsFilter->contains( authid ) && !crsFilter->contains( authid.toLower() ) )
557 continue;
558
559 QTreeWidgetItem *newItem = new QTreeWidgetItem( mUserProjList, QStringList() << details.name );
560 newItem->setText( QgisCrsIdColumn, QString::number( details.id ) );
561 newItem->setText( AuthidColumn, authid );
562 }
563
564 mUserProjListDone = true;
565}
566
567void QgsProjectionSelectionTreeWidget::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 mGeoList->setFlags( mGeoList->flags() & ~Qt::ItemIsSelectable );
581
582 QFont fontTemp = mGeoList->font( 0 );
583 fontTemp.setItalic( true );
584 fontTemp.setBold( true );
585 mGeoList->setFont( 0, fontTemp );
586 mGeoList->setIcon( 0, QgsApplication::getThemeIcon( QStringLiteral( "/mIconProjectionEnabled.svg" ) ) );
587
588 // Projected coordinate system node
589 mProjList = new QTreeWidgetItem( lstCoordinateSystems, QStringList( tr( "Projected Coordinate Systems" ) ) );
590 mProjList->setFlags( mProjList->flags() & ~Qt::ItemIsSelectable );
591
592 fontTemp = mProjList->font( 0 );
593 fontTemp.setItalic( true );
594 fontTemp.setBold( true );
595 mProjList->setFont( 0, fontTemp );
596 mProjList->setIcon( 0, QgsApplication::getThemeIcon( QStringLiteral( "/transformed.svg" ) ) );
597
598 //bail out in case the projections db does not exist
599 //this is necessary in case the pc is running linux with a
600 //read only filesystem because otherwise sqlite will try
601 //to create the db file on the fly
602
603 if ( !QFileInfo::exists( mSrsDatabaseFileName ) )
604 {
605 mProjListDone = true;
606 return;
607 }
608
609 // open the database containing the spatial reference data
610 sqlite3 *database = nullptr;
611 int rc = sqlite3_open_v2( mSrsDatabaseFileName.toUtf8().constData(), &database, SQLITE_OPEN_READONLY, nullptr );
612 if ( rc )
613 {
614 // XXX This will likely never happen since on open, sqlite creates the
615 // database if it does not exist.
616 showDBMissingWarning( mSrsDatabaseFileName );
617 return;
618 }
619
620 const char *tail = nullptr;
621 sqlite3_stmt *stmt = nullptr;
622 // Set up the query to retrieve the projection information needed to populate the list
623 //note I am giving the full field names for clarity here and in case someone
624 //changes the underlying view TS
625 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" )
626 .arg( sqlFilter );
627
628 rc = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail );
629 // XXX Need to free memory from the error msg if one is set
630 if ( rc == SQLITE_OK )
631 {
632 QTreeWidgetItem *newItem = nullptr;
633 // Cache some stuff to speed up creating of the list of projected
634 // spatial reference systems
635 QString previousSrsType;
636 QTreeWidgetItem *previousSrsTypeNode = nullptr;
637
638 while ( sqlite3_step( stmt ) == SQLITE_ROW )
639 {
640 // check to see if the srs is geographic
641 int isGeo = sqlite3_column_int( stmt, 3 );
642 if ( isGeo )
643 {
644 // this is a geographic coordinate system
645 // Add it to the tree (field 0)
646 newItem = new QTreeWidgetItem( mGeoList, QStringList( QString::fromUtf8( ( char * )sqlite3_column_text( stmt, 0 ) ) ) );
647
648 // display the authority name (field 2) in the second column of the list view
649 newItem->setText( AuthidColumn, QString::fromUtf8( ( char * )sqlite3_column_text( stmt, 2 ) ) );
650
651 // display the qgis srs_id (field 1) in the third column of the list view
652 newItem->setText( QgisCrsIdColumn, QString::fromUtf8( ( char * )sqlite3_column_text( stmt, 1 ) ) );
653 }
654 else
655 {
656 // This is a projected srs
657 QTreeWidgetItem *node = nullptr;
658 QString srsType = QString::fromUtf8( ( char * )sqlite3_column_text( stmt, 4 ) );
659 if ( srsType.isEmpty() )
660 srsType = tr( "Other" );
661
662 // Find the node for this type and add the projection to it
663 // If the node doesn't exist, create it
664 if ( srsType == previousSrsType )
665 {
666 node = previousSrsTypeNode;
667 }
668 else
669 {
670 // Different from last one, need to search
671 QList<QTreeWidgetItem *> nodes = lstCoordinateSystems->findItems( srsType, Qt::MatchExactly | Qt::MatchRecursive, NameColumn );
672 if ( nodes.isEmpty() )
673 {
674 // the node doesn't exist -- create it
675 // Make in an italic font to distinguish them from real projections
676 node = new QTreeWidgetItem( mProjList, QStringList( srsType ) );
677 node->setFlags( node->flags() & ~Qt::ItemIsSelectable );
678
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( AuthidColumn, 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( QgisCrsIdColumn, 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, RoleDeprecated, 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
716void QgsProjectionSelectionTreeWidget::loadUnknownCrs( const QgsCoordinateReferenceSystem &crs )
717{
718 if ( !mUnknownList )
719 {
720 mUnknownList = new QTreeWidgetItem( lstCoordinateSystems, QStringList( tr( "Custom Coordinate Systems" ) ) );
721 mUnknownList->setFlags( mUnknownList->flags() & ~Qt::ItemIsSelectable );
722
723 QFont fontTemp = mUnknownList->font( 0 );
724 fontTemp.setItalic( true );
725 fontTemp.setBold( true );
726 mUnknownList->setFont( 0, fontTemp );
727 mUnknownList->setIcon( 0, QgsApplication::getThemeIcon( QStringLiteral( "/user.svg" ) ) );
728 }
729
730 QTreeWidgetItem *newItem = new QTreeWidgetItem( mUnknownList, QStringList( crs.description().isEmpty() ? QObject::tr( "Custom CRS" ) : crs.description() ) );
731 newItem->setData( 0, RoleWkt, crs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ) );
732 newItem->setData( 0, RoleProj, crs.toProj() );
733
734 lstCoordinateSystems->setCurrentItem( newItem );
735}
736
737// New coordinate system selected from the list
738void QgsProjectionSelectionTreeWidget::lstCoordinateSystems_currentItemChanged( QTreeWidgetItem *current, QTreeWidgetItem * )
739{
740 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
741
742 if ( !current )
743 {
744 QgsDebugMsgLevel( QStringLiteral( "no current item" ), 4 );
745 return;
746 }
747
748 lstCoordinateSystems->scrollToItem( current );
749
750 // If the item has children, it's not an end node in the tree, and
751 // hence is just a grouping thingy, not an actual CRS.
752 if ( current->childCount() == 0 )
753 {
754 // Found a real CRS
755 if ( !mBlockSignals )
756 {
757 emit crsSelected();
758 emit hasValidSelectionChanged( true );
759 }
760
761 updateBoundsPreview();
762
763 const QString crsId = current->text( QgisCrsIdColumn );
764 if ( !crsId.isEmpty() )
765 {
766 QList<QTreeWidgetItem *> nodes = lstRecent->findItems( current->text( QgisCrsIdColumn ), Qt::MatchExactly, QgisCrsIdColumn );
767 if ( !nodes.isEmpty() )
768 {
769 QgsDebugMsgLevel( QStringLiteral( "found srs %1 in recent" ).arg( current->text( QgisCrsIdColumn ) ), 4 );
770 lstRecent->setCurrentItem( nodes.first() );
771 }
772 else
773 {
774 QgsDebugMsgLevel( QStringLiteral( "srs %1 not recent" ).arg( current->text( QgisCrsIdColumn ) ), 4 );
775 lstRecent->clearSelection();
776 lstCoordinateSystems->setFocus( Qt::OtherFocusReason );
777 }
778 }
779 else
780 {
781 lstRecent->clearSelection();
782 lstCoordinateSystems->setFocus( Qt::OtherFocusReason );
783 }
784 }
785 else
786 {
787 // Not a CRS - remove the highlight so the user doesn't get too confused
788 current->setSelected( false );
789 teProjection->clear();
790 lstRecent->clearSelection();
791 emit hasValidSelectionChanged( false );
792 }
793}
794
795void QgsProjectionSelectionTreeWidget::lstCoordinateSystems_itemDoubleClicked( QTreeWidgetItem *current, int column )
796{
797 Q_UNUSED( column )
798
799 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
800
801 if ( !current )
802 {
803 QgsDebugMsgLevel( QStringLiteral( "no current item" ), 4 );
804 return;
805 }
806
807 // If the item has children, it's not an end node in the tree, and
808 // hence is just a grouping thingy, not an actual CRS.
809 if ( current->childCount() == 0 )
811}
812
813void QgsProjectionSelectionTreeWidget::lstRecent_currentItemChanged( QTreeWidgetItem *current, QTreeWidgetItem * )
814{
815 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
816
817 if ( !current )
818 {
819 QgsDebugMsgLevel( QStringLiteral( "no current item" ), 4 );
820 return;
821 }
822
823 lstRecent->scrollToItem( current );
824
825 QList<QTreeWidgetItem *> nodes = lstCoordinateSystems->findItems( current->text( QgisCrsIdColumn ), Qt::MatchExactly | Qt::MatchRecursive, QgisCrsIdColumn );
826 if ( !nodes.isEmpty() )
827 lstCoordinateSystems->setCurrentItem( nodes.first() );
828}
829
830void QgsProjectionSelectionTreeWidget::lstRecent_itemDoubleClicked( QTreeWidgetItem *current, int column )
831{
832 Q_UNUSED( column )
833
834 QgsDebugMsgLevel( QStringLiteral( "Entered." ), 4 );
835
836 if ( !current )
837 {
838 QgsDebugMsgLevel( QStringLiteral( "no current item" ), 4 );
839 return;
840 }
841
842 QList<QTreeWidgetItem *> nodes = lstCoordinateSystems->findItems( current->text( QgisCrsIdColumn ), Qt::MatchExactly | Qt::MatchRecursive, QgisCrsIdColumn );
843 if ( !nodes.isEmpty() )
845}
846
847void QgsProjectionSelectionTreeWidget::updateFilter()
848{
849 QString filterTxtCopy = QgsStringUtils::qRegExpEscape( leSearch->text() );
850 const thread_local QRegularExpression filterRx( QStringLiteral( "\\s+" ) );
851 filterTxtCopy.replace( filterRx, QStringLiteral( ".*" ) );
852 const QRegularExpression re( filterTxtCopy, QRegularExpression::PatternOption::CaseInsensitiveOption );
853
854 const bool hideDeprecated = cbxHideDeprecated->isChecked();
855
856 auto filterTreeWidget = [ = ]( QTreeWidget * tree )
857 {
858 QTreeWidgetItemIterator itr( tree );
859 while ( *itr )
860 {
861 if ( ( *itr )->childCount() == 0 ) // it's an end node aka a projection
862 {
863 if ( hideDeprecated && ( *itr )->data( 0, RoleDeprecated ).toBool() )
864 {
865 ( *itr )->setHidden( true );
866 if ( ( *itr )->isSelected() )
867 {
868 ( *itr )->setSelected( false );
869 teProjection->clear();
870 }
871 }
872 else if ( ( *itr )->text( NameColumn ).contains( re )
873 || ( *itr )->text( AuthidColumn ).contains( re )
874 )
875 {
876 ( *itr )->setHidden( false );
877 QTreeWidgetItem *parent = ( *itr )->parent();
878 while ( parent )
879 {
880 parent->setExpanded( true );
881 parent->setHidden( false );
882 parent = parent->parent();
883 }
884 }
885 else
886 {
887 ( *itr )->setHidden( true );
888 }
889 }
890 else
891 {
892 ( *itr )->setHidden( true );
893 }
894 ++itr;
895 }
896 };
897
898 // filter recent crs's
899 filterTreeWidget( lstRecent );
900
901 // filter crs's
902 filterTreeWidget( lstCoordinateSystems );
903}
904
906{
907}
908
909long QgsProjectionSelectionTreeWidget::getLargestCrsIdMatch( const QString &sql )
910{
911 long srsId = 0;
912
913 //
914 // Now perform the actual search
915 //
916
917 sqlite3 *database = nullptr;
918 const char *tail = nullptr;
919 sqlite3_stmt *stmt = nullptr;
920 int result;
921
922 // first we search the users db as any srsid there will be definition be greater than in sys db
923
924 //check the db is available
925 QString databaseFileName = QgsApplication::qgisUserDatabaseFilePath();
926 if ( QFileInfo::exists( databaseFileName ) ) //only bother trying to open if the file exists
927 {
928 result = sqlite3_open_v2( databaseFileName.toUtf8().constData(), &database, SQLITE_OPEN_READONLY, nullptr );
929 if ( result )
930 {
931 // XXX This will likely never happen since on open, sqlite creates the
932 // database if it does not exist. But we checked earlier for its existence
933 // and aborted in that case. This is because we may be running from read only
934 // media such as live cd and don't want to force trying to create a db.
935 showDBMissingWarning( databaseFileName );
936 return 0;
937 }
938
939 result = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail );
940 // XXX Need to free memory from the error msg if one is set
941 if ( result == SQLITE_OK && sqlite3_step( stmt ) == SQLITE_ROW )
942 {
943 QString srsIdString = QString::fromUtf8( ( char * )sqlite3_column_text( stmt, 0 ) );
944 srsId = srsIdString.toLong();
945 // close the sqlite3 statement
946 sqlite3_finalize( stmt );
947 sqlite3_close( database );
948 return srsId;
949 }
950 }
951 else
952 {
953 //only bother looking in srs.db if it wasn't found above
954 result = sqlite3_open_v2( mSrsDatabaseFileName.toUtf8().constData(), &database, SQLITE_OPEN_READONLY, nullptr );
955 if ( result )
956 {
957 QgsDebugError( QStringLiteral( "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 0;
960 }
961 }
962
963 result = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail );
964 // XXX Need to free memory from the error msg if one is set
965 if ( result == SQLITE_OK && sqlite3_step( stmt ) == SQLITE_ROW )
966 {
967 QString srsIdString = QString::fromUtf8( ( char * )sqlite3_column_text( stmt, 0 ) );
968 srsId = srsIdString.toLong();
969 }
970
971 // close the sqlite3 statement
972 sqlite3_finalize( stmt );
973 sqlite3_close( database );
974
975 return srsId;
976}
977
978void QgsProjectionSelectionTreeWidget::updateBoundsPreview()
979{
980 QTreeWidgetItem *lvi = lstCoordinateSystems->currentItem();
981 if ( !lvi || ( lvi->text( QgisCrsIdColumn ).isEmpty() && !lvi->data( 0, RoleWkt ).isValid() ) )
982 return;
983
984 QgsCoordinateReferenceSystem currentCrs = crs();
985 if ( !currentCrs.isValid() )
986 return;
987
988 QgsRectangle rect = currentCrs.bounds();
989 QString extentString = tr( "Extent not known" );
990 mAreaCanvas->setPreviewRect( rect );
991 if ( !qgsDoubleNear( rect.area(), 0.0 ) )
992 {
993 extentString = QStringLiteral( "%1, %2, %3, %4" )
994 .arg( rect.xMinimum(), 0, 'f', 2 )
995 .arg( rect.yMinimum(), 0, 'f', 2 )
996 .arg( rect.xMaximum(), 0, 'f', 2 )
997 .arg( rect.yMaximum(), 0, 'f', 2 );
998 }
999
1000 QStringList properties;
1001 if ( currentCrs.isGeographic() )
1002 properties << tr( "Geographic (uses latitude and longitude for coordinates)" );
1003 else
1004 {
1005 properties << tr( "Units: %1" ).arg( QgsUnitTypes::toString( currentCrs.mapUnits() ) );
1006 }
1007 properties << ( currentCrs.isDynamic() ? tr( "Dynamic (relies on a datum which is not plate-fixed)" ) : tr( "Static (relies on a datum which is plate-fixed)" ) );
1008
1009 try
1010 {
1011 const QString celestialBody = currentCrs.celestialBodyName();
1012 if ( !celestialBody.isEmpty() )
1013 {
1014 properties << tr( "Celestial body: %1" ).arg( celestialBody );
1015 }
1016 }
1017 catch ( QgsNotSupportedException & )
1018 {
1019
1020 }
1021
1022 try
1023 {
1024 const QgsDatumEnsemble ensemble = currentCrs.datumEnsemble();
1025 if ( ensemble.isValid() )
1026 {
1027 QString id;
1028 if ( !ensemble.code().isEmpty() )
1029 id = QStringLiteral( "<i>%1</i> (%2:%3)" ).arg( ensemble.name(), ensemble.authority(), ensemble.code() );
1030 else
1031 id = QStringLiteral( "<i>%</i>”" ).arg( ensemble.name() );
1032 if ( ensemble.accuracy() > 0 )
1033 {
1034 properties << tr( "Based on %1, which has a limited accuracy of <b>at best %2 meters</b>." ).arg( id ).arg( ensemble.accuracy() );
1035 }
1036 else
1037 {
1038 properties << tr( "Based on %1, which has a limited accuracy." ).arg( id );
1039 }
1040 }
1041 }
1042 catch ( QgsNotSupportedException & )
1043 {
1044
1045 }
1046
1047 const QgsProjOperation operation = currentCrs.operation();
1048 properties << tr( "Method: %1" ).arg( operation.description() );
1049
1050 const QString propertiesString = QStringLiteral( "<dt><b>%1</b></dt><dd><ul><li>%2</li></ul></dd>" ).arg( tr( "Properties" ),
1051 properties.join( QLatin1String( "</li><li>" ) ) );
1052
1053 const QString extentHtml = QStringLiteral( "<dt><b>%1</b></dt><dd>%2</dd>" ).arg( tr( "Extent" ), extentString );
1054 const QString wktString = QStringLiteral( "<dt><b>%1</b></dt><dd><code>%2</code></dd>" ).arg( tr( "WKT" ), currentCrs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED, true ).replace( '\n', QLatin1String( "<br>" ) ).replace( ' ', QLatin1String( "&nbsp;" ) ) );
1055 const QString proj4String = QStringLiteral( "<dt><b>%1</b></dt><dd><code>%2</code></dd>" ).arg( tr( "Proj4" ), currentCrs.toProj() );
1056
1057#ifdef Q_OS_WIN
1058 const int smallerPointSize = std::max( font().pointSize() - 1, 8 ); // bit less on windows, due to poor rendering of small point sizes
1059#else
1060 const int smallerPointSize = std::max( font().pointSize() - 2, 6 );
1061#endif
1062
1063 teProjection->setText( QStringLiteral( "<div style=\"font-size: %1pt\"><h3>%2</h3><dl>" ).arg( smallerPointSize ).arg( selectedName() ) + propertiesString + wktString + proj4String + extentHtml + QStringLiteral( "</dl></div>" ) );
1064}
1065
1066QStringList QgsProjectionSelectionTreeWidget::authorities()
1067{
1068 sqlite3 *database = nullptr;
1069 const char *tail = nullptr;
1070 sqlite3_stmt *stmt = nullptr;
1071
1072 int result = sqlite3_open_v2( mSrsDatabaseFileName.toUtf8().constData(), &database, SQLITE_OPEN_READONLY, nullptr );
1073 if ( result )
1074 {
1075 QgsDebugError( QStringLiteral( "Can't open * user * database: %1" ).arg( sqlite3_errmsg( database ) ) );
1076 //no need for assert because user db may not have been created yet
1077 return QStringList();
1078 }
1079
1080 QString sql = QStringLiteral( "select distinct auth_name from tbl_srs" );
1081 result = sqlite3_prepare( database, sql.toUtf8(), sql.toUtf8().length(), &stmt, &tail );
1082
1083 QStringList authorities;
1084 if ( result == SQLITE_OK )
1085 {
1086 while ( sqlite3_step( stmt ) == SQLITE_ROW )
1087 {
1088 authorities << QString::fromUtf8( ( char * )sqlite3_column_text( stmt, 0 ) );
1089 }
1090
1091 }
1092
1093 // close the sqlite3 statement
1094 sqlite3_finalize( stmt );
1095 sqlite3_close( database );
1096
1097 return authorities;
1098}
1099
1100QString QgsProjectionSelectionTreeWidget::sqlSafeString( const QString &theSQL ) const
1101{
1102 QString retval = theSQL;
1103 retval.replace( '\\', QLatin1String( "\\\\" ) );
1104 retval.replace( '\"', QLatin1String( "\\\"" ) );
1105 retval.replace( '\'', QLatin1String( "\\'" ) );
1106 retval.replace( '%', QLatin1String( "\\%" ) );
1107 return retval;
1108}
1109
1110void QgsProjectionSelectionTreeWidget::showDBMissingWarning( const QString &fileName )
1111{
1112
1113 QMessageBox::critical( this, tr( "Resource Location Error" ),
1114 tr( "Error reading database file from: \n %1\n"
1115 "Because of this the projection selector will not work…" )
1116 .arg( fileName ) );
1117}
1118
1120{
1121 // If the list is empty, there is nothing to do
1122 if ( lstRecent->topLevelItemCount() == 0 )
1123 {
1124 return;
1125 }
1126
1127 // Ask for confirmation
1128 if ( QMessageBox::question( this, tr( "Clear Recent CRS" ),
1129 tr( "Are you sure you want to clear the list of recently used coordinate reference system?" ),
1130 QMessageBox::Yes | QMessageBox::No ) != QMessageBox::Yes )
1131 {
1132 return;
1133 }
1135 lstRecent->clear();
1136}
1137
1138
1139void QgsProjectionSelectionTreeWidget::removeRecentCrsItem( QTreeWidgetItem *item )
1140{
1141 int index = lstRecent->indexOfTopLevelItem( item );
1142 if ( index == -1 )
1143 return;
1144 QgsCoordinateReferenceSystem crs = crsForItem( item );
1146 lstRecent->takeTopLevelItem( index );
1147 delete item;
1148}
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QgsCoordinateReferenceSystemRegistry * coordinateReferenceSystemRegistry()
Returns the application's coordinate reference system (CRS) registry, which handles known CRS definit...
static QString qgisUserDatabaseFilePath()
Returns the path to the user qgis.db file.
static QString srsDatabaseFilePath()
Returns the path to the srs.db file.
QList< QgsCoordinateReferenceSystemRegistry::UserCrsDetails > userCrsList() const
Returns a list containing the details of all registered custom (user-defined) CRSes.
This class represents a coordinate reference system (CRS).
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Q_GADGET Qgis::DistanceUnit mapUnits
QgsRectangle bounds() const
Returns the approximate bounds for the region the CRS is usable within.
QString toProj() const
Returns a Proj string representation of this CRS.
static void pushRecentCoordinateReferenceSystem(const QgsCoordinateReferenceSystem &crs)
Pushes a recently used CRS to the top of the recent CRS list.
QgsDatumEnsemble datumEnsemble() const SIP_THROW(QgsNotSupportedException)
Attempts to retrieve datum ensemble details from the CRS.
static void removeRecentCoordinateReferenceSystem(const QgsCoordinateReferenceSystem &crs)
Removes a CRS from the list of recently used CRS.
bool isDynamic() const
Returns true if the CRS is a dynamic CRS.
static QgsCoordinateReferenceSystem fromProj(const QString &proj)
Creates a CRS from a proj style formatted string.
static QList< QgsCoordinateReferenceSystem > recentCoordinateReferenceSystems()
Returns a list of recently used CRS.
static void clearRecentCoordinateReferenceSystems()
Cleans the list of recently used CRS.
@ WKT_PREFERRED
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
static QgsCoordinateReferenceSystem fromWkt(const QString &wkt)
Creates a CRS from a WKT spatial ref sys definition string.
QString toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
QgsProjOperation operation() const
Returns information about the PROJ operation associated with the coordinate reference system,...
long srsid() const
Returns the internal CRS ID, if available.
QString celestialBodyName() const SIP_THROW(QgsNotSupportedException)
Attempts to retrieve the name of the celestial body associated with the CRS (e.g.
Contains information about a datum ensemble.
Definition: qgsdatums.h:95
QString code() const
Identification code, e.g.
Definition: qgsdatums.h:122
QString authority() const
Authority name, e.g.
Definition: qgsdatums.h:117
bool isValid() const
Returns true if the datum ensemble is a valid object, or false if it is a null/invalid object.
Definition: qgsdatums.h:102
QString name() const
Display name of datum ensemble.
Definition: qgsdatums.h:107
double accuracy() const
Positional accuracy (in meters).
Definition: qgsdatums.h:112
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Custom exception class which is raised when an operation is not supported.
Definition: qgsexception.h:119
Contains information about a PROJ operation.
QString description() const
Description.
void resizeEvent(QResizeEvent *event) override
void setPreviewRect(const QgsRectangle &rect)
Sets the initial "preview" rectangle for the bounds overview map.
void setShowBoundsMap(bool show)
Sets whether to show the bounds preview map.
void crsSelected()
Emitted when a projection is selected in the widget.
Q_DECL_DEPRECATED void pushProjectionToFront()
Marks the current selected projection for push to front of recent projections list.
QgsCoordinateReferenceSystem crs() const
Returns the CRS currently selected in the widget.
bool showNoProjection() const
Returns whether the "no/invalid" projection option is shown.
void setShowNoProjection(bool show)
Sets whether a "no/invalid" projection option should be shown.
bool showBoundsMap() const
Returns whether the bounds preview map is shown.
QgsRectangle previewRect() const
The initial "preview" rectangle for the bounds overview map.
QgsProjectionSelectionTreeWidget(QWidget *parent=nullptr)
Constructor for QgsProjectionSelectionTreeWidget.
void projectionDoubleClicked()
Emitted when a projection is double clicked in the list.
bool hasValidSelection() const
Returns true if the current selection in the widget is a valid choice.
void setOgcWmsCrsFilter(const QSet< QString > &crsFilter)
filters this widget by the given CRSs
bool eventFilter(QObject *obj, QEvent *ev) override
void setNotSetText(const QString &text)
Sets the text to show for the not set option.
void clearRecentCrs()
Clear the list of recent projections.
void initialized()
Notifies others that the widget is now fully initialized, including deferred selection of projection.
void hasValidSelectionChanged(bool isValid)
Emitted when the selection in the tree is changed from a valid selection to an invalid selection,...
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the initial crs to show within the dialog.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
double area() const SIP_HOLDGIL
Returns the area of the rectangle.
Definition: qgsrectangle.h:239
This class is a composition of two QSettings instances:
Definition: qgssettings.h:63
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
static QString qRegExpEscape(const QString &string)
Returns an escaped string matching the behavior of QRegExp::escape.
static Q_INVOKABLE QString toString(Qgis::DistanceUnit unit)
Returns a translated string representing a distance unit.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:3988
const int USER_CRS_START_ID
Magick number that determines whether a projection crsid is a system (srs.db) or user (~/....
Definition: qgis.h:4520
struct sqlite3 sqlite3
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugError(str)
Definition: qgslogger.h:38
const QgsCoordinateReferenceSystem & crs