13#include "moc_qgsprojectionselectiontreewidget.cpp"
37#include <QResizeEvent>
39#include <QRegularExpression>
41#ifdef ENABLE_MODELTEST
51 mCrsModel->setFilters(
filters );
53 mRecentCrsModel =
new QgsRecentCoordinateReferenceSystemTableModel(
this );
54 mRecentCrsModel->setFilters(
filters );
56 lstCoordinateSystems->setModel( mCrsModel );
57 lstCoordinateSystems->setSelectionBehavior( QAbstractItemView::SelectRows );
59 lstRecent->setModel( mRecentCrsModel );
60 lstRecent->viewport()->setAttribute( Qt::WA_Hover );
61 lstRecent->setSelectionBehavior( QAbstractItemView::SelectRows );
62 lstRecent->setRootIsDecorated(
false );
64 RemoveRecentCrsDelegate *removeDelegate =
new RemoveRecentCrsDelegate( lstRecent );
65 lstRecent->setItemDelegateForColumn( 2, removeDelegate );
66 lstRecent->viewport()->installEventFilter( removeDelegate );
68 if ( mCrsModel->rowCount() == 1 )
71 lstCoordinateSystems->expand( mCrsModel->index( 0, 0, QModelIndex() ) );
74 QFont f = teProjection->font();
75 f.setPointSize( f.pointSize() - 2 );
76 teProjection->setFont( f );
78 leSearch->setShowSearchIcon(
true );
80 connect( lstCoordinateSystems, &QTreeView::doubleClicked,
this, &QgsProjectionSelectionTreeWidget::lstCoordinateSystemsDoubleClicked );
81 connect( lstRecent, &QTreeView::doubleClicked,
this, &QgsProjectionSelectionTreeWidget::lstRecentDoubleClicked );
82 connect( lstRecent, &QTreeView::clicked,
this, &QgsProjectionSelectionTreeWidget::lstRecentClicked );
83 connect( lstCoordinateSystems->selectionModel(), &QItemSelectionModel::selectionChanged,
this, &QgsProjectionSelectionTreeWidget::lstCoordinateSystemsSelectionChanged );
84 connect( lstRecent->selectionModel(), &QItemSelectionModel::selectionChanged,
this, &QgsProjectionSelectionTreeWidget::lstRecentSelectionChanged );
85 connect( cbxHideDeprecated, &QCheckBox::toggled,
this, [
this](
bool selected ) {
86 mCrsModel->setFilterDeprecated( selected );
87 mRecentCrsModel->setFilterDeprecated( selected );
89 connect( leSearch, &QgsFilterLineEdit::textChanged,
this, [
this](
const QString &filter ) {
90 mCrsModel->setFilterString( filter );
91 mRecentCrsModel->setFilterString( filter );
92 if ( filter.length() >= 3 )
93 lstCoordinateSystems->expandAll();
96 mAreaCanvas->setVisible( mShowMap );
98 lstCoordinateSystems->header()->setSectionResizeMode( AuthidColumn, QHeaderView::Stretch );
99 lstRecent->header()->setSectionResizeMode( AuthidColumn, QHeaderView::Stretch );
102 lstRecent->header()->setMinimumSectionSize( 10 );
103 lstRecent->header()->setStretchLastSection(
false );
104 lstRecent->header()->resizeSection( ClearColumn, 20 );
107 lstRecent->setContextMenuPolicy( Qt::CustomContextMenu );
108 connect( lstRecent, &QTreeView::customContextMenuRequested,
this, [
this](
const QPoint &pos ) {
110 if ( lstRecent->model()->rowCount() == 0 )
114 const QModelIndex currentIndex = lstRecent->indexAt( pos );
115 if ( currentIndex.isValid() )
117 QAction *clearSelected = menu.addAction(
QgsApplication::getThemeIcon(
"/mIconClearItem.svg" ), tr(
"Remove Selected CRS from Recently Used CRS" ) );
118 connect( clearSelected, &QAction::triggered,
this, [
this, currentIndex] { removeRecentCrsItem( currentIndex ); } );
122 QAction *clearAll = menu.addAction(
QgsApplication::getThemeIcon(
"/console/iconClearConsole.svg" ), tr(
"Clear All Recently Used CRS" ) );
124 menu.exec( lstRecent->viewport()->mapToGlobal( pos ) );
128 lstRecent->installEventFilter(
this );
130 mCheckBoxNoProjection->setHidden(
true );
131 mCheckBoxNoProjection->setEnabled(
false );
132 connect( mCheckBoxNoProjection, &QCheckBox::toggled,
this, [
this] {
133 if ( !mBlockSignals )
139 connect( mCheckBoxNoProjection, &QCheckBox::toggled,
this, [
this](
bool checked ) {
140 if ( mCheckBoxNoProjection->isEnabled() )
142 mFrameProjections->setDisabled( checked );
147 mSplitter->restoreState( settings.value( QStringLiteral(
"Windows/ProjectionSelector/splitterState" ) ).toByteArray() );
153 settings.
setValue( QStringLiteral(
"Windows/ProjectionSelector/splitterState" ), mSplitter->saveState() );
163 lstCoordinateSystems->header()->resizeSection( NameColumn, event->size().width() - 240 );
164 lstCoordinateSystems->header()->resizeSection( AuthidColumn, 240 );
166 lstRecent->header()->resizeSection( NameColumn, event->size().width() - 260 );
167 lstRecent->header()->resizeSection( AuthidColumn, 240 );
168 lstRecent->header()->resizeSection( ClearColumn, 20 );
173 if ( obj != lstRecent )
176 if ( ev->type() != QEvent::KeyPress )
179 QKeyEvent *keyEvent =
static_cast<QKeyEvent *
>( ev );
180 if ( keyEvent->matches( QKeySequence::Delete ) )
182 const QModelIndex currentIndex = lstRecent->selectionModel()->selectedRows( 0 ).value( 0 );
183 if ( currentIndex.isValid() )
184 removeRecentCrsItem( currentIndex );
191void QgsProjectionSelectionTreeWidget::selectCrsByAuthId(
const QString &authid )
194 if ( !sourceIndex.isValid() )
197 const QModelIndex proxyIndex = mCrsModel->mapFromSource( sourceIndex );
198 if ( proxyIndex.isValid() )
200 lstCoordinateSystems->selectionModel()->select( proxyIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
201 lstCoordinateSystems->scrollTo( proxyIndex );
206 lstCoordinateSystems->clearSelection();
207 lstRecent->clearSelection();
208 teProjection->clear();
214 if ( !
crs.isValid() )
216 mCheckBoxNoProjection->setChecked(
true );
220 mBlockSignals =
true;
221 mCheckBoxNoProjection->setChecked(
false );
222 mBlockSignals =
false;
224 if ( !
crs.authid().isEmpty() )
225 selectCrsByAuthId(
crs.authid() );
227 loadUnknownCrs(
crs );
240 mAreaCanvas->setCanvasRect( rect );
245 return mAreaCanvas->canvasRect();
250 return mCrsModel->filters();
255 mCrsModel->setFilters(
filters );
256 mRecentCrsModel->setFilters(
filters );
257 if ( mCrsModel->rowCount() == 1 )
260 lstCoordinateSystems->expand( mCrsModel->index( 0, 0, QModelIndex() ) );
266 if ( mCheckBoxNoProjection->isEnabled() && mCheckBoxNoProjection->isChecked() )
269 const QModelIndex currentIndex = lstCoordinateSystems->selectionModel()->selectedRows( 0 ).value( 0 );
271 if ( !authid.isEmpty() )
281 if ( !wkt.isEmpty() )
283 else if ( !proj.isEmpty() )
292 mCheckBoxNoProjection->setVisible( show );
293 mCheckBoxNoProjection->setEnabled( show );
296 mFrameProjections->setDisabled( mCheckBoxNoProjection->isChecked() );
303 mAreaCanvas->setVisible( show );
308 return !mCheckBoxNoProjection->isHidden();
313 mCheckBoxNoProjection->setText( text );
323 if ( mCheckBoxNoProjection->isChecked() )
329 const QModelIndex currentIndex = lstCoordinateSystems->selectionModel()->selectedRows( 0 ).value( 0 );
333 return !authid.isEmpty() || !wkt.isEmpty() || !proj.isEmpty();
339 mCrsModel->setFilterAuthIds( crsFilter );
345 lstCoordinateSystems->selectionModel()->select( mCrsModel->mapFromSource( sourceIndex ), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
346 lstCoordinateSystems->scrollTo( mCrsModel->mapFromSource( sourceIndex ) );
350void QgsProjectionSelectionTreeWidget::lstCoordinateSystemsSelectionChanged(
const QItemSelection &selected,
const QItemSelection & )
352 if ( selected.isEmpty() )
358 const QModelIndex selectedProxyIndex = lstCoordinateSystems->selectionModel()->selectedRows( 0 ).value( 0 );
359 if ( !selectedProxyIndex.isValid() )
362 lstCoordinateSystems->scrollTo( selectedProxyIndex );
363 const QModelIndex sourceIndex = mCrsModel->mapToSource( selectedProxyIndex );
367 if ( mCrsModel->coordinateReferenceSystemModel()->rowCount( sourceIndex ) == 0 )
370 if ( !mBlockSignals )
376 updateBoundsPreview();
379 if ( !crsAuthId.isEmpty() )
382 if ( !recentMatches.isEmpty() )
384 QgsDebugMsgLevel( QStringLiteral(
"found srs %1 in recent" ).arg( crsAuthId ), 4 );
386 lstRecent->selectionModel()->select( recentMatches.at( 0 ), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
387 lstRecent->scrollTo( recentMatches.at( 0 ) );
391 QgsDebugMsgLevel( QStringLiteral(
"srs %1 not recent" ).arg( crsAuthId ), 4 );
392 lstRecent->clearSelection();
393 lstCoordinateSystems->setFocus( Qt::OtherFocusReason );
398 lstRecent->clearSelection();
399 lstCoordinateSystems->setFocus( Qt::OtherFocusReason );
405 teProjection->clear();
406 lstRecent->clearSelection();
411void QgsProjectionSelectionTreeWidget::lstCoordinateSystemsDoubleClicked(
const QModelIndex &index )
413 if ( !index.isValid() )
421 if ( !mCrsModel->coordinateReferenceSystemModel()->hasChildren( mCrsModel->mapToSource( index ) ) )
425void QgsProjectionSelectionTreeWidget::lstRecentSelectionChanged(
const QItemSelection &selected,
const QItemSelection & )
427 if ( selected.isEmpty() )
433 const QModelIndex selectedIndex = lstRecent->selectionModel()->selectedRows( 0 ).value( 0 );
434 if ( !selectedIndex.isValid() )
437 lstRecent->scrollTo( selectedIndex );
439 const QString selectedAuthId = mRecentCrsModel->crs( selectedIndex ).authid();
440 const QModelIndex sourceIndex = mCrsModel->coordinateReferenceSystemModel()->authIdToIndex( selectedAuthId );
441 if ( sourceIndex.isValid() )
443 const QModelIndex proxyIndex = mCrsModel->mapFromSource( sourceIndex );
444 if ( proxyIndex.isValid() )
446 lstCoordinateSystems->selectionModel()->select( proxyIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
447 lstCoordinateSystems->scrollTo( proxyIndex );
452void QgsProjectionSelectionTreeWidget::lstRecentDoubleClicked(
const QModelIndex &index )
455 if ( !index.isValid() )
464void QgsProjectionSelectionTreeWidget::lstRecentClicked(
const QModelIndex &index )
466 if ( index.column() == ClearColumn )
468 removeRecentCrsItem( index );
476void QgsProjectionSelectionTreeWidget::updateBoundsPreview()
483 QString extentString = tr(
"Extent not known" );
484 mAreaCanvas->setPreviewRect( rect );
487 extentString = QStringLiteral(
"%1, %2, %3, %4" )
494 QStringList properties;
496 properties << tr(
"Geographic (uses latitude and longitude for coordinates)" );
501 properties << ( currentCrs.
isDynamic() ? tr(
"Dynamic (relies on a datum which is not plate-fixed)" ) : tr(
"Static (relies on a datum which is plate-fixed)" ) );
506 if ( !celestialBody.isEmpty() )
508 properties << tr(
"Celestial body: %1" ).arg( celestialBody );
511 catch ( QgsNotSupportedException & )
517 const QgsDatumEnsemble ensemble = currentCrs.
datumEnsemble();
521 if ( !ensemble.
code().isEmpty() )
522 id = QStringLiteral(
"<i>%1</i> (%2:%3)" ).arg( ensemble.
name(), ensemble.
authority(), ensemble.
code() );
524 id = QStringLiteral(
"<i>%1</i>”" ).arg( ensemble.
name() );
527 properties << tr(
"Based on %1, which has a limited accuracy of <b>at best %2 meters</b>." ).arg(
id ).arg( ensemble.
accuracy() );
531 properties << tr(
"Based on %1, which has a limited accuracy." ).arg(
id );
535 catch ( QgsNotSupportedException & )
539 const QgsProjOperation operation = currentCrs.
operation();
540 properties << tr(
"Method: %1" ).arg( operation.
description() );
542 const QString propertiesString = QStringLiteral(
"<dt><b>%1</b></dt><dd><ul><li>%2</li></ul></dd>" ).arg( tr(
"Properties" ), properties.join( QLatin1String(
"</li><li>" ) ) );
544 const QString extentHtml = QStringLiteral(
"<dt><b>%1</b></dt><dd>%2</dd>" ).arg( tr(
"Extent" ), extentString );
545 const QString wktString = QStringLiteral(
"<dt><b>%1</b></dt><dd><code>%2</code></dd>" ).arg( tr(
"WKT" ), currentCrs.
toWkt(
Qgis::CrsWktVariant::Preferred,
true ).replace(
'\n', QLatin1String(
"<br>" ) ).replace(
' ', QLatin1String(
" " ) ) );
546 const QString proj4String = QStringLiteral(
"<dt><b>%1</b></dt><dd><code>%2</code></dd>" ).arg( tr(
"Proj4" ), currentCrs.
toProj() );
549 const int smallerPointSize = std::max( font().pointSize() - 1, 8 );
551 const int smallerPointSize = std::max( font().pointSize() - 2, 6 );
554 const QModelIndex currentIndex = lstCoordinateSystems->selectionModel()->selectedRows( 0 ).value( 0 );
555 QString selectedName;
556 if ( currentIndex.isValid() )
560 teProjection->setText( QStringLiteral(
"<div style=\"font-size: %1pt\"><h3>%2</h3><dl>" ).arg( smallerPointSize ).arg( selectedName ) + propertiesString + wktString + proj4String + extentHtml + QStringLiteral(
"</dl></div>" ) );
572 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 )
579void QgsProjectionSelectionTreeWidget::removeRecentCrsItem(
const QModelIndex &index )
587QgsRecentCoordinateReferenceSystemTableModel::QgsRecentCoordinateReferenceSystemTableModel( QObject *parent )
590#ifdef ENABLE_MODELTEST
591 new ModelTest(
this,
this );
595QVariant QgsRecentCoordinateReferenceSystemTableModel::headerData(
int section, Qt::Orientation orientation,
int role )
const
597 if ( orientation == Qt::Horizontal )
601 case Qt::DisplayRole:
605 return tr(
"Coordinate Reference System" );
607 return tr(
"Authority ID" );
622QVariant QgsRecentCoordinateReferenceSystemTableModel::data(
const QModelIndex &index,
int role )
const
624 if ( !index.isValid() )
627 const int column = index.column();
635 case Qt::DisplayRole:
636 case Qt::ToolTipRole:
649 case Qt::ToolTipRole:
650 return tr(
"Remove from recently used CRS" );
661 return QgsRecentCoordinateReferenceSystemsProxyModel::data( index, role );
669RemoveRecentCrsDelegate::RemoveRecentCrsDelegate( QObject *parent )
670 : QStyledItemDelegate( parent )
674bool RemoveRecentCrsDelegate::eventFilter( QObject *obj, QEvent *event )
676 if ( event->type() == QEvent::HoverEnter || event->type() == QEvent::HoverMove )
678 QHoverEvent *hoverEvent =
static_cast<QHoverEvent *
>( event );
679 if ( QAbstractItemView *view = qobject_cast<QAbstractItemView *>( obj->parent() ) )
681 const QModelIndex indexUnderMouse = view->indexAt( hoverEvent->pos() );
682 setHoveredIndex( indexUnderMouse );
683 view->viewport()->update();
686 else if ( event->type() == QEvent::HoverLeave )
688 setHoveredIndex( QModelIndex() );
689 qobject_cast<QWidget *>( obj )->update();
691 return QStyledItemDelegate::eventFilter( obj, event );
694void RemoveRecentCrsDelegate::paint( QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index )
const
696 QStyledItemDelegate::paint( painter, option, index );
698 if ( index == mHoveredIndex )
700 QStyleOptionButton buttonOption;
701 buttonOption.initFrom( option.widget );
702 buttonOption.rect = option.rect;
704 option.widget->style()->drawControl( QStyle::CE_PushButton, &buttonOption, painter );
708 const QRect iconRect( option.rect.left() + ( option.rect.width() - 16 ) / 2, option.rect.top() + ( option.rect.height() - 16 ) / 2, 16, 16 );
710 icon.paint( painter, iconRect );
713void RemoveRecentCrsDelegate::setHoveredIndex(
const QModelIndex &index )
715 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)