diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 1327d6d1aa1..87154d922bd 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -323,6 +323,7 @@ set(QGIS_CORE_SRCS providers/ogr/qgsogrproviderconnection.cpp providers/ogr/qgsogrtransaction.cpp + providers/sensorthings/qgssensorthingsconnection.cpp providers/sensorthings/qgssensorthingsfeatureiterator.cpp providers/sensorthings/qgssensorthingsprovider.cpp providers/sensorthings/qgssensorthingsshareddata.cpp @@ -1817,6 +1818,7 @@ set(QGIS_CORE_HDRS providers/ogr/qgsogrproviderutils.h providers/ogr/qgsogrtransaction.h + providers/sensorthings/qgssensorthingsconnection.h providers/sensorthings/qgssensorthingsfeatureiterator.h providers/sensorthings/qgssensorthingsprovider.h providers/sensorthings/qgssensorthingsshareddata.h diff --git a/src/core/providers/sensorthings/qgssensorthingsconnection.cpp b/src/core/providers/sensorthings/qgssensorthingsconnection.cpp new file mode 100644 index 00000000000..8a2e682c22e --- /dev/null +++ b/src/core/providers/sensorthings/qgssensorthingsconnection.cpp @@ -0,0 +1,75 @@ +/*************************************************************************** + qgssensorthingsconnection.cpp + --------------------- + Date : December 2023 + Copyright : (C) 2023 by Nyall Dawson + Email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include +#include "qgssensorthingsconnection.h" + +#include "qgsowsconnection.h" +#include "qgsdatasourceuri.h" +#include "qgssettingsentryimpl.h" + + +const QgsSettingsEntryString *QgsSensorThingsConnection::settingsUrl = new QgsSettingsEntryString( QStringLiteral( "url" ), sTreeSensorThingsConnections, QString() ) ; +const QgsSettingsEntryVariantMap *QgsSensorThingsConnection::settingsHeaders = new QgsSettingsEntryVariantMap( QStringLiteral( "http-header" ), sTreeSensorThingsConnections ) ; +const QgsSettingsEntryString *QgsSensorThingsConnection::settingsUsername = new QgsSettingsEntryString( QStringLiteral( "username" ), sTreeSensorThingsConnections ) ; +const QgsSettingsEntryString *QgsSensorThingsConnection::settingsPassword = new QgsSettingsEntryString( QStringLiteral( "password" ), sTreeSensorThingsConnections ) ; +const QgsSettingsEntryString *QgsSensorThingsConnection::settingsAuthcfg = new QgsSettingsEntryString( QStringLiteral( "authcfg" ), sTreeSensorThingsConnections ) ; + +QString QgsSensorThingsConnection::encodedUri() const +{ + QgsDataSourceUri uri; + uri.setParam( QStringLiteral( "url" ), url ); + if ( ! authCfg.isEmpty() ) + uri.setAuthConfigId( authCfg ); + if ( ! username.isEmpty() ) + uri.setUsername( username ); + if ( ! password.isEmpty() ) + uri.setPassword( password ); + + uri.setHttpHeaders( httpHeaders ); + return uri.uri( false ); +} + +QStringList QgsSensorThingsConnectionUtils::connectionList() +{ + return QgsSensorThingsConnection::sTreeSensorThingsConnections->items(); +} + +QgsSensorThingsConnection QgsSensorThingsConnectionUtils::connection( const QString &name ) +{ + QgsSensorThingsConnection conn; + conn.name = name; + conn.url = QgsSensorThingsConnection::settingsUrl->value( name ); + conn.authCfg = QgsSensorThingsConnection::settingsAuthcfg->value( name ); + conn.username = QgsSensorThingsConnection::settingsUsername->value( name ); + conn.password = QgsSensorThingsConnection::settingsPassword->value( name ); + conn.httpHeaders = QgsSensorThingsConnection::settingsHeaders->value( name ); + return conn; +} + +void QgsSensorThingsConnectionUtils::deleteConnection( const QString &name ) +{ + QgsSensorThingsConnection::sTreeSensorThingsConnections->deleteItem( name ); +} + +void QgsSensorThingsConnectionUtils::addConnection( const QgsSensorThingsConnection &conn ) +{ + QgsSensorThingsConnection::settingsUrl->setValue( conn.url, conn.name ); + QgsSensorThingsConnection::settingsAuthcfg->setValue( conn.authCfg, conn.name ); + QgsSensorThingsConnection::settingsUsername->setValue( conn.username, conn.name ); + QgsSensorThingsConnection::settingsPassword->setValue( conn.password, conn.name ); + QgsSensorThingsConnection::settingsHeaders->setValue( conn.httpHeaders.headers(), conn.name ); +} + diff --git a/src/core/providers/sensorthings/qgssensorthingsconnection.h b/src/core/providers/sensorthings/qgssensorthingsconnection.h new file mode 100644 index 00000000000..f023b13380a --- /dev/null +++ b/src/core/providers/sensorthings/qgssensorthingsconnection.h @@ -0,0 +1,83 @@ +/*************************************************************************** + qgssensorthingsconnection.h + --------------------- + Date : December 2023 + Copyright : (C) 2023 by Nyall Dawson + Email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSSENSORTHINGSCONNECTION_H + +#define SIP_NO_FILE + +#include + +#include "qgshttpheaders.h" +#include "qgis_core.h" +#include "qgssettingstree.h" + +class QgsSettingsEntryBool; +class QgsSettingsEntryDouble; +class QgsSettingsEntryInteger; +class QgsSettingsEntryString; +class QgsSettingsEntryVariantMap; +template class QgsSettingsEntryEnumFlag; + +///@cond PRIVATE + +class CORE_EXPORT QgsSensorThingsConnection +{ + + public: + + static inline QgsSettingsTreeNamedListNode *sTreeSensorThingsConnections = QgsSettingsTree::sTreeConnections->createNamedListNode( + QStringLiteral( "sensorthings" ), Qgis::SettingsTreeNodeOption::NamedListSelectedItemSetting ); + + static const QgsSettingsEntryString *settingsUrl; + static const QgsSettingsEntryVariantMap *settingsHeaders; + static const QgsSettingsEntryString *settingsUsername; + static const QgsSettingsEntryString *settingsPassword; + static const QgsSettingsEntryString *settingsAuthcfg; + + QString name; + QString url; + // Authentication configuration id + QString authCfg; + // HTTP Basic username + QString username; + // HTTP Basic password + QString password; + // http headers + QgsHttpHeaders httpHeaders; + + QString encodedUri() const; +}; + +//! Utility class for handling list of connections to sensor things layers +class CORE_EXPORT QgsSensorThingsConnectionUtils +{ + + public: + //! Returns list of existing connections, unless the hidden ones + static QStringList connectionList(); + + //! Returns connection details + static QgsSensorThingsConnection connection( const QString &name ); + + //! Removes a connection from the list + static void deleteConnection( const QString &name ); + + //! Adds a new connection to the list + static void addConnection( const QgsSensorThingsConnection &conn ); +}; + +///@endcond PRIVATE + +#endif // QGSSENSORTHINGSCONNECTION_H diff --git a/src/core/providers/sensorthings/qgssensorthingsprovider.cpp b/src/core/providers/sensorthings/qgssensorthingsprovider.cpp index a80bbd01608..bbcc4a97123 100644 --- a/src/core/providers/sensorthings/qgssensorthingsprovider.cpp +++ b/src/core/providers/sensorthings/qgssensorthingsprovider.cpp @@ -283,6 +283,22 @@ QVariantMap QgsSensorThingsProviderMetadata::decodeUri( const QString &uri ) con { components.insert( QStringLiteral( "authcfg" ), dsUri.authConfigId() ); } + if ( !dsUri.username().isEmpty() ) + { + components.insert( QStringLiteral( "username" ), dsUri.username() ); + } + if ( !dsUri.password().isEmpty() ) + { + components.insert( QStringLiteral( "password" ), dsUri.password() ); + } + if ( !dsUri.param( QStringLiteral( "referer" ) ).isEmpty() ) + { + components.insert( QStringLiteral( "referer" ), dsUri.param( QStringLiteral( "referer" ) ) ); + } + if ( !dsUri.param( QStringLiteral( "http-header:referer" ) ).isEmpty() ) + { + components.insert( QStringLiteral( "referer" ), dsUri.param( QStringLiteral( "http-header:referer" ) ) ); + } const QString entityParam = dsUri.param( QStringLiteral( "entity" ) ); Qgis::SensorThingsEntity entity = QgsSensorThingsUtils::entitySetStringToEntity( entityParam ); @@ -331,6 +347,18 @@ QString QgsSensorThingsProviderMetadata::encodeUri( const QVariantMap &parts ) c { dsUri.setAuthConfigId( parts.value( QStringLiteral( "authcfg" ) ).toString() ); } + if ( !parts.value( QStringLiteral( "username" ) ).toString().isEmpty() ) + { + dsUri.setUsername( parts.value( QStringLiteral( "username" ) ).toString() ); + } + if ( !parts.value( QStringLiteral( "password" ) ).toString().isEmpty() ) + { + dsUri.setPassword( parts.value( QStringLiteral( "password" ) ).toString() ); + } + if ( !parts.value( QStringLiteral( "referer" ) ).toString().isEmpty() ) + { + dsUri.setParam( QStringLiteral( "referer" ), parts.value( QStringLiteral( "referer" ) ).toString() ); + } Qgis::SensorThingsEntity entity = QgsSensorThingsUtils::entitySetStringToEntity( parts.value( QStringLiteral( "entity" ) ).toString() ); diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index e8fb03f40ca..3324f8defcb 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -457,7 +457,10 @@ set(QGIS_GUI_SRCS providers/ogr/qgsogritemguiprovider.cpp providers/ogr/qgsgeopackageprojectstorageguiprovider.cpp + providers/sensorthings/qgssensorthingsconnectiondialog.cpp + providers/sensorthings/qgssensorthingsconnectionwidget.cpp providers/sensorthings/qgssensorthingsguiprovider.cpp + providers/sensorthings/qgssensorthingssourceselect.cpp providers/sensorthings/qgssensorthingssourcewidget.cpp providers/vtpkvectortiles/qgsvtpkvectortileguiprovider.cpp @@ -1414,7 +1417,10 @@ set(QGIS_GUI_HDRS providers/ogr/qgsogritemguiprovider.h providers/ogr/qgsogrsourceselect.h + providers/sensorthings/qgssensorthingsconnectiondialog.h + providers/sensorthings/qgssensorthingsconnectionwidget.h providers/sensorthings/qgssensorthingsguiprovider.h + providers/sensorthings/qgssensorthingssourceselect.h providers/sensorthings/qgssensorthingssourcewidget.h providers/vtpkvectortiles/qgsvtpkvectortileguiprovider.h diff --git a/src/gui/providers/sensorthings/qgssensorthingsconnectiondialog.cpp b/src/gui/providers/sensorthings/qgssensorthingsconnectiondialog.cpp new file mode 100644 index 00000000000..ee3ab741854 --- /dev/null +++ b/src/gui/providers/sensorthings/qgssensorthingsconnectiondialog.cpp @@ -0,0 +1,82 @@ +/*************************************************************************** + qgssensorthingsconnectiondialog.cpp + --------------------- + Date : December 2023 + Copyright : (C) 2023 by Nyall Dawson + Email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgssensorthingsconnectiondialog.h" +#include "qgssensorthingsconnection.h" +#include "qgsgui.h" +#include "qgshelp.h" +#include "qgssensorthingsconnectionwidget.h" + +#include +#include + +///@cond PRIVATE + +QgsSensorThingsConnectionDialog::QgsSensorThingsConnectionDialog( QWidget *parent ) + : QDialog( parent ) +{ + setupUi( this ); + QgsGui::enableAutoGeometryRestore( this ); + + mConnectionWidget = new QgsSensorThingsConnectionWidget(); + QHBoxLayout *hlayout = new QHBoxLayout(); + hlayout->addWidget( mConnectionWidget ); + mConnectionGroupBox->setLayout( hlayout ); + + buttonBox->button( QDialogButtonBox::Ok )->setDisabled( true ); + connect( buttonBox, &QDialogButtonBox::helpRequested, this, [ = ] + { + QgsHelp::openHelp( QStringLiteral( "managing_data_source/opening_data.html" ) ); + } ); + connect( mEditName, &QLineEdit::textChanged, this, &QgsSensorThingsConnectionDialog::updateOkButtonState ); + connect( mConnectionWidget, &QgsSensorThingsConnectionWidget::validChanged, this, &QgsSensorThingsConnectionDialog::updateOkButtonState ); +} + +void QgsSensorThingsConnectionDialog::setConnection( const QgsSensorThingsConnection &conn ) +{ + mEditName->setText( conn.name ); + mConnectionWidget->setUrl( conn.url ); + mConnectionWidget->setUsername( conn.username ); + mConnectionWidget->setPassword( conn.password ); + mConnectionWidget->setReferer( conn.httpHeaders[QgsHttpHeaders::KEY_REFERER].toString() ); + mConnectionWidget->setAuthCfg( conn.authCfg ); +} + +QgsSensorThingsConnection QgsSensorThingsConnectionDialog::connection() const +{ + QgsSensorThingsConnection conn; + conn.name = mEditName->text(); + conn.url = mConnectionWidget->url(); + conn.username = mConnectionWidget->username(); + conn.password = mConnectionWidget->password(); + if ( !mConnectionWidget->referer().isEmpty() ) + conn.httpHeaders[QgsHttpHeaders::KEY_REFERER] = mConnectionWidget->referer(); + conn.authCfg = mConnectionWidget->authcfg( ); + return conn; +} + +void QgsSensorThingsConnectionDialog::updateOkButtonState() +{ + const bool enabled = !mEditName->text().isEmpty() && !mConnectionWidget->url().isEmpty(); + buttonBox->button( QDialogButtonBox::Ok )->setEnabled( enabled ); +} + +void QgsSensorThingsConnectionDialog::accept() +{ + // validate here if required + QDialog::accept(); +} + +///@endcond PRIVATE diff --git a/src/gui/providers/sensorthings/qgssensorthingsconnectiondialog.h b/src/gui/providers/sensorthings/qgssensorthingsconnectiondialog.h new file mode 100644 index 00000000000..3c26138dc6f --- /dev/null +++ b/src/gui/providers/sensorthings/qgssensorthingsconnectiondialog.h @@ -0,0 +1,57 @@ +/*************************************************************************** + qgssensorthingsconnectiondialog.h + --------------------- + Date : December 2023 + Copyright : (C) 2023 by Nyall Dawson + Email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSSENSORTHINGSCONNECTIONDIALOG_H +#define QGSSENSORTHINGSCONNECTIONDIALOG_H + +///@cond PRIVATE +#define SIP_NO_FILE + +#include + +#include "ui_qgssensorthingsconnectiondialog.h" + + +struct QgsSensorThingsConnection; + +class QgsSensorThingsConnectionWidget; + +class QgsSensorThingsConnectionDialog : public QDialog, public Ui::QgsSensorThingsConnectionDialog +{ + Q_OBJECT + public: + explicit QgsSensorThingsConnectionDialog( QWidget *parent = nullptr ); + + void setConnection( const QgsSensorThingsConnection &conn ); + + QgsSensorThingsConnection connection() const; + + QgsSensorThingsConnectionWidget *connectionWidget() { return mConnectionWidget; } + + void accept() override; + + private slots: + //! Updates state of the OK button depending of the filled fields + void updateOkButtonState(); + + private: + QString mBaseKey; + QString mCredentialsBaseKey; + + QgsSensorThingsConnectionWidget *mConnectionWidget = nullptr; +}; + +///@endcond PRIVATE +#endif // QGSSENSORTHINGSCONNECTIONDIALOG_H diff --git a/src/gui/providers/sensorthings/qgssensorthingsconnectionwidget.cpp b/src/gui/providers/sensorthings/qgssensorthingsconnectionwidget.cpp new file mode 100644 index 00000000000..d2f1aaf8ae9 --- /dev/null +++ b/src/gui/providers/sensorthings/qgssensorthingsconnectionwidget.cpp @@ -0,0 +1,137 @@ +/*************************************************************************** + qgssensorthingsconnectionwidget.cpp + -------------------------------------- + Date : December 2023 + Copyright : (C) 2023 by Nyall Dawson + Email : nyall dot dawson at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgssensorthingsconnectionwidget.h" +#include "qgsproviderregistry.h" +#include "qgssensorthingsprovider.h" + +///@cond PRIVATE +QgsSensorThingsConnectionWidget::QgsSensorThingsConnectionWidget( QWidget *parent ) + : QWidget( parent ) +{ + setupUi( this ); + + connect( mEditUrl, &QLineEdit::textChanged, this, &QgsSensorThingsConnectionWidget::validate ); + connect( mEditUrl, &QLineEdit::textChanged, this, &QgsSensorThingsConnectionWidget::changed ); + + connect( mAuthSettings, &QgsAuthSettingsWidget::configIdChanged, this, &QgsSensorThingsConnectionWidget::changed ); + connect( mAuthSettings, &QgsAuthSettingsWidget::usernameChanged, this, &QgsSensorThingsConnectionWidget::changed ); + connect( mAuthSettings, &QgsAuthSettingsWidget::passwordChanged, this, &QgsSensorThingsConnectionWidget::changed ); + connect( mEditReferer, &QLineEdit::textChanged, this, &QgsSensorThingsConnectionWidget::changed ); +} + +void QgsSensorThingsConnectionWidget::setSourceUri( const QString &uri ) +{ + mSourceParts = QgsProviderRegistry::instance()->decodeUri( QgsSensorThingsProvider::SENSORTHINGS_PROVIDER_KEY, uri ); + + mEditUrl->setText( mSourceParts.value( QStringLiteral( "url" ) ).toString() ); + mAuthSettings->setUsername( mSourceParts.value( QStringLiteral( "username" ) ).toString() ); + mAuthSettings->setPassword( mSourceParts.value( QStringLiteral( "password" ) ).toString() ); + mEditReferer->setText( mSourceParts.value( QStringLiteral( "referer" ) ).toString() ); + + mAuthSettings->setConfigId( mSourceParts.value( QStringLiteral( "authcfg" ) ).toString() ); +} + +QString QgsSensorThingsConnectionWidget::sourceUri() const +{ + QVariantMap parts = mSourceParts; + + parts.insert( QStringLiteral( "url" ), mEditUrl->text() ); + + if ( !mAuthSettings->username().isEmpty() ) + parts.insert( QStringLiteral( "username" ), mAuthSettings->username() ); + else + parts.remove( QStringLiteral( "username" ) ); + if ( !mAuthSettings->password().isEmpty() ) + parts.insert( QStringLiteral( "password" ), mAuthSettings->password() ); + else + parts.remove( QStringLiteral( "password" ) ); + + if ( !mEditReferer->text().isEmpty() ) + parts.insert( QStringLiteral( "referer" ), mEditReferer->text() ); + else + parts.remove( QStringLiteral( "referer" ) ); + + if ( !mAuthSettings->configId().isEmpty() ) + parts.insert( QStringLiteral( "authcfg" ), mAuthSettings->configId() ); + else + parts.remove( QStringLiteral( "authcfg" ) ); + + return QgsProviderRegistry::instance()->encodeUri( QgsSensorThingsProvider::SENSORTHINGS_PROVIDER_KEY, parts ); +} + +void QgsSensorThingsConnectionWidget::setUrl( const QString &url ) +{ + mEditUrl->setText( url ); +} + +QString QgsSensorThingsConnectionWidget::url() const +{ + return mEditUrl->text(); +} + +void QgsSensorThingsConnectionWidget::setUsername( const QString &username ) +{ + mAuthSettings->setUsername( username ); +} + +void QgsSensorThingsConnectionWidget::setPassword( const QString &password ) +{ + mAuthSettings->setPassword( password ); +} + +void QgsSensorThingsConnectionWidget::setAuthCfg( const QString &id ) +{ + mAuthSettings->setConfigId( id ); +} + +QString QgsSensorThingsConnectionWidget::username() const +{ + return mAuthSettings->username(); +} + +QString QgsSensorThingsConnectionWidget::password() const +{ + return mAuthSettings->password(); +} + +QString QgsSensorThingsConnectionWidget::authcfg() const +{ + return mAuthSettings->configId(); +} + +void QgsSensorThingsConnectionWidget::setReferer( const QString &referer ) +{ + mEditReferer->setText( referer ); +} + +QString QgsSensorThingsConnectionWidget::referer() const +{ + return mEditReferer->text(); +} + +void QgsSensorThingsConnectionWidget::validate() +{ + const bool valid = !mEditUrl->text().isEmpty(); + if ( valid == mIsValid ) + return; + + mIsValid = valid; + emit validChanged( mIsValid ); +} + +///@endcond PRIVATE diff --git a/src/gui/providers/sensorthings/qgssensorthingsconnectionwidget.h b/src/gui/providers/sensorthings/qgssensorthingsconnectionwidget.h new file mode 100644 index 00000000000..dfbd4e0fdfe --- /dev/null +++ b/src/gui/providers/sensorthings/qgssensorthingsconnectionwidget.h @@ -0,0 +1,68 @@ +/*************************************************************************** + qgssensorthingsconnectionwidget.h + -------------------------------------- + Date : December 2023 + Copyright : (C) 2023 by Nyall Dawson + Email : nyall dot dawson at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef QGSSENSORTHINGSCONNECTIONWIDGET_H +#define QGSSENSORTHINGSCONNECTIONWIDGET_H + +#include "ui_qgssensorthingsconnectionwidgetbase.h" +#include + +#define SIP_NO_FILE +///@cond PRIVATE + +class QgsSensorThingsConnectionWidget : public QWidget, private Ui::QgsSensorThingsConnectionWidgetBase +{ + Q_OBJECT + + public: + QgsSensorThingsConnectionWidget( QWidget *parent = nullptr ); + + void setSourceUri( const QString &uri ); + QString sourceUri() const; + + void setUrl( const QString &url ); + QString url() const; + + void setUsername( const QString &username ); + void setPassword( const QString &password ); + void setAuthCfg( const QString &id ); + + QString username() const; + QString password() const; + QString authcfg() const; + + void setReferer( const QString &referer ); + QString referer() const; + + bool isValid() const { return mIsValid; } + + signals: + + void changed(); + void validChanged( bool valid ); + + private slots: + + void validate(); + + private: + + QVariantMap mSourceParts; + bool mIsValid = false; +}; + +///@endcond PRIVATE +#endif // QGSSENSORTHINGSCONNECTIONWIDGET_H diff --git a/src/gui/providers/sensorthings/qgssensorthingsguiprovider.cpp b/src/gui/providers/sensorthings/qgssensorthingsguiprovider.cpp index c20938096be..45aabe0d2b6 100644 --- a/src/gui/providers/sensorthings/qgssensorthingsguiprovider.cpp +++ b/src/gui/providers/sensorthings/qgssensorthingsguiprovider.cpp @@ -25,8 +25,34 @@ #include #include "qgsmaplayer.h" +#include "qgssensorthingssourceselect.h" #include "qgssensorthingssourcewidget.h" #include "qgssensorthingsprovider.h" +#include "qgsapplication.h" + +// +// QgsSensorThingsSourceSelectProvider +// + +QString QgsSensorThingsSourceSelectProvider::providerKey() const +{ + return QgsSensorThingsProvider::SENSORTHINGS_PROVIDER_KEY; +} + +QString QgsSensorThingsSourceSelectProvider::text() const +{ + return QObject::tr( "SensorThings" ); +} + +QIcon QgsSensorThingsSourceSelectProvider::icon() const +{ + return QgsApplication::getThemeIcon( QStringLiteral( "/mActionAddXyzLayer.svg" ) ); +} + +QgsAbstractDataSourceWidget *QgsSensorThingsSourceSelectProvider::createDataSourceWidget( QWidget *parent, Qt::WindowFlags fl, QgsProviderRegistry::WidgetMode widgetMode ) const +{ + return new QgsSensorThingsSourceSelect( parent, fl, widgetMode ); +} // @@ -65,11 +91,14 @@ QgsSensorThingsProviderGuiMetadata::QgsSensorThingsProviderGuiMetadata(): { } +QList QgsSensorThingsProviderGuiMetadata::sourceSelectProviders() +{ + return { new QgsSensorThingsSourceSelectProvider }; +} + QList QgsSensorThingsProviderGuiMetadata::sourceWidgetProviders() { - QList providers; - providers << new QgsSensorThingsSourceWidgetProvider(); - return providers; + return { new QgsSensorThingsSourceWidgetProvider() }; } ///@endcond diff --git a/src/gui/providers/sensorthings/qgssensorthingsguiprovider.h b/src/gui/providers/sensorthings/qgssensorthingsguiprovider.h index 5e6ac8fae3a..183f2271e67 100644 --- a/src/gui/providers/sensorthings/qgssensorthingsguiprovider.h +++ b/src/gui/providers/sensorthings/qgssensorthingsguiprovider.h @@ -20,11 +20,23 @@ #define QGSSENSORTHINGSGUIPROVIDER_H #include "qgsproviderguimetadata.h" +#include "qgssourceselectprovider.h" #include "qgsprovidersourcewidgetprovider.h" ///@cond PRIVATE #define SIP_NO_FILE +class QgsSensorThingsSourceSelectProvider : public QgsSourceSelectProvider +{ + public: + + QString providerKey() const override; + QString text() const override; + int ordering() const override { return QgsSourceSelectProvider::OrderRemoteProvider + 40; } + QIcon icon() const override; + QgsAbstractDataSourceWidget *createDataSourceWidget( QWidget *parent = nullptr, Qt::WindowFlags fl = Qt::Widget, QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::Embedded ) const override; +}; + class QgsSensorThingsSourceWidgetProvider : public QgsProviderSourceWidgetProvider { public: @@ -38,6 +50,7 @@ class QgsSensorThingsProviderGuiMetadata: public QgsProviderGuiMetadata { public: QgsSensorThingsProviderGuiMetadata(); + QList sourceSelectProviders() override; QList sourceWidgetProviders() override; }; diff --git a/src/gui/providers/sensorthings/qgssensorthingssourceselect.cpp b/src/gui/providers/sensorthings/qgssensorthingssourceselect.cpp new file mode 100644 index 00000000000..b92c80325b3 --- /dev/null +++ b/src/gui/providers/sensorthings/qgssensorthingssourceselect.cpp @@ -0,0 +1,228 @@ +/*************************************************************************** + qgssensorthingssourceselect.cpp + --------------------------------- + Date : December 2023 + Copyright : (C) 2023 by Nyall Dawson + Email : nyall dot dawson at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgshelp.h" +#include "qgsgui.h" +#include "qgsmanageconnectionsdialog.h" +#include "qgssensorthingssourceselect.h" +#include "qgssensorthingsconnection.h" +#include "qgssensorthingsconnectionwidget.h" +#include "qgssensorthingsconnectiondialog.h" +#include "qgsowsconnection.h" +#include "qgssensorthingssourcewidget.h" +#include "qgssensorthingsprovider.h" + +#include +#include +#include + +///@cond PRIVATE + +QgsSensorThingsSourceSelect::QgsSensorThingsSourceSelect( QWidget *parent, Qt::WindowFlags fl, QgsProviderRegistry::WidgetMode theWidgetMode ) + : QgsAbstractDataSourceWidget( parent, fl, theWidgetMode ) +{ + setupUi( this ); + + setWindowTitle( tr( "Add SensorThings Layer" ) ); + mConnectionsGroupBox->setTitle( tr( "SensorThings Connections" ) ); + + mConnectionWidget = new QgsSensorThingsConnectionWidget(); + QVBoxLayout *vlayout = new QVBoxLayout(); + vlayout->setContentsMargins( 0, 0, 0, 0 ); + vlayout->addWidget( mConnectionWidget ); + mConnectionContainerWidget->setLayout( vlayout ); + + mSourceWidget = new QgsSensorThingsSourceWidget(); + vlayout = new QVBoxLayout(); + vlayout->setContentsMargins( 0, 0, 0, 0 ); + vlayout->addWidget( mSourceWidget ); + mLayerSettingsContainerWidget->setLayout( vlayout ); + + connect( mSourceWidget, &QgsProviderSourceWidget::validChanged, this, &QgsSensorThingsSourceSelect::validate ); + connect( mConnectionWidget, &QgsSensorThingsConnectionWidget::validChanged, this, &QgsSensorThingsSourceSelect::validate ); + + connect( mConnectionWidget, &QgsSensorThingsConnectionWidget::changed, this, [this] + { + if ( mBlockChanges ) + return; + + mBlockChanges++; + cmbConnections->setCurrentIndex( cmbConnections->findData( QStringLiteral( "~~custom~~" ) ) ); + mBlockChanges--; + } ); + + QgsGui::enableAutoGeometryRestore( this ); + + connect( btnNew, &QPushButton::clicked, this, &QgsSensorThingsSourceSelect::btnNew_clicked ); + connect( btnEdit, &QPushButton::clicked, this, &QgsSensorThingsSourceSelect::btnEdit_clicked ); + connect( btnDelete, &QPushButton::clicked, this, &QgsSensorThingsSourceSelect::btnDelete_clicked ); + connect( btnSave, &QPushButton::clicked, this, &QgsSensorThingsSourceSelect::btnSave_clicked ); + connect( btnLoad, &QPushButton::clicked, this, &QgsSensorThingsSourceSelect::btnLoad_clicked ); + connect( cmbConnections, &QComboBox::currentTextChanged, this, &QgsSensorThingsSourceSelect::cmbConnections_currentTextChanged ); +#if 0 // TODO when documentation page is available + connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsSensorThingsSourceSelect::showHelp ); +#endif + setupButtons( buttonBox ); + populateConnectionList(); +} + +void QgsSensorThingsSourceSelect::btnNew_clicked() +{ + const bool isCustom = cmbConnections->currentData().toString() == QLatin1String( "~~custom~~" ); + QgsSensorThingsConnectionDialog nc( this ); + if ( isCustom ) + { + // when creating a new connection, default to the current connection parameters + nc.connectionWidget()->setSourceUri( mConnectionWidget->sourceUri() ); + } + if ( nc.exec() ) + { + QgsSensorThingsConnectionUtils::addConnection( nc.connection() ); + + QgsSensorThingsConnection::sTreeSensorThingsConnections->setSelectedItem( nc.connection().name ); + populateConnectionList(); + emit connectionsChanged(); + } +} + +void QgsSensorThingsSourceSelect::btnEdit_clicked() +{ + QgsSensorThingsConnectionDialog nc( this ); + nc.setConnection( QgsSensorThingsConnectionUtils::connection( cmbConnections->currentText() ) ); + if ( nc.exec() ) + { + QgsSensorThingsConnectionUtils::addConnection( nc.connection() ); + populateConnectionList(); + emit connectionsChanged(); + } +} + +void QgsSensorThingsSourceSelect::btnDelete_clicked() +{ + const QString msg = tr( "Are you sure you want to remove the %1 connection and all associated settings?" ) + .arg( cmbConnections->currentText() ); + if ( QMessageBox::Yes != QMessageBox::question( this, tr( "Confirm Delete" ), msg, QMessageBox::Yes | QMessageBox::No ) ) + return; + + QgsSensorThingsConnectionUtils::deleteConnection( cmbConnections->currentText() ); + + populateConnectionList(); + emit connectionsChanged(); +} + +void QgsSensorThingsSourceSelect::btnSave_clicked() +{ +#if 0 + QgsManageConnectionsDialog dlg( this, QgsManageConnectionsDialog::Export, QgsManageConnectionsDialog::XyzTiles ); + dlg.exec(); +#endif +} + +void QgsSensorThingsSourceSelect::btnLoad_clicked() +{ +#if 0 + const QString fileName = QFileDialog::getOpenFileName( this, tr( "Load Connections" ), QDir::homePath(), + tr( "XML files (*.xml *.XML)" ) ); + if ( fileName.isEmpty() ) + { + return; + } + + QgsManageConnectionsDialog dlg( this, QgsManageConnectionsDialog::Import, QgsManageConnectionsDialog::XyzTiles, fileName ); + dlg.exec(); + populateConnectionList(); +#endif +} + +void QgsSensorThingsSourceSelect::addButtonClicked() +{ + const bool isCustom = cmbConnections->currentData().toString() == QLatin1String( "~~custom~~" ); + Q_NOWARN_DEPRECATED_PUSH + emit addVectorLayer( mSourceWidget->sourceUri(), isCustom ? tr( "SensorThings Layer" ) : cmbConnections->currentText(), QgsSensorThingsProvider::SENSORTHINGS_PROVIDER_KEY ); + Q_NOWARN_DEPRECATED_POP + emit addLayer( Qgis::LayerType::Vector, mSourceWidget->sourceUri(), isCustom ? tr( "SensorThings Layer" ) : cmbConnections->currentText(), QgsSensorThingsProvider::SENSORTHINGS_PROVIDER_KEY ); +} + +void QgsSensorThingsSourceSelect::populateConnectionList() +{ + cmbConnections->blockSignals( true ); + cmbConnections->clear(); + cmbConnections->addItem( tr( "Custom" ), QStringLiteral( "~~custom~~" ) ); + cmbConnections->addItems( QgsSensorThingsConnectionUtils::connectionList() ); + cmbConnections->blockSignals( false ); + + btnSave->setDisabled( cmbConnections->count() == 1 ); + + setConnectionListPosition(); +} + +void QgsSensorThingsSourceSelect::setConnectionListPosition() +{ + const QString toSelect = QgsSensorThingsConnection::sTreeSensorThingsConnections->selectedItem(); + + cmbConnections->setCurrentIndex( cmbConnections->findText( toSelect ) ); + + if ( cmbConnections->currentIndex() < 0 ) + { + if ( toSelect.isNull() ) + cmbConnections->setCurrentIndex( 0 ); + else + cmbConnections->setCurrentIndex( cmbConnections->count() - 1 ); + } + + const bool isCustom = cmbConnections->currentData().toString() == QLatin1String( "~~custom~~" ); + btnEdit->setDisabled( isCustom ); + btnDelete->setDisabled( isCustom ); +} + +void QgsSensorThingsSourceSelect::cmbConnections_currentTextChanged( const QString &text ) +{ + QgsSensorThingsConnection::sTreeSensorThingsConnections->setSelectedItem( text ); + + const bool isCustom = cmbConnections->currentData().toString() == QLatin1String( "~~custom~~" ); + btnEdit->setDisabled( isCustom ); + btnDelete->setDisabled( isCustom ); + + if ( !mBlockChanges ) + { + mBlockChanges++; + if ( isCustom ) + { + mConnectionWidget->setSourceUri( QString() ); + } + else + { + mConnectionWidget->setSourceUri( QgsSensorThingsConnectionUtils::connection( cmbConnections->currentText() ).encodedUri() ); + } + mBlockChanges--; + } +} + +void QgsSensorThingsSourceSelect::validate() +{ + const bool isValid = mConnectionWidget->isValid() && mSourceWidget->isValid(); + emit enableButtons( isValid ); +} + +#if 0 // TODO +void QgsSensorThingsSourceSelect::showHelp() +{ + QgsHelp::openHelp( QStringLiteral( "managing_data_source/opening_data.html#using-xyz-tile-services" ) ); +} +#endif + +///@endcond PRIVATE diff --git a/src/gui/providers/sensorthings/qgssensorthingssourceselect.h b/src/gui/providers/sensorthings/qgssensorthingssourceselect.h new file mode 100644 index 00000000000..fc555aea571 --- /dev/null +++ b/src/gui/providers/sensorthings/qgssensorthingssourceselect.h @@ -0,0 +1,68 @@ +/*************************************************************************** + qgssensorthingssourceselect.h + --------------------------------- + Date : December 2023 + Copyright : (C) 2023 by Nyall Dawson + Email : nyall dot dawson at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSSENSORTHINGSSOURCESELECT_H +#define QGSSENSORTHINGSSOURCESELECT_H + +#include "qgsabstractdatasourcewidget.h" +#include "ui_qgssensorthingssourceselectbase.h" + +class QgsSensorThingsSourceWidget; +class QgsSensorThingsConnectionWidget; + +///@cond PRIVATE +class QgsSensorThingsSourceSelect : public QgsAbstractDataSourceWidget, private Ui::QgsSensorThingsSourceSelectBase +{ + Q_OBJECT + + public: + QgsSensorThingsSourceSelect( QWidget *parent = nullptr, Qt::WindowFlags fl = QgsGuiUtils::ModalDialogFlags, QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::None ); + + //! Determines the layers the user selected + void addButtonClicked() override; + + private slots: + + //! Opens the create connection dialog to build a new connection + void btnNew_clicked(); + //! Opens a dialog to edit an existing connection + void btnEdit_clicked(); + //! Deletes the selected connection + void btnDelete_clicked(); + //! Saves connections to the file + void btnSave_clicked(); + //! Loads connections from the file + void btnLoad_clicked(); + //! Stores the selected datasource whenerver it is changed + void cmbConnections_currentTextChanged( const QString &text ); + + void validate(); + + private: + void populateConnectionList(); + void setConnectionListPosition(); +#if 0 + void showHelp(); +#endif + + QgsSensorThingsConnectionWidget *mConnectionWidget = nullptr; + QgsSensorThingsSourceWidget *mSourceWidget = nullptr; + int mBlockChanges = 0; +}; +///@endcond PRIVATE + +#endif // QGSSENSORTHINGSSOURCESELECT_H diff --git a/src/gui/providers/sensorthings/qgssensorthingssourcewidget.cpp b/src/gui/providers/sensorthings/qgssensorthingssourcewidget.cpp index 54895b06bdb..1be64291b41 100644 --- a/src/gui/providers/sensorthings/qgssensorthingssourcewidget.cpp +++ b/src/gui/providers/sensorthings/qgssensorthingssourcewidget.cpp @@ -144,10 +144,11 @@ void QgsSensorThingsSourceWidget::validate() if ( QgsSensorThingsUtils::entityTypeHasGeometry( entityType ) ) valid = valid && mComboGeometryType->currentIndex() >= 0; - if ( valid != mIsValid ) - emit validChanged( valid ); + if ( valid == mIsValid ) + return; mIsValid = valid; + emit validChanged( mIsValid ); } void QgsSensorThingsSourceWidget::rebuildGeometryTypes( Qgis::SensorThingsEntity type ) diff --git a/src/gui/providers/sensorthings/qgssensorthingssourcewidget.h b/src/gui/providers/sensorthings/qgssensorthingssourcewidget.h index 58b47097540..fcaf2de4c81 100644 --- a/src/gui/providers/sensorthings/qgssensorthingssourcewidget.h +++ b/src/gui/providers/sensorthings/qgssensorthingssourcewidget.h @@ -37,6 +37,8 @@ class QgsSensorThingsSourceWidget : public QgsProviderSourceWidget, protected Ui void setSourceUri( const QString &uri ) override; QString sourceUri() const override; + bool isValid() const { return mIsValid; } + private slots: void entityTypeChanged(); diff --git a/src/ui/qgssensorthingsconnectiondialog.ui b/src/ui/qgssensorthingsconnectiondialog.ui new file mode 100644 index 00000000000..0b1a2d2f375 --- /dev/null +++ b/src/ui/qgssensorthingsconnectiondialog.ui @@ -0,0 +1,85 @@ + + + QgsSensorThingsConnectionDialog + + + + 0 + 0 + 636 + 353 + + + + SensorThings Connection + + + + + + Name + + + + + + + Name of the new connection + + + + + + + Connection Details + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Help|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + QgsSensorThingsConnectionDialog + accept() + + + 224 + 381 + + + 157 + 274 + + + + + buttonBox + rejected() + QgsSensorThingsConnectionDialog + reject() + + + 292 + 387 + + + 286 + 274 + + + + + diff --git a/src/ui/qgssensorthingsconnectionwidgetbase.ui b/src/ui/qgssensorthingsconnectionwidgetbase.ui new file mode 100644 index 00000000000..58988236fab --- /dev/null +++ b/src/ui/qgssensorthingsconnectionwidgetbase.ui @@ -0,0 +1,109 @@ + + + QgsSensorThingsConnectionWidgetBase + + + + 0 + 0 + 360 + 168 + + + + SensorThings Connection + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Referer + + + mEditReferer + + + + + + + URL + + + + + + + Authentication + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + + + + + + Optional custom referer + + + + + + + URL of the connection, {x}, {y}, and {z} will be replaced with actual values. Use {-y} for inverted y axis. + + + http://example.com/v1.1/ + + + + + + + + QgsAuthSettingsWidget + QWidget +
qgsauthsettingswidget.h
+ 1 +
+ + QgsProviderSourceWidget + QWidget +
qgsprovidersourcewidget.h
+ 1 +
+
+ + mEditUrl + mEditReferer + + + +
diff --git a/src/ui/qgssensorthingssourceselectbase.ui b/src/ui/qgssensorthingssourceselectbase.ui new file mode 100644 index 00000000000..c2f34a97b37 --- /dev/null +++ b/src/ui/qgssensorthingssourceselectbase.ui @@ -0,0 +1,157 @@ + + + QgsSensorThingsSourceSelectBase + + + + 0 + 0 + 564 + 321 + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Help + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Connection Details + + + + + + + + + + + + Connections + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 171 + 30 + + + + + + + + + + + false + + + Edit selected service connection + + + Edit + + + + + + + false + + + Remove connection to selected service + + + Remove + + + + + + + Load connections from file + + + Load + + + + + + + Save connections to file + + + Save + + + + + + + Create a new service connection + + + &New + + + + + + + + + + Layer Settings + + + + + + + + + + + + cmbConnections + btnNew + btnEdit + btnDelete + btnLoad + btnSave + + + + diff --git a/src/ui/qgssensorthingssourcewidgetbase.ui b/src/ui/qgssensorthingssourcewidgetbase.ui index bd2a1e4fbe7..1da757758df 100644 --- a/src/ui/qgssensorthingssourcewidgetbase.ui +++ b/src/ui/qgssensorthingssourcewidgetbase.ui @@ -14,6 +14,18 @@ QgsSensorThingsSourceWidgetBase + + 0 + + + 0 + + + 0 + + + 0 +