Skip to content

Commit

Permalink
[needs-docs] Show a warning in the projection selection widget when
Browse files Browse the repository at this point in the history
a CRS based on a datum ensemble is selected, warning the user that
there's an inherent lack of accuracy in the selected CRS

Requires PROJ 8+
  • Loading branch information
nyalldawson committed May 11, 2021
1 parent 5084127 commit e66d75a
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 6 deletions.
28 changes: 28 additions & 0 deletions python/gui/auto_generated/qgsprojectionselectionwidget.sip.in
Expand Up @@ -83,6 +83,34 @@ passed, the message will be a generic
%End


bool showDatumEnsembleWarnings() const;
%Docstring
Returns ``True`` if the widget will show a warning to users when they select a CRS which uses
a datum ensemble.

.. warning::

Determining datum ensembles requires PROJ 8.0 or later.

.. seealso:: :py:func:`setShowDatumEnsembleWarnings`

.. versionadded:: 3.20
%End

void setShowDatumEnsembleWarnings( bool showDatumEnsembleWarnings );
%Docstring
Sets whether the widget will show a warning to users when they select a CRS which uses
a datum ensemble.

.. warning::

Determining datum ensembles requires PROJ 8.0 or later.

.. seealso:: :py:func:`showDatumEnsembleWarnings`

.. versionadded:: 3.20
%End

signals:

void crsChanged( const QgsCoordinateReferenceSystem & );
Expand Down
111 changes: 105 additions & 6 deletions src/gui/qgsprojectionselectionwidget.cpp
Expand Up @@ -22,19 +22,21 @@
#include "qgssettings.h"
#include "qgshighlightablecombobox.h"
#include "qgscoordinatereferencesystemregistry.h"
#include "qgsdatums.h"

QgsProjectionSelectionWidget::QgsProjectionSelectionWidget( QWidget *parent )
: QWidget( parent )
{
QHBoxLayout *layout = new QHBoxLayout();
layout->setContentsMargins( 0, 0, 0, 0 );
layout->setSpacing( 6 );
setLayout( layout );

mCrsComboBox = new QgsHighlightableComboBox( this );
mCrsComboBox->addItem( tr( "invalid projection" ), QgsProjectionSelectionWidget::CurrentCrs );
mCrsComboBox->setSizePolicy( QSizePolicy::Ignored, QSizePolicy::Preferred );

const int labelMargin = static_cast< int >( std::round( mCrsComboBox->fontMetrics().horizontalAdvance( 'X' ) ) );
QHBoxLayout *layout = new QHBoxLayout();
layout->setContentsMargins( 0, 0, 0, 0 );
layout->setSpacing( 0 );
setLayout( layout );

mProjectCrs = QgsProject::instance()->crs();
addProjectCrsOption();

Expand All @@ -49,7 +51,25 @@ QgsProjectionSelectionWidget::QgsProjectionSelectionWidget( QWidget *parent )

addRecentCrs();

layout->addWidget( mCrsComboBox );
layout->addWidget( mCrsComboBox, 1 );

// bit of fiddlyness here -- we want the initial spacing to only be visible
// when the warning label is shown, so it's embedded inside mWarningLabel
// instead of outside it
mWarningLabelContainer = new QWidget();
QHBoxLayout *warningLayout = new QHBoxLayout();
warningLayout->setContentsMargins( 0, 0, 0, 0 );
mWarningLabel = new QLabel();
QIcon icon = QgsApplication::getThemeIcon( QStringLiteral( "mIconWarning.svg" ) );
const int size = static_cast< int >( std::max( 24.0, mCrsComboBox->minimumSize().height() * 0.5 ) );
mWarningLabel->setPixmap( icon.pixmap( icon.actualSize( QSize( size, size ) ) ) );
warningLayout->insertSpacing( 0, labelMargin / 2 );
warningLayout->insertWidget( 1, mWarningLabel );
mWarningLabelContainer->setLayout( warningLayout );
layout->addWidget( mWarningLabelContainer );
updateWarning();

layout->addSpacing( labelMargin / 2 );

mButton = new QToolButton( this );
mButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionSetProjection.svg" ) ) );
Expand Down Expand Up @@ -269,6 +289,20 @@ void QgsProjectionSelectionWidget::dropEvent( QDropEvent *event )
update();
}

bool QgsProjectionSelectionWidget::showDatumEnsembleWarnings() const
{
return mShowDatumEnsembleWarnings;
}

void QgsProjectionSelectionWidget::setShowDatumEnsembleWarnings( bool showDatumEnsembleWarnings )
{
mShowDatumEnsembleWarnings = showDatumEnsembleWarnings;
if ( !mShowDatumEnsembleWarnings )
mWarningLabelContainer->hide();
else
updateWarning();
}

void QgsProjectionSelectionWidget::addNotSetOption()
{
mCrsComboBox->insertItem( 0, mNotSetText, QgsProjectionSelectionWidget::CrsNotSet );
Expand Down Expand Up @@ -307,6 +341,70 @@ void QgsProjectionSelectionWidget::comboIndexChanged( int idx )
updateTooltip();
}

void QgsProjectionSelectionWidget::updateWarning()
{
if ( !mShowDatumEnsembleWarnings )
{
if ( mWarningLabelContainer->isVisible() )
mWarningLabelContainer->hide();
return;
}

try
{
const QgsDatumEnsemble ensemble = crs().datumEnsemble();
if ( !ensemble.isValid() )
{
mWarningLabelContainer->hide();
}
else
{
mWarningLabelContainer->show();

QString warning = QStringLiteral( "<p>" );

QString id;
if ( !ensemble.code().isEmpty() )
id = QStringLiteral( "<i>%1</i> (%2:%3)" ).arg( ensemble.name(), ensemble.authority(), ensemble.code() );
else
id = QStringLiteral( "<i>%</i>”" ).arg( ensemble.name() );

if ( ensemble.accuracy() > 0 )
{
warning = tr( "The selected CRS is based on %1, which has a limited accuracy of <b>at best %2 meters</b>." ).arg( id ).arg( ensemble.accuracy() );
}
else
{
warning = tr( "The selected CRS is based on %1, which has a limited accuracy." ).arg( id );
}
warning += QStringLiteral( "</p><p>" ) + tr( "Use an alternative CRS if accurate positioning is required." ) + QStringLiteral( "</p>" );

const QList< QgsDatumEnsembleMember > members = ensemble.members();
if ( !members.isEmpty() )
{
warning += QStringLiteral( "<p>" ) + tr( "%1 consists of the datums:" ).arg( ensemble.name() ) + QStringLiteral( "</p><ul>" );

for ( const QgsDatumEnsembleMember &member : members )
{
if ( !member.code().isEmpty() )
id = QStringLiteral( "%1 (%2:%3)" ).arg( member.name(), member.authority(), member.code() );
else
id = member.name();
warning += QStringLiteral( "<li>%1</li>" ).arg( id );
}

warning += QStringLiteral( "</ul>" );
}

mWarningLabel->setToolTip( warning );
}
}
catch ( QgsNotSupportedException & )
{
mWarningLabelContainer->hide();
}
}

void QgsProjectionSelectionWidget::setCrs( const QgsCoordinateReferenceSystem &crs )
{
if ( crs.isValid() )
Expand Down Expand Up @@ -439,6 +537,7 @@ void QgsProjectionSelectionWidget::updateTooltip()
setToolTip( c.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED, true ) );
else
setToolTip( QString() );
updateWarning();
}

QgsMapLayer *QgsProjectionSelectionWidget::mapLayerFromMimeData( const QMimeData *data ) const
Expand Down
28 changes: 28 additions & 0 deletions src/gui/qgsprojectionselectionwidget.h
Expand Up @@ -28,6 +28,7 @@

class QgsProjectionSelectionDialog;
class QgsHighlightableComboBox;
class QLabel;

/**
* \class QgsProjectionSelectionWidget
Expand Down Expand Up @@ -100,6 +101,28 @@ class GUI_EXPORT QgsProjectionSelectionWidget : public QWidget
*/
static QString crsOptionText( const QgsCoordinateReferenceSystem &crs ) SIP_SKIP;

/**
* Returns TRUE if the widget will show a warning to users when they select a CRS which uses
* a datum ensemble.
*
* \warning Determining datum ensembles requires PROJ 8.0 or later.
*
* \see setShowDatumEnsembleWarnings()
* \since QGIS 3.20
*/
bool showDatumEnsembleWarnings() const;

/**
* Sets whether the widget will show a warning to users when they select a CRS which uses
* a datum ensemble.
*
* \warning Determining datum ensembles requires PROJ 8.0 or later.
*
* \see showDatumEnsembleWarnings()
* \since QGIS 3.20
*/
void setShowDatumEnsembleWarnings( bool showDatumEnsembleWarnings );

signals:

/**
Expand Down Expand Up @@ -151,6 +174,10 @@ class GUI_EXPORT QgsProjectionSelectionWidget : public QWidget
QString mNotSetText;
QString mMessage;

bool mShowDatumEnsembleWarnings = true;
QWidget *mWarningLabelContainer = nullptr;
QLabel *mWarningLabel = nullptr;

void addNotSetOption();
void addProjectCrsOption();
void addDefaultCrsOption();
Expand All @@ -167,6 +194,7 @@ class GUI_EXPORT QgsProjectionSelectionWidget : public QWidget
private slots:

void comboIndexChanged( int idx );
void updateWarning();

};

Expand Down

0 comments on commit e66d75a

Please sign in to comment.