15#include "moc_qgsprojectionselectiontreewidget.cpp"
17using namespace Qt::StringLiterals;
41#include <QResizeEvent>
43#include <QRegularExpression>
45#ifdef ENABLE_MODELTEST
55 mCrsModel->setFilters(
filters );
57 mRecentCrsModel =
new QgsRecentCoordinateReferenceSystemTableModel(
this );
58 mRecentCrsModel->setFilters(
filters );
60 lstCoordinateSystems->setModel( mCrsModel );
61 lstCoordinateSystems->setSelectionBehavior( QAbstractItemView::SelectRows );
63 lstRecent->setModel( mRecentCrsModel );
64 lstRecent->viewport()->setAttribute( Qt::WA_Hover );
65 lstRecent->setSelectionBehavior( QAbstractItemView::SelectRows );
66 lstRecent->setRootIsDecorated(
false );
68 RemoveRecentCrsDelegate *removeDelegate =
new RemoveRecentCrsDelegate( lstRecent );
69 lstRecent->setItemDelegateForColumn( 2, removeDelegate );
70 lstRecent->viewport()->installEventFilter( removeDelegate );
72 if ( mCrsModel->rowCount() == 1 )
75 lstCoordinateSystems->expand( mCrsModel->index( 0, 0, QModelIndex() ) );
78 QFont f = teProjection->font();
79 f.setPointSize( f.pointSize() - 2 );
80 teProjection->setFont( f );
82 leSearch->setShowSearchIcon(
true );
84 connect( lstCoordinateSystems, &QTreeView::doubleClicked,
this, &QgsProjectionSelectionTreeWidget::lstCoordinateSystemsDoubleClicked );
85 connect( lstRecent, &QTreeView::doubleClicked,
this, &QgsProjectionSelectionTreeWidget::lstRecentDoubleClicked );
86 connect( lstRecent, &QTreeView::clicked,
this, &QgsProjectionSelectionTreeWidget::lstRecentClicked );
87 connect( lstCoordinateSystems->selectionModel(), &QItemSelectionModel::selectionChanged,
this, &QgsProjectionSelectionTreeWidget::lstCoordinateSystemsSelectionChanged );
88 connect( lstRecent->selectionModel(), &QItemSelectionModel::selectionChanged,
this, &QgsProjectionSelectionTreeWidget::lstRecentSelectionChanged );
89 connect( cbxHideDeprecated, &QCheckBox::toggled,
this, [
this](
bool selected ) {
90 mCrsModel->setFilterDeprecated( selected );
91 mRecentCrsModel->setFilterDeprecated( selected );
93 connect( leSearch, &QgsFilterLineEdit::textChanged,
this, [
this](
const QString &filter ) {
94 mCrsModel->setFilterString( filter );
95 mRecentCrsModel->setFilterString( filter );
96 if ( filter.length() >= 3 )
97 lstCoordinateSystems->expandAll();
100 mAreaCanvas->setVisible( mShowMap );
102 lstCoordinateSystems->header()->setSectionResizeMode( AuthidColumn, QHeaderView::Stretch );
103 lstRecent->header()->setSectionResizeMode( AuthidColumn, QHeaderView::Stretch );
106 lstRecent->header()->setMinimumSectionSize( 10 );
107 lstRecent->header()->setStretchLastSection(
false );
108 lstRecent->header()->resizeSection( ClearColumn, 20 );
111 lstRecent->setContextMenuPolicy( Qt::CustomContextMenu );
112 connect( lstRecent, &QTreeView::customContextMenuRequested,
this, [
this](
const QPoint &pos ) {
114 if ( lstRecent->model()->rowCount() == 0 )
118 const QModelIndex currentIndex = lstRecent->indexAt( pos );
119 if ( currentIndex.isValid() )
121 QAction *clearSelected = menu.addAction(
QgsApplication::getThemeIcon(
"/mIconClearItem.svg" ), tr(
"Remove Selected CRS from Recently Used CRS" ) );
122 connect( clearSelected, &QAction::triggered,
this, [
this, currentIndex] { removeRecentCrsItem( currentIndex ); } );
126 QAction *clearAll = menu.addAction(
QgsApplication::getThemeIcon(
"/console/iconClearConsole.svg" ), tr(
"Clear All Recently Used CRS" ) );
128 menu.exec( lstRecent->viewport()->mapToGlobal( pos ) );
132 lstRecent->installEventFilter(
this );
134 mCheckBoxNoProjection->setHidden(
true );
135 mCheckBoxNoProjection->setEnabled(
false );
136 connect( mCheckBoxNoProjection, &QCheckBox::toggled,
this, [
this] {
137 if ( !mBlockSignals )
143 connect( mCheckBoxNoProjection, &QCheckBox::toggled,
this, [
this](
bool checked ) {
144 if ( mCheckBoxNoProjection->isEnabled() )
146 mFrameProjections->setDisabled( checked );
151 mSplitter->restoreState( settings.value( u
"Windows/ProjectionSelector/splitterState"_s ).toByteArray() );
157 settings.
setValue( u
"Windows/ProjectionSelector/splitterState"_s, mSplitter->saveState() );
167 lstCoordinateSystems->header()->resizeSection( NameColumn, event->size().width() - 240 );
168 lstCoordinateSystems->header()->resizeSection( AuthidColumn, 240 );
170 lstRecent->header()->resizeSection( NameColumn, event->size().width() - 260 );
171 lstRecent->header()->resizeSection( AuthidColumn, 240 );
172 lstRecent->header()->resizeSection( ClearColumn, 20 );
177 if ( obj != lstRecent )
180 if ( ev->type() != QEvent::KeyPress )
183 QKeyEvent *keyEvent =
static_cast<QKeyEvent *
>( ev );
184 if ( keyEvent->matches( QKeySequence::Delete ) )
186 const QModelIndex currentIndex = lstRecent->selectionModel()->selectedRows( 0 ).value( 0 );
187 if ( currentIndex.isValid() )
188 removeRecentCrsItem( currentIndex );
195void QgsProjectionSelectionTreeWidget::selectCrsByAuthId(
const QString &authid )
198 if ( !sourceIndex.isValid() )
201 const QModelIndex proxyIndex = mCrsModel->mapFromSource( sourceIndex );
202 if ( proxyIndex.isValid() )
204 lstCoordinateSystems->selectionModel()->select( proxyIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
205 lstCoordinateSystems->scrollTo( proxyIndex );
210 lstCoordinateSystems->clearSelection();
211 lstRecent->clearSelection();
212 teProjection->clear();
218 if ( !
crs.isValid() )
220 mCheckBoxNoProjection->setChecked(
true );
224 mBlockSignals =
true;
225 mCheckBoxNoProjection->setChecked(
false );
226 mBlockSignals =
false;
228 if ( !
crs.authid().isEmpty() )
229 selectCrsByAuthId(
crs.authid() );
231 loadUnknownCrs(
crs );
244 mAreaCanvas->setCanvasRect( rect );
249 return mAreaCanvas->canvasRect();
254 return mCrsModel->filters();
259 mCrsModel->setFilters(
filters );
260 mRecentCrsModel->setFilters(
filters );
261 if ( mCrsModel->rowCount() == 1 )
264 lstCoordinateSystems->expand( mCrsModel->index( 0, 0, QModelIndex() ) );
270 if ( mCheckBoxNoProjection->isEnabled() && mCheckBoxNoProjection->isChecked() )
273 const QModelIndex currentIndex = lstCoordinateSystems->selectionModel()->selectedRows( 0 ).value( 0 );
275 if ( !authid.isEmpty() )
285 if ( !wkt.isEmpty() )
287 else if ( !proj.isEmpty() )
296 mCheckBoxNoProjection->setVisible( show );
297 mCheckBoxNoProjection->setEnabled( show );
300 mFrameProjections->setDisabled( mCheckBoxNoProjection->isChecked() );
307 mAreaCanvas->setVisible( show );
312 return !mCheckBoxNoProjection->isHidden();
317 mCheckBoxNoProjection->setText( text );
327 if ( mCheckBoxNoProjection->isChecked() )
333 const QModelIndex currentIndex = lstCoordinateSystems->selectionModel()->selectedRows( 0 ).value( 0 );
337 return !authid.isEmpty() || !wkt.isEmpty() || !proj.isEmpty();
343 mCrsModel->setFilterAuthIds( crsFilter );
349 lstCoordinateSystems->selectionModel()->select( mCrsModel->mapFromSource( sourceIndex ), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
350 lstCoordinateSystems->scrollTo( mCrsModel->mapFromSource( sourceIndex ) );
354void QgsProjectionSelectionTreeWidget::lstCoordinateSystemsSelectionChanged(
const QItemSelection &selected,
const QItemSelection & )
356 if ( selected.isEmpty() )
362 const QModelIndex selectedProxyIndex = lstCoordinateSystems->selectionModel()->selectedRows( 0 ).value( 0 );
363 if ( !selectedProxyIndex.isValid() )
366 lstCoordinateSystems->scrollTo( selectedProxyIndex );
367 const QModelIndex sourceIndex = mCrsModel->mapToSource( selectedProxyIndex );
371 if ( mCrsModel->coordinateReferenceSystemModel()->rowCount( sourceIndex ) == 0 )
374 if ( !mBlockSignals )
380 updateBoundsPreview();
383 if ( !crsAuthId.isEmpty() )
386 if ( !recentMatches.isEmpty() )
390 lstRecent->selectionModel()->select( recentMatches.at( 0 ), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
391 lstRecent->scrollTo( recentMatches.at( 0 ) );
396 lstRecent->clearSelection();
397 lstCoordinateSystems->setFocus( Qt::OtherFocusReason );
402 lstRecent->clearSelection();
403 lstCoordinateSystems->setFocus( Qt::OtherFocusReason );
409 teProjection->clear();
410 lstRecent->clearSelection();
415void QgsProjectionSelectionTreeWidget::lstCoordinateSystemsDoubleClicked(
const QModelIndex &index )
417 if ( !index.isValid() )
425 if ( !mCrsModel->coordinateReferenceSystemModel()->hasChildren( mCrsModel->mapToSource( index ) ) )
429void QgsProjectionSelectionTreeWidget::lstRecentSelectionChanged(
const QItemSelection &selected,
const QItemSelection & )
431 if ( selected.isEmpty() )
437 const QModelIndex selectedIndex = lstRecent->selectionModel()->selectedRows( 0 ).value( 0 );
438 if ( !selectedIndex.isValid() )
441 lstRecent->scrollTo( selectedIndex );
443 const QString selectedAuthId = mRecentCrsModel->crs( selectedIndex ).authid();
444 const QModelIndex sourceIndex = mCrsModel->coordinateReferenceSystemModel()->authIdToIndex( selectedAuthId );
445 if ( sourceIndex.isValid() )
447 const QModelIndex proxyIndex = mCrsModel->mapFromSource( sourceIndex );
448 if ( proxyIndex.isValid() )
450 lstCoordinateSystems->selectionModel()->select( proxyIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
451 lstCoordinateSystems->scrollTo( proxyIndex );
456void QgsProjectionSelectionTreeWidget::lstRecentDoubleClicked(
const QModelIndex &index )
459 if ( !index.isValid() )
468void QgsProjectionSelectionTreeWidget::lstRecentClicked(
const QModelIndex &index )
470 if ( index.column() == ClearColumn )
472 removeRecentCrsItem( index );
480void QgsProjectionSelectionTreeWidget::updateBoundsPreview()
487 QString extentString = tr(
"Extent not known" );
488 mAreaCanvas->setPreviewRect( rect );
491 extentString = u
"%1, %2, %3, %4"_s
498 QStringList properties;
500 properties << tr(
"Geographic (uses latitude and longitude for coordinates)" );
505 properties << ( currentCrs.
isDynamic() ? tr(
"Dynamic (relies on a datum which is not plate-fixed)" ) : tr(
"Static (relies on a datum which is plate-fixed)" ) );
510 if ( !celestialBody.isEmpty() )
512 properties << tr(
"Celestial body: %1" ).arg( celestialBody );
515 catch ( QgsNotSupportedException & )
521 const QgsDatumEnsemble ensemble = currentCrs.
datumEnsemble();
525 if ( !ensemble.
code().isEmpty() )
526 id = u
"<i>%1</i> (%2:%3)"_s.arg( ensemble.
name(), ensemble.
authority(), ensemble.
code() );
528 id = u
"<i>%1</i>”"_s.arg( ensemble.
name() );
531 properties << tr(
"Based on %1, which has a limited accuracy of <b>at best %2 meters</b>." ).arg(
id ).arg( ensemble.
accuracy() );
535 properties << tr(
"Based on %1, which has a limited accuracy." ).arg(
id );
539 catch ( QgsNotSupportedException & )
543 const QgsProjOperation operation = currentCrs.
operation();
544 properties << tr(
"Method: %1" ).arg( operation.
description() );
546 const QString propertiesString = u
"<dt><b>%1</b></dt><dd><ul><li>%2</li></ul></dd>"_s.arg( tr(
"Properties" ), properties.join(
"</li><li>"_L1 ) );
548 const QString extentHtml = u
"<dt><b>%1</b></dt><dd>%2</dd>"_s.arg( tr(
"Extent" ), extentString );
549 const QString wktString = u
"<dt><b>%1</b></dt><dd><code>%2</code></dd>"_s.arg( tr(
"WKT" ), currentCrs.
toWkt(
Qgis::CrsWktVariant::Preferred,
true ).replace(
'\n',
"<br>"_L1 ).replace(
' ',
" "_L1 ) );
550 const QString proj4String = u
"<dt><b>%1</b></dt><dd><code>%2</code></dd>"_s.arg( tr(
"Proj4" ), currentCrs.
toProj() );
553 const int smallerPointSize = std::max( font().pointSize() - 1, 8 );
555 const int smallerPointSize = std::max( font().pointSize() - 2, 6 );
558 const QModelIndex currentIndex = lstCoordinateSystems->selectionModel()->selectedRows( 0 ).value( 0 );
559 QString selectedName;
560 if ( currentIndex.isValid() )
564 teProjection->setText( u
"<div style=\"font-size: %1pt\"><h3>%2</h3><dl>"_s.arg( smallerPointSize ).arg( selectedName ) + propertiesString + wktString + proj4String + extentHtml + u
"</dl></div>"_s );
576 if ( QMessageBox::question(
this, tr(
"Clear Recent CRS" ), tr(
"Are you sure you want to clear the list of recently used coordinate reference system?" ), QMessageBox::Yes | QMessageBox::No ) != QMessageBox::Yes )
583void QgsProjectionSelectionTreeWidget::removeRecentCrsItem(
const QModelIndex &index )
591QgsRecentCoordinateReferenceSystemTableModel::QgsRecentCoordinateReferenceSystemTableModel( QObject *parent )
594#ifdef ENABLE_MODELTEST
595 new ModelTest(
this,
this );
599QVariant QgsRecentCoordinateReferenceSystemTableModel::headerData(
int section, Qt::Orientation orientation,
int role )
const
601 if ( orientation == Qt::Horizontal )
605 case Qt::DisplayRole:
609 return tr(
"Coordinate Reference System" );
611 return tr(
"Authority ID" );
626QVariant QgsRecentCoordinateReferenceSystemTableModel::data(
const QModelIndex &index,
int role )
const
628 if ( !index.isValid() )
631 const int column = index.column();
639 case Qt::DisplayRole:
640 case Qt::ToolTipRole:
653 case Qt::ToolTipRole:
654 return tr(
"Remove from recently used CRS" );
665 return QgsRecentCoordinateReferenceSystemsProxyModel::data( index, role );
673RemoveRecentCrsDelegate::RemoveRecentCrsDelegate( QObject *parent )
674 : QStyledItemDelegate( parent )
678bool RemoveRecentCrsDelegate::eventFilter( QObject *obj, QEvent *event )
680 if ( event->type() == QEvent::HoverEnter || event->type() == QEvent::HoverMove )
682 QHoverEvent *hoverEvent =
static_cast<QHoverEvent *
>( event );
683 if ( QAbstractItemView *view = qobject_cast<QAbstractItemView *>( obj->parent() ) )
685 const QModelIndex indexUnderMouse = view->indexAt( hoverEvent->pos() );
686 setHoveredIndex( indexUnderMouse );
687 view->viewport()->update();
690 else if ( event->type() == QEvent::HoverLeave )
692 setHoveredIndex( QModelIndex() );
693 qobject_cast<QWidget *>( obj )->update();
695 return QStyledItemDelegate::eventFilter( obj, event );
698void RemoveRecentCrsDelegate::paint( QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index )
const
700 QStyledItemDelegate::paint( painter, option, index );
702 if ( index == mHoveredIndex )
704 QStyleOptionButton buttonOption;
705 buttonOption.initFrom( option.widget );
706 buttonOption.rect = option.rect;
708 option.widget->style()->drawControl( QStyle::CE_PushButton, &buttonOption, painter );
712 const QRect iconRect( option.rect.left() + ( option.rect.width() - 16 ) / 2, option.rect.top() + ( option.rect.height() - 16 ) / 2, 16, 16 );
714 icon.paint( painter, iconRect );
717void RemoveRecentCrsDelegate::setHoveredIndex(
const QModelIndex &index )
719 mHoveredIndex = index;
@ Preferred
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
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...
QModelIndex addCustomCrs(const QgsCoordinateReferenceSystem &crs)
Adds a custom crs to the model.
QModelIndex authIdToIndex(const QString &authId) const
Retrieves the model index corresponding to a CRS with the specified authId.
@ AuthId
The coordinate reference system authority name and id.
@ Name
The coordinate reference system name.
@ Wkt
The coordinate reference system's WKT representation. This is only used for non-standard CRS (i....
@ Proj
The coordinate reference system's PROJ representation. This is only used for non-standard CRS (i....
A sort/filter proxy model for coordinate reference systems.
QgsCoordinateReferenceSystemModel * coordinateReferenceSystemModel()
Returns the underlying source model.
void removeRecent(const QgsCoordinateReferenceSystem &crs)
Removes a CRS from the list of recently used CRS.
void clearRecent()
Cleans the list of recently used CRS.
void pushRecent(const QgsCoordinateReferenceSystem &crs)
Pushes a recently used CRS to the top of the recent CRS list.
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.
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.
QgsDatumEnsemble datumEnsemble() const
Attempts to retrieve datum ensemble details from the 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.
QString toWkt(Qgis::CrsWktVariant variant=Qgis::CrsWktVariant::Wkt1Gdal, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
QString celestialBodyName() const
Attempts to retrieve the name of the celestial body associated with the CRS (e.g.
static QgsCoordinateReferenceSystem fromWkt(const QString &wkt)
Creates a CRS from a WKT spatial ref sys definition string.
QgsProjOperation operation() const
Returns information about the PROJ operation associated with the coordinate reference system,...
Qgis::DistanceUnit mapUnits
QString code() const
Identification code, e.g.
QString authority() const
Authority name, e.g.
bool isValid() const
Returns true if the datum ensemble is a valid object, or false if it is a null/invalid object.
QString name() const
Display name of datum ensemble.
double accuracy() const
Positional accuracy (in meters).
QString description() const
Description.
@ AuthId
CRS authority ID.
A sort/filter proxy model for recent coordinate reference systems.
A rectangle specified with double values.
Stores settings for use within QGIS.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
static Q_INVOKABLE QString toString(Qgis::DistanceUnit unit)
Returns a translated string representing a distance unit.
#define QgsDebugMsgLevel(str, level)