12#include "moc_qgsprojectionselectiontreewidget.cpp"
36#include <QResizeEvent>
38#include <QRegularExpression>
40#ifdef ENABLE_MODELTEST
52 mRecentCrsModel =
new QgsRecentCoordinateReferenceSystemTableModel(
this );
53 mRecentCrsModel->setFilters(
filters );
55 lstCoordinateSystems->setModel( mCrsModel );
56 lstCoordinateSystems->setSelectionBehavior( QAbstractItemView::SelectRows );
58 lstRecent->setModel( mRecentCrsModel );
59 lstRecent->viewport()->setAttribute( Qt::WA_Hover );
60 lstRecent->setSelectionBehavior( QAbstractItemView::SelectRows );
61 lstRecent->setRootIsDecorated(
false );
63 RemoveRecentCrsDelegate *removeDelegate =
new RemoveRecentCrsDelegate( lstRecent );
64 lstRecent->setItemDelegateForColumn( 2, removeDelegate );
65 lstRecent->viewport()->installEventFilter( removeDelegate );
67 if ( mCrsModel->rowCount() == 1 )
70 lstCoordinateSystems->expand( mCrsModel->index( 0, 0, QModelIndex() ) );
73 QFont f = teProjection->font();
74 f.setPointSize( f.pointSize() - 2 );
75 teProjection->setFont( f );
77 leSearch->setShowSearchIcon(
true );
79 connect( lstCoordinateSystems, &QTreeView::doubleClicked,
this, &QgsProjectionSelectionTreeWidget::lstCoordinateSystemsDoubleClicked );
80 connect( lstRecent, &QTreeView::doubleClicked,
this, &QgsProjectionSelectionTreeWidget::lstRecentDoubleClicked );
81 connect( lstRecent, &QTreeView::clicked,
this, &QgsProjectionSelectionTreeWidget::lstRecentClicked );
82 connect( lstCoordinateSystems->selectionModel(), &QItemSelectionModel::selectionChanged,
this, &QgsProjectionSelectionTreeWidget::lstCoordinateSystemsSelectionChanged );
83 connect( lstRecent->selectionModel(), &QItemSelectionModel::selectionChanged,
this, &QgsProjectionSelectionTreeWidget::lstRecentSelectionChanged );
84 connect( cbxHideDeprecated, &QCheckBox::toggled,
this, [ = ](
bool selected )
87 mRecentCrsModel->setFilterDeprecated( selected );
89 connect( leSearch, &QgsFilterLineEdit::textChanged,
this, [ = ](
const QString & filter )
92 mRecentCrsModel->setFilterString( filter );
95 mAreaCanvas->setVisible( mShowMap );
97 lstCoordinateSystems->header()->setSectionResizeMode( AuthidColumn, QHeaderView::Stretch );
98 lstRecent->header()->setSectionResizeMode( AuthidColumn, QHeaderView::Stretch );
101 lstRecent->header()->setMinimumSectionSize( 10 );
102 lstRecent->header()->setStretchLastSection(
false );
103 lstRecent->header()->resizeSection( ClearColumn, 20 );
106 lstRecent->setContextMenuPolicy( Qt::CustomContextMenu );
107 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, [ = ]
134 if ( !mBlockSignals )
140 connect( mCheckBoxNoProjection, &QCheckBox::toggled,
this, [ = ](
bool checked )
142 if ( mCheckBoxNoProjection->isEnabled() )
144 mFrameProjections->setDisabled( checked );
149 mSplitter->restoreState( settings.value( QStringLiteral(
"Windows/ProjectionSelector/splitterState" ) ).toByteArray() );
155 settings.
setValue( QStringLiteral(
"Windows/ProjectionSelector/splitterState" ), mSplitter->saveState() );
165 lstCoordinateSystems->header()->resizeSection( NameColumn, event->size().width() - 240 );
166 lstCoordinateSystems->header()->resizeSection( AuthidColumn, 240 );
168 lstRecent->header()->resizeSection( NameColumn, event->size().width() - 260 );
169 lstRecent->header()->resizeSection( AuthidColumn, 240 );
170 lstRecent->header()->resizeSection( ClearColumn, 20 );
175 if ( obj != lstRecent )
178 if ( ev->type() != QEvent::KeyPress )
181 QKeyEvent *keyEvent =
static_cast<QKeyEvent *
>( ev );
182 if ( keyEvent->matches( QKeySequence::Delete ) )
184 const QModelIndex currentIndex = lstRecent->selectionModel()->selectedRows( 0 ).value( 0 );
185 if ( currentIndex.isValid() )
186 removeRecentCrsItem( currentIndex );
193void QgsProjectionSelectionTreeWidget::selectCrsByAuthId(
const QString &authid )
196 if ( !sourceIndex.isValid() )
199 const QModelIndex proxyIndex = mCrsModel->mapFromSource( sourceIndex );
200 if ( proxyIndex.isValid() )
202 lstCoordinateSystems->selectionModel()->select( proxyIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
203 lstCoordinateSystems->scrollTo( proxyIndex );
208 lstCoordinateSystems->clearSelection();
209 lstRecent->clearSelection();
210 teProjection->clear();
218 mCheckBoxNoProjection->setChecked(
true );
222 mBlockSignals =
true;
223 mCheckBoxNoProjection->setChecked(
false );
224 mBlockSignals =
false;
229 loadUnknownCrs(
crs );
242 mAreaCanvas->setCanvasRect( rect );
247 return mAreaCanvas->canvasRect();
258 mRecentCrsModel->setFilters(
filters );
259 if ( mCrsModel->rowCount() == 1 )
262 lstCoordinateSystems->expand( mCrsModel->index( 0, 0, QModelIndex() ) );
268 if ( mCheckBoxNoProjection->isEnabled() && mCheckBoxNoProjection->isChecked() )
271 const QModelIndex currentIndex = lstCoordinateSystems->selectionModel()->selectedRows( 0 ).value( 0 );
273 if ( !authid.isEmpty() )
283 if ( !wkt.isEmpty() )
285 else if ( !proj.isEmpty() )
294 mCheckBoxNoProjection->setVisible( show );
295 mCheckBoxNoProjection->setEnabled( show );
298 mFrameProjections->setDisabled( mCheckBoxNoProjection->isChecked() );
305 mAreaCanvas->setVisible( show );
310 return !mCheckBoxNoProjection->isHidden();
315 mCheckBoxNoProjection->setText( text );
325 if ( mCheckBoxNoProjection->isChecked() )
331 const QModelIndex currentIndex = lstCoordinateSystems->selectionModel()->selectedRows( 0 ).value( 0 );
335 return !authid.isEmpty() || !wkt.isEmpty() || !proj.isEmpty();
347 lstCoordinateSystems->selectionModel()->select( mCrsModel->mapFromSource( sourceIndex ), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
348 lstCoordinateSystems->scrollTo( mCrsModel->mapFromSource( sourceIndex ) );
352void QgsProjectionSelectionTreeWidget::lstCoordinateSystemsSelectionChanged(
const QItemSelection &selected,
const QItemSelection & )
354 if ( selected.isEmpty() )
360 const QModelIndex selectedProxyIndex = lstCoordinateSystems->selectionModel()->selectedRows( 0 ).value( 0 );
361 if ( !selectedProxyIndex.isValid() )
364 lstCoordinateSystems->scrollTo( selectedProxyIndex );
365 const QModelIndex sourceIndex = mCrsModel->mapToSource( selectedProxyIndex );
372 if ( !mBlockSignals )
378 updateBoundsPreview();
381 if ( !crsAuthId.isEmpty() )
383 const QModelIndexList recentMatches = mRecentCrsModel->match( mRecentCrsModel->index( 0, 0 ),
386 if ( !recentMatches.isEmpty() )
388 QgsDebugMsgLevel( QStringLiteral(
"found srs %1 in recent" ).arg( crsAuthId ), 4 );
390 lstRecent->selectionModel()->select( recentMatches.at( 0 ), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows );
391 lstRecent->scrollTo( recentMatches.at( 0 ) );
395 QgsDebugMsgLevel( QStringLiteral(
"srs %1 not recent" ).arg( crsAuthId ), 4 );
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() )
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();
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 = QStringLiteral(
"%1, %2, %3, %4" )
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 );
526 if ( !ensemble.
code().isEmpty() )
527 id = QStringLiteral(
"<i>%1</i> (%2:%3)" ).arg( ensemble.
name(), ensemble.
authority(), ensemble.
code() );
529 id = QStringLiteral(
"<i>%</i>”" ).arg( ensemble.
name() );
532 properties << tr(
"Based on %1, which has a limited accuracy of <b>at best %2 meters</b>." ).arg(
id ).arg( ensemble.
accuracy() );
536 properties << tr(
"Based on %1, which has a limited accuracy." ).arg(
id );
546 properties << tr(
"Method: %1" ).arg( operation.
description() );
548 const QString propertiesString = QStringLiteral(
"<dt><b>%1</b></dt><dd><ul><li>%2</li></ul></dd>" ).arg( tr(
"Properties" ),
549 properties.join( QLatin1String(
"</li><li>" ) ) );
551 const QString extentHtml = QStringLiteral(
"<dt><b>%1</b></dt><dd>%2</dd>" ).arg( tr(
"Extent" ), extentString );
552 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(
" " ) ) );
553 const QString proj4String = QStringLiteral(
"<dt><b>%1</b></dt><dd><code>%2</code></dd>" ).arg( tr(
"Proj4" ), currentCrs.
toProj() );
556 const int smallerPointSize = std::max( font().pointSize() - 1, 8 );
558 const int smallerPointSize = std::max( font().pointSize() - 2, 6 );
561 const QModelIndex currentIndex = lstCoordinateSystems->selectionModel()->selectedRows( 0 ).value( 0 );
562 QString selectedName;
563 if ( currentIndex.isValid() )
567 teProjection->setText( QStringLiteral(
"<div style=\"font-size: %1pt\"><h3>%2</h3><dl>" ).arg( smallerPointSize ).arg( selectedName ) + propertiesString + wktString + proj4String + extentHtml + QStringLiteral(
"</dl></div>" ) );
579 if ( QMessageBox::question(
this, tr(
"Clear Recent CRS" ),
580 tr(
"Are you sure you want to clear the list of recently used coordinate reference system?" ),
581 QMessageBox::Yes | QMessageBox::No ) != QMessageBox::Yes )
588void QgsProjectionSelectionTreeWidget::removeRecentCrsItem(
const QModelIndex &index )
596QgsRecentCoordinateReferenceSystemTableModel::QgsRecentCoordinateReferenceSystemTableModel( QObject *parent )
599#ifdef ENABLE_MODELTEST
600 new ModelTest(
this,
this );
604QVariant QgsRecentCoordinateReferenceSystemTableModel::headerData(
int section, Qt::Orientation orientation,
int role )
const
606 if ( orientation == Qt::Horizontal )
610 case Qt::DisplayRole:
614 return tr(
"Coordinate Reference System" );
616 return tr(
"Authority ID" );
631QVariant QgsRecentCoordinateReferenceSystemTableModel::data(
const QModelIndex &index,
int role )
const
633 if ( !index.isValid() )
636 const int column = index.column();
644 case Qt::DisplayRole:
645 case Qt::ToolTipRole:
658 case Qt::ToolTipRole:
659 return tr(
"Remove from recently used CRS" );
670 return QgsRecentCoordinateReferenceSystemsProxyModel::data( index, role );
678RemoveRecentCrsDelegate::RemoveRecentCrsDelegate( QObject *parent )
679 : QStyledItemDelegate( parent )
684bool RemoveRecentCrsDelegate::eventFilter( QObject *obj, QEvent *event )
686 if ( event->type() == QEvent::HoverEnter || event->type() == QEvent::HoverMove )
688 QHoverEvent *hoverEvent =
static_cast<QHoverEvent *
>( event );
689 if ( QAbstractItemView *view = qobject_cast<QAbstractItemView *>( obj->parent() ) )
691 const QModelIndex indexUnderMouse = view->indexAt( hoverEvent->pos() );
692 setHoveredIndex( indexUnderMouse );
693 view->viewport()->update();
696 else if ( event->type() == QEvent::HoverLeave )
698 setHoveredIndex( QModelIndex() );
699 qobject_cast< QWidget * >( obj )->update();
701 return QStyledItemDelegate::eventFilter( obj, event );
704void RemoveRecentCrsDelegate::paint( QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index )
const
706 QStyledItemDelegate::paint( painter, option, index );
708 if ( index == mHoveredIndex )
710 QStyleOptionButton buttonOption;
711 buttonOption.initFrom( option.widget );
712 buttonOption.rect = option.rect;
714 option.widget->style()->drawControl( QStyle::CE_PushButton, &buttonOption, painter );
718 const QRect iconRect( option.rect.left() + ( option.rect.width() - 16 ) / 2,
719 option.rect.top() + ( option.rect.height() - 16 ) / 2,
722 icon.paint( painter, iconRect );
725void RemoveRecentCrsDelegate::setHoveredIndex(
const QModelIndex &index )
727 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...
int rowCount(const QModelIndex &parent=QModelIndex()) const override
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....
QVariant data(const QModelIndex &index, int role) const override
A sort/filter proxy model for coordinate reference systems.
void setFilterDeprecated(bool filter)
Sets whether deprecated CRS should be filtered from the results.
Filters filters() const
Returns any filters that affect how CRS are filtered.
QgsCoordinateReferenceSystemModel * coordinateReferenceSystemModel()
Returns the underlying source model.
void setFilterString(const QString &filter)
Sets a filter string, such that only coordinate reference systems matching the specified string will ...
void setFilterAuthIds(const QSet< QString > &filter)
Sets a filter list of CRS auth ID strings, such that only coordinate reference systems matching the s...
void setFilters(QgsCoordinateReferenceSystemProxyModel::Filters filters)
Set filters that affect how CRS are filtered.
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.
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.
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
Contains information about a datum ensemble.
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).
Custom exception class which is raised when an operation is not supported.
Contains information about a PROJ operation.
QString description() const
Description.
@ AuthId
CRS authority ID.
A sort/filter proxy model for recent coordinate reference systems.
A rectangle specified with double values.
This class is a composition of two QSettings instances:
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.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
#define QgsDebugMsgLevel(str, level)
const QgsCoordinateReferenceSystem & crs