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 );
479void QgsProjectionSelectionTreeWidget::updateBoundsPreview()
486 QString extentString = tr(
"Extent not known" );
487 mAreaCanvas->setPreviewRect( rect );
490 extentString = u
"%1, %2, %3, %4"_s.arg( rect.
xMinimum(), 0,
'f', 2 ).arg( rect.
yMinimum(), 0,
'f', 2 ).arg( rect.
xMaximum(), 0,
'f', 2 ).arg( rect.
yMaximum(), 0,
'f', 2 );
493 QStringList properties;
495 properties << tr(
"Geographic (uses latitude and longitude for coordinates)" );
500 properties << ( currentCrs.
isDynamic() ? tr(
"Dynamic (relies on a datum which is not plate-fixed)" ) : tr(
"Static (relies on a datum which is plate-fixed)" ) );
505 if ( !celestialBody.isEmpty() )
507 properties << tr(
"Celestial body: %1" ).arg( celestialBody );
510 catch ( QgsNotSupportedException & )
515 const QgsDatumEnsemble ensemble = currentCrs.
datumEnsemble();
519 if ( !ensemble.
code().isEmpty() )
520 id = u
"<i>%1</i> (%2:%3)"_s.arg( ensemble.
name(), ensemble.
authority(), ensemble.
code() );
522 id = u
"<i>%1</i>”"_s.arg( ensemble.
name() );
525 properties << tr(
"Based on %1, which has a limited accuracy of <b>at best %2 meters</b>." ).arg(
id ).arg( ensemble.
accuracy() );
529 properties << tr(
"Based on %1, which has a limited accuracy." ).arg(
id );
533 catch ( QgsNotSupportedException & )
536 const QgsProjOperation operation = currentCrs.
operation();
537 properties << tr(
"Method: %1" ).arg( operation.
description() );
539 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 ) );
541 const QString extentHtml = u
"<dt><b>%1</b></dt><dd>%2</dd>"_s.arg( tr(
"Extent" ), extentString );
542 const QString wktString
543 = 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 ) );
544 const QString proj4String = u
"<dt><b>%1</b></dt><dd><code>%2</code></dd>"_s.arg( tr(
"Proj4" ), currentCrs.
toProj() );
547 const int smallerPointSize = std::max( font().pointSize() - 1, 8 );
549 const int smallerPointSize = std::max( font().pointSize() - 2, 6 );
552 const QModelIndex currentIndex = lstCoordinateSystems->selectionModel()->selectedRows( 0 ).value( 0 );
553 QString selectedName;
554 if ( currentIndex.isValid() )
558 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 );
570 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 )
571 != QMessageBox::Yes )
578void QgsProjectionSelectionTreeWidget::removeRecentCrsItem(
const QModelIndex &index )
586QgsRecentCoordinateReferenceSystemTableModel::QgsRecentCoordinateReferenceSystemTableModel( QObject *parent )
589#ifdef ENABLE_MODELTEST
590 new ModelTest(
this,
this );
594QVariant QgsRecentCoordinateReferenceSystemTableModel::headerData(
int section, Qt::Orientation orientation,
int role )
const
596 if ( orientation == Qt::Horizontal )
600 case Qt::DisplayRole:
604 return tr(
"Coordinate Reference System" );
606 return tr(
"Authority ID" );
621QVariant QgsRecentCoordinateReferenceSystemTableModel::data(
const QModelIndex &index,
int role )
const
623 if ( !index.isValid() )
626 const int column = index.column();
634 case Qt::DisplayRole:
635 case Qt::ToolTipRole:
648 case Qt::ToolTipRole:
649 return tr(
"Remove from recently used CRS" );
660 return QgsRecentCoordinateReferenceSystemsProxyModel::data( index, role );
668RemoveRecentCrsDelegate::RemoveRecentCrsDelegate( QObject *parent )
669 : QStyledItemDelegate( parent )
672bool RemoveRecentCrsDelegate::eventFilter( QObject *obj, QEvent *event )
674 if ( event->type() == QEvent::HoverEnter || event->type() == QEvent::HoverMove )
676 QHoverEvent *hoverEvent =
static_cast<QHoverEvent *
>( event );
677 if ( QAbstractItemView *view = qobject_cast<QAbstractItemView *>( obj->parent() ) )
679 const QModelIndex indexUnderMouse = view->indexAt( hoverEvent->pos() );
680 setHoveredIndex( indexUnderMouse );
681 view->viewport()->update();
684 else if ( event->type() == QEvent::HoverLeave )
686 setHoveredIndex( QModelIndex() );
687 qobject_cast<QWidget *>( obj )->update();
689 return QStyledItemDelegate::eventFilter( obj, event );
692void RemoveRecentCrsDelegate::paint( QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index )
const
694 QStyledItemDelegate::paint( painter, option, index );
696 if ( index == mHoveredIndex )
698 QStyleOptionButton buttonOption;
699 buttonOption.initFrom( option.widget );
700 buttonOption.rect = option.rect;
702 option.widget->style()->drawControl( QStyle::CE_PushButton, &buttonOption, painter );
706 const QRect iconRect( option.rect.left() + ( option.rect.width() - 16 ) / 2, option.rect.top() + ( option.rect.height() - 16 ) / 2, 16, 16 );
708 icon.paint( painter, iconRect );
711void RemoveRecentCrsDelegate::setHoveredIndex(
const QModelIndex &index )
713 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)