From ede1c6394e90c76747cc6ab3e10dfce41e215cf3 Mon Sep 17 00:00:00 2001 From: NEDJIMAbelgacem Date: Tue, 23 Mar 2021 16:09:02 +0100 Subject: [PATCH] UI initial implementation for remote ept files --- .../qgspointcloudblockhandle.sip.in | 40 +++ .../pointcloud/qgspointcloudlayer.sip.in | 1 + .../auto_generated/qgsprovidermetadata.sip.in | 1 + .../auto_generated/qgsproviderregistry.sip.in | 1 + .../gui/auto_generated/qgisinterface.sip.in | 2 +- .../qgsabstractdatasourcewidget.sip.in | 2 +- src/app/qgisapp.cpp | 26 +- src/app/qgisapp.h | 3 +- src/app/qgisappinterface.cpp | 4 +- src/app/qgisappinterface.h | 2 +- .../pointcloud/qgspointcloudblockhandle.h | 14 +- src/core/pointcloud/qgspointcloudlayer.cpp | 7 +- src/core/pointcloud/qgspointcloudlayer.h | 3 + .../pointcloud/qgspointcloudlayerrenderer.cpp | 2 + src/core/providers/ept/qgseptprovider.cpp | 27 +- src/core/providers/ept/qgseptprovider.h | 5 + src/core/qgsprovidermetadata.cpp | 9 + src/core/qgsprovidermetadata.h | 3 + src/core/qgsproviderregistry.cpp | 9 + src/core/qgsproviderregistry.h | 3 + src/gui/providers/ogr/qgsogrsourceselect.h | 12 +- .../providers/qgspointcloudsourceselect.cpp | 118 +++++++- src/gui/providers/qgspointcloudsourceselect.h | 9 +- src/gui/qgisinterface.h | 2 +- src/gui/qgsabstractdatasourcewidget.h | 2 +- src/gui/qgsbrowserdockwidget_p.cpp | 5 +- src/gui/qgsdatasourcemanagerdialog.h | 2 +- src/ui/mesh/qgsmdalsourceselectbase.ui | 1 + .../qgspointcloudsourceselectbase.ui | 259 ++++++++++++++---- tests/src/providers/testqgspdalprovider.cpp | 2 + 30 files changed, 475 insertions(+), 101 deletions(-) create mode 100644 python/core/auto_generated/pointcloud/qgspointcloudblockhandle.sip.in diff --git a/python/core/auto_generated/pointcloud/qgspointcloudblockhandle.sip.in b/python/core/auto_generated/pointcloud/qgspointcloudblockhandle.sip.in new file mode 100644 index 00000000000..9900934a5de --- /dev/null +++ b/python/core/auto_generated/pointcloud/qgspointcloudblockhandle.sip.in @@ -0,0 +1,40 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/pointcloud/qgspointcloudblockhandle.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + +class QgsPointCloudBlockHandle : QObject +{ +%Docstring +Base class for handling loading :py:class:`QgsPointCloudBlock` asynchronously + +.. note:: + + The API is considered EXPERIMENTAL and can be changed without a notice + +.. versionadded:: 3.20 +%End + +%TypeHeaderCode +#include "qgspointcloudblockhandle.h" +%End + public: + QgsPointCloudBlockHandle( const QString &dataType, const QgsPointCloudAttributeCollection &attributes, const QgsPointCloudAttributeCollection &requestedAttributes, QgsTileDownloadManagerReply *tileDownloadManagerReply ); + signals: + void blockLoadingSucceeded( QgsPointCloudBlock *block ); + void blockLoadingFailed( const QString &errorStr ); +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/pointcloud/qgspointcloudblockhandle.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/core/auto_generated/pointcloud/qgspointcloudlayer.sip.in b/python/core/auto_generated/pointcloud/qgspointcloudlayer.sip.in index 52a3edfacca..a6528298840 100644 --- a/python/core/auto_generated/pointcloud/qgspointcloudlayer.sip.in +++ b/python/core/auto_generated/pointcloud/qgspointcloudlayer.sip.in @@ -50,6 +50,7 @@ Constructor for LayerOptions with optional ``transformContext``. explicit QgsPointCloudLayer( const QString &path = QString(), const QString &baseName = QString(), + const QString &dataSourceType = QString(), const QString &providerLib = QStringLiteral( "pointcloud" ), const QgsPointCloudLayer::LayerOptions &options = QgsPointCloudLayer::LayerOptions() ); %Docstring diff --git a/python/core/auto_generated/qgsprovidermetadata.sip.in b/python/core/auto_generated/qgsprovidermetadata.sip.in index e7853eb8a61..92a826e115d 100644 --- a/python/core/auto_generated/qgsprovidermetadata.sip.in +++ b/python/core/auto_generated/qgsprovidermetadata.sip.in @@ -313,6 +313,7 @@ Creates a new instance of the raster data provider. .. versionadded:: 3.10 %End + virtual bool createMeshData( const QgsMesh &mesh, const QString uri, diff --git a/python/core/auto_generated/qgsproviderregistry.sip.in b/python/core/auto_generated/qgsproviderregistry.sip.in index 45f4378b081..bbf1964cf5f 100644 --- a/python/core/auto_generated/qgsproviderregistry.sip.in +++ b/python/core/auto_generated/qgsproviderregistry.sip.in @@ -127,6 +127,7 @@ Creates new instance of raster data provider .. versionadded:: 3.10 %End + QList > pyramidResamplingMethods( const QString &providerKey ); %Docstring Returns list of raster pyramid resampling methods diff --git a/python/gui/auto_generated/qgisinterface.sip.in b/python/gui/auto_generated/qgisinterface.sip.in index 747e30eeaae..eed1bf3cf18 100644 --- a/python/gui/auto_generated/qgisinterface.sip.in +++ b/python/gui/auto_generated/qgisinterface.sip.in @@ -821,7 +821,7 @@ Adds a vector tile layer to the current project. .. versionadded:: 3.14 %End - virtual QgsPointCloudLayer *addPointCloudLayer( const QString &url, const QString &baseName, const QString &providerKey ) = 0; + virtual QgsPointCloudLayer *addPointCloudLayer( const QString &url, const QString &dataSourceType, const QString &baseName, const QString &providerKey ) = 0; %Docstring Adds a point cloud layer to the current project. diff --git a/python/gui/auto_generated/qgsabstractdatasourcewidget.sip.in b/python/gui/auto_generated/qgsabstractdatasourcewidget.sip.in index e5003e3be20..d7690de9c04 100644 --- a/python/gui/auto_generated/qgsabstractdatasourcewidget.sip.in +++ b/python/gui/auto_generated/qgsabstractdatasourcewidget.sip.in @@ -118,7 +118,7 @@ Emitted when a vector tile layer has been selected for addition. .. versionadded:: 3.14 %End - void addPointCloudLayer( const QString &url, const QString &baseName, const QString &providerKey ); + void addPointCloudLayer( const QString &url, const QString &baseName, const QString &dataSourceType, const QString &providerKey ); %Docstring Emitted when a point cloud layer has been selected for addition. diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 9cd66ae9cb5..d2304a1d89d 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -2008,7 +2008,9 @@ void QgisApp::handleDropUriList( const QgsMimeDataUtils::UriList &lst ) } else if ( u.layerType == QLatin1String( "pointcloud" ) ) { - addPointCloudLayer( uri, u.name, u.providerKey ); + QString dataSourceType = QStringLiteral( "remote" ); + if ( QFileInfo::exists( uri ) ) dataSourceType = QStringLiteral( "file" ); + addPointCloudLayer( uri, u.name, dataSourceType, u.providerKey ); } else if ( u.layerType == QLatin1String( "vector-tile" ) ) { @@ -5607,9 +5609,9 @@ QgsVectorTileLayer *QgisApp::addVectorTileLayer( const QString &url, const QStri return addVectorTileLayerPrivate( url, baseName ); } -QgsPointCloudLayer *QgisApp::addPointCloudLayer( const QString &url, const QString &baseName, const QString &providerKey ) +QgsPointCloudLayer *QgisApp::addPointCloudLayer( const QString &url, const QString &baseName, const QString &dataSourceType, const QString &providerKey ) { - return addPointCloudLayerPrivate( url, baseName, providerKey ); + return addPointCloudLayerPrivate( url, baseName, dataSourceType, providerKey ); } QgsVectorTileLayer *QgisApp::addVectorTileLayerPrivate( const QString &url, const QString &baseName, const bool guiWarning ) @@ -5654,7 +5656,12 @@ QgsVectorTileLayer *QgisApp::addVectorTileLayerPrivate( const QString &url, cons return layer.release(); } -QgsPointCloudLayer *QgisApp::addPointCloudLayerPrivate( const QString &uri, const QString &baseName, const QString &providerKey, bool guiWarning ) +QgsPointCloudLayer *QgisApp::addPointCloudLayerPrivate( + const QString &uri, + const QString &baseName, + const QString &dataSourceType, + const QString &providerKey, + bool guiWarning ) { QgsCanvasRefreshBlocker refreshBlocker; QgsSettings settings; @@ -5669,7 +5676,7 @@ QgsPointCloudLayer *QgisApp::addPointCloudLayerPrivate( const QString &uri, cons QgsDebugMsgLevel( "completeBaseName: " + base, 2 ); // create the layer - std::unique_ptr layer( new QgsPointCloudLayer( uri, base, providerKey ) ); + std::unique_ptr layer( new QgsPointCloudLayer( uri, base, dataSourceType, providerKey ) ); if ( !layer || !layer->isValid() ) { @@ -7454,8 +7461,13 @@ bool QgisApp::openLayer( const QString &fileName, bool allowInteractive ) break; case QgsMapLayerType::PointCloudLayer: - ok = static_cast< bool >( addPointCloudLayerPrivate( fileName, fileInfo.completeBaseName(), candidateProviders.at( 0 ).metadata()->key(), false ) ); - break; + { + QString dataSourceType = "remote"; + if ( QFileInfo::exists( fileName ) ) + dataSourceType = "file"; + ok = static_cast< bool >( addPointCloudLayerPrivate( fileName, fileInfo.completeBaseName(), dataSourceType, candidateProviders.at( 0 ).metadata()->key(), false ) ); + } + break; } } diff --git a/src/app/qgisapp.h b/src/app/qgisapp.h index 26e44343a87..c5d6c03d015 100644 --- a/src/app/qgisapp.h +++ b/src/app/qgisapp.h @@ -1161,7 +1161,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow * in the Map Legend so it should be formed in a meaningful way. * \since QGIS 3.18 */ - QgsPointCloudLayer *addPointCloudLayer( const QString &url, const QString &baseName, const QString &providerKey ); + QgsPointCloudLayer *addPointCloudLayer( const QString &url, const QString &baseName, const QString &dataSourceType, const QString &providerKey ); /** * \brief overloaded version of the private addLayer method that takes a list of @@ -2114,6 +2114,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow //! Open a point cloud layer - this is the generic function which takes all parameters QgsPointCloudLayer *addPointCloudLayerPrivate( const QString &uri, + const QString &dataSourceType, const QString &baseName, const QString &providerKey, bool guiWarning = true ); diff --git a/src/app/qgisappinterface.cpp b/src/app/qgisappinterface.cpp index deea8cbb675..f5f93a89423 100644 --- a/src/app/qgisappinterface.cpp +++ b/src/app/qgisappinterface.cpp @@ -168,9 +168,9 @@ QgsVectorTileLayer *QgisAppInterface::addVectorTileLayer( const QString &url, co return qgis->addVectorTileLayer( url, baseName ); } -QgsPointCloudLayer *QgisAppInterface::addPointCloudLayer( const QString &url, const QString &baseName, const QString &providerKey ) +QgsPointCloudLayer *QgisAppInterface::addPointCloudLayer( const QString &url, const QString &dataSourceType, const QString &baseName, const QString &providerKey ) { - return qgis->addPointCloudLayer( url, baseName, providerKey ); + return qgis->addPointCloudLayer( url, baseName, dataSourceType, providerKey ); } bool QgisAppInterface::addProject( const QString &projectName ) diff --git a/src/app/qgisappinterface.h b/src/app/qgisappinterface.h index a85fe8da652..5a57ebe3054 100644 --- a/src/app/qgisappinterface.h +++ b/src/app/qgisappinterface.h @@ -71,7 +71,7 @@ class APP_EXPORT QgisAppInterface : public QgisInterface QgsRasterLayer *addRasterLayer( const QString &url, const QString &baseName, const QString &providerKey ) override; QgsMeshLayer *addMeshLayer( const QString &url, const QString &baseName, const QString &providerKey ) override; QgsVectorTileLayer *addVectorTileLayer( const QString &url, const QString &baseName ) override; - QgsPointCloudLayer *addPointCloudLayer( const QString &url, const QString &baseName, const QString &providerKey ) override; + QgsPointCloudLayer *addPointCloudLayer( const QString &url, const QString &dataSourceType, const QString &baseName, const QString &providerKey ) override; bool addProject( const QString &projectName ) override; bool newProject( bool promptToSaveFlag = false ) override; void reloadConnections( ) override; diff --git a/src/core/pointcloud/qgspointcloudblockhandle.h b/src/core/pointcloud/qgspointcloudblockhandle.h index ae396a2f248..9e9991b7e1f 100644 --- a/src/core/pointcloud/qgspointcloudblockhandle.h +++ b/src/core/pointcloud/qgspointcloudblockhandle.h @@ -4,12 +4,22 @@ #include #include "qgspointcloudattribute.h" +#include "qgstiledownloadmanager.h" + +#define SIP_NO_FILE -class QgsTileDownloadManagerReply; class QgsPointCloudAttributeCollection; class QgsPointCloudBlock; -class QgsPointCloudBlockHandle : public QObject +/** + * \ingroup core + * \brief Base class for handling loading QgsPointCloudBlock asynchronously + * + * \note The API is considered EXPERIMENTAL and can be changed without a notice + * + * \since QGIS 3.20 + */ +class CORE_EXPORT QgsPointCloudBlockHandle : public QObject { Q_OBJECT public: diff --git a/src/core/pointcloud/qgspointcloudlayer.cpp b/src/core/pointcloud/qgspointcloudlayer.cpp index 1830b3cf641..5202d971110 100644 --- a/src/core/pointcloud/qgspointcloudlayer.cpp +++ b/src/core/pointcloud/qgspointcloudlayer.cpp @@ -32,13 +32,16 @@ #include "qgsmaplayerlegend.h" #include "qgsmaplayerfactory.h" #include +#include "qgseptprovider.h" QgsPointCloudLayer::QgsPointCloudLayer( const QString &path, const QString &baseName, + const QString &dataSourceType, const QString &providerLib, const QgsPointCloudLayer::LayerOptions &options ) : QgsMapLayer( QgsMapLayerType::PointCloudLayer, baseName, path ) , mElevationProperties( new QgsPointCloudLayerElevationProperties( this ) ) + , mDataSourceType( dataSourceType ) { if ( !path.isEmpty() && !providerLib.isEmpty() ) @@ -62,7 +65,7 @@ QgsPointCloudLayer *QgsPointCloudLayer::clone() const options.transformContext = transformContext(); options.skipCrsValidation = true; - QgsPointCloudLayer *layer = new QgsPointCloudLayer( source(), name(), mProviderKey, options ); + QgsPointCloudLayer *layer = new QgsPointCloudLayer( source(), name(), mDataSourceType, mProviderKey, options ); QgsMapLayer::clone( layer ); if ( mRenderer ) @@ -299,7 +302,7 @@ void QgsPointCloudLayer::setDataSource( const QString &dataSource, const QString flags |= QgsDataProvider::FlagTrustDataSource; } - mDataProvider.reset( qobject_cast( QgsProviderRegistry::instance()->createProvider( provider, dataSource, options, flags ) ) ); + mDataProvider.reset( qobject_cast( QgsProviderRegistry::instance()->createEptDataProvider( provider, dataSource, mDataSourceType, options, flags ) ) ); if ( !mDataProvider ) { QgsDebugMsg( QStringLiteral( "Unable to get point cloud data provider" ) ); diff --git a/src/core/pointcloud/qgspointcloudlayer.h b/src/core/pointcloud/qgspointcloudlayer.h index 3024083fe32..7adeadb118f 100644 --- a/src/core/pointcloud/qgspointcloudlayer.h +++ b/src/core/pointcloud/qgspointcloudlayer.h @@ -90,6 +90,7 @@ class CORE_EXPORT QgsPointCloudLayer : public QgsMapLayer */ explicit QgsPointCloudLayer( const QString &path = QString(), const QString &baseName = QString(), + const QString &dataSourceType = QString(), const QString &providerLib = QStringLiteral( "pointcloud" ), const QgsPointCloudLayer::LayerOptions &options = QgsPointCloudLayer::LayerOptions() ); @@ -185,6 +186,8 @@ class CORE_EXPORT QgsPointCloudLayer : public QgsMapLayer std::unique_ptr mRenderer; QgsPointCloudLayerElevationProperties *mElevationProperties = nullptr; + + QString mDataSourceType; }; diff --git a/src/core/pointcloud/qgspointcloudlayerrenderer.cpp b/src/core/pointcloud/qgspointcloudlayerrenderer.cpp index 16264e4f974..da5b0288588 100644 --- a/src/core/pointcloud/qgspointcloudlayerrenderer.cpp +++ b/src/core/pointcloud/qgspointcloudlayerrenderer.cpp @@ -17,6 +17,8 @@ #include #include +#include +#include #include "qgspointcloudlayerrenderer.h" #include "qgspointcloudlayer.h" diff --git a/src/core/providers/ept/qgseptprovider.cpp b/src/core/providers/ept/qgseptprovider.cpp index 63b1255f265..d29cd71bf37 100644 --- a/src/core/providers/ept/qgseptprovider.cpp +++ b/src/core/providers/ept/qgseptprovider.cpp @@ -16,6 +16,8 @@ ***************************************************************************/ #include "qgis.h" +#include "qgslogger.h" +#include "qgsproviderregistry.h" #include "qgseptprovider.h" #include "qgseptpointcloudindex.h" #include "qgsremoteeptpointcloudindex.h" @@ -33,13 +35,14 @@ QgsEptProvider::QgsEptProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, + const QString &dataSourceType, QgsDataProvider::ReadFlags flags ) : QgsPointCloudDataProvider( uri, options, flags ) { - if ( QFileInfo::exists( uri ) ) - mIndex.reset( new QgsEptPointCloudIndex ); - else + if ( dataSourceType == QStringLiteral( "remote" ) ) mIndex.reset( new QgsRemoteEptPointCloudIndex ); + else + mIndex.reset( new QgsEptPointCloudIndex ); std::unique_ptr< QgsScopedRuntimeProfile > profile; if ( QgsApplication::profiler()->groupIsActive( QStringLiteral( "projectload" ) ) ) @@ -48,6 +51,17 @@ QgsEptProvider::QgsEptProvider( loadIndex( ); } +QgsEptProvider *QgsEptProvider::create( const QString &providerKey, const QString &uri, const QString &dataSourceType, const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags ) +{ + QgsEptProvider *ret = QgsProviderRegistry::instance()->createEptDataProvider( providerKey, uri, dataSourceType, options, flags ); + if ( !ret ) + { + QgsDebugMsg( "Cannot resolve 'createEptDataProviderFunction' function in " + providerKey + " provider" ); + } + + return ret; +} + QgsEptProvider::~QgsEptProvider() = default; QgsCoordinateReferenceSystem QgsEptProvider::crs() const @@ -130,7 +144,12 @@ QgsEptProviderMetadata::QgsEptProviderMetadata(): QgsEptProvider *QgsEptProviderMetadata::createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags ) { - return new QgsEptProvider( uri, options, flags ); + return new QgsEptProvider( uri, options, "", flags ); +} + +QgsEptProvider *QgsEptProviderMetadata::createEptDataProvider( const QString &uri, const QString &dataSourceType, const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags ) +{ + return new QgsEptProvider( uri, options, dataSourceType, flags ); } QList QgsEptProviderMetadata::dataItemProviders() const diff --git a/src/core/providers/ept/qgseptprovider.h b/src/core/providers/ept/qgseptprovider.h index 4d144233dfc..1ce233a615a 100644 --- a/src/core/providers/ept/qgseptprovider.h +++ b/src/core/providers/ept/qgseptprovider.h @@ -38,9 +38,13 @@ class QgsEptProvider: public QgsPointCloudDataProvider public: QgsEptProvider( const QString &uri, const QgsDataProvider::ProviderOptions &providerOptions, + const QString &dataSourceType, QgsDataProvider::ReadFlags flags = QgsDataProvider::ReadFlags() ); ~QgsEptProvider(); + + static QgsEptProvider *create( const QString &providerKey, const QString &uri, const QString &dataSourceType, const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags ); + QgsCoordinateReferenceSystem crs() const override; QgsRectangle extent() const override; @@ -68,6 +72,7 @@ class QgsEptProviderMetadata : public QgsProviderMetadata QgsEptProviderMetadata(); QgsProviderMetadata::ProviderMetadataCapabilities capabilities() const override; QgsEptProvider *createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags = QgsDataProvider::ReadFlags() ) override; + QgsEptProvider *createEptDataProvider( const QString &uri, const QString &dataSourceType, const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags = QgsDataProvider::ReadFlags() ) override; QList< QgsDataItemProvider * > dataItemProviders() const override; int priorityForUri( const QString &uri ) const override; QList< QgsMapLayerType > validLayerTypesForUri( const QString &uri ) const override; diff --git a/src/core/qgsprovidermetadata.cpp b/src/core/qgsprovidermetadata.cpp index c8d36daf924..307b9e776ea 100644 --- a/src/core/qgsprovidermetadata.cpp +++ b/src/core/qgsprovidermetadata.cpp @@ -181,6 +181,15 @@ QgsRasterDataProvider *QgsProviderMetadata::createRasterDataProvider( return nullptr; } +QgsEptProvider *QgsProviderMetadata::createEptDataProvider( + const QString &, + const QString &, + const QgsDataProvider::ProviderOptions &, + QgsDataProvider::ReadFlags ) +{ + return nullptr; +} + bool QgsProviderMetadata::createMeshData( const QgsMesh &, const QString, diff --git a/src/core/qgsprovidermetadata.h b/src/core/qgsprovidermetadata.h index 0a808836ecd..6035eb3b889 100644 --- a/src/core/qgsprovidermetadata.h +++ b/src/core/qgsprovidermetadata.h @@ -43,6 +43,7 @@ class QgsTransaction; class QgsRasterDataProvider; class QgsMeshDataProvider; class QgsAbstractDatabaseProviderConnection; +class QgsEptProvider; struct QgsMesh; @@ -385,6 +386,8 @@ class CORE_EXPORT QgsProviderMetadata : public QObject const QgsCoordinateReferenceSystem &crs, const QStringList &createOptions = QStringList() ) SIP_FACTORY; + SIP_SKIP virtual QgsEptProvider *createEptDataProvider( const QString &uri, const QString &dataSourceType, const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags = QgsDataProvider::ReadFlags() ) SIP_FACTORY; + /** * Creates mesh data source, that is the mesh frame stored in file, memory or with other way (depending of the provider) * \since QGIS 3.16 diff --git a/src/core/qgsproviderregistry.cpp b/src/core/qgsproviderregistry.cpp index a3a79bc93c9..60d0161229b 100644 --- a/src/core/qgsproviderregistry.cpp +++ b/src/core/qgsproviderregistry.cpp @@ -570,6 +570,15 @@ QgsRasterDataProvider *QgsProviderRegistry::createRasterDataProvider( const QStr return nullptr; } +QgsEptProvider *QgsProviderRegistry::createEptDataProvider( const QString &providerKey, const QString &uri, const QString &dataSourceType, const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags ) +{ + QgsProviderMetadata *meta = findMetadata_( mProviders, providerKey ); + if ( meta ) + return meta->createEptDataProvider( uri, dataSourceType, options, flags ); + + return nullptr; +} + QList > QgsProviderRegistry::pyramidResamplingMethods( const QString &providerKey ) { QgsProviderMetadata *meta = findMetadata_( mProviders, providerKey ); diff --git a/src/core/qgsproviderregistry.h b/src/core/qgsproviderregistry.h index 3c1ac06c69e..f6601c76c31 100644 --- a/src/core/qgsproviderregistry.h +++ b/src/core/qgsproviderregistry.h @@ -39,6 +39,7 @@ class QgsCoordinateReferenceSystem; class QgsDataItemProvider; class QgsDataItem; class QgsRasterDataProvider; +class QgsEptProvider; /** * \ingroup core @@ -178,6 +179,8 @@ class CORE_EXPORT QgsProviderRegistry const QgsCoordinateReferenceSystem &crs, const QStringList &createOptions = QStringList() ) SIP_FACTORY; + SIP_SKIP QgsEptProvider *createEptDataProvider( const QString &providerKey, const QString &uri, const QString &dataSourceType, const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags = QgsDataProvider::ReadFlags() ) SIP_FACTORY; + /** * Returns list of raster pyramid resampling methods * diff --git a/src/gui/providers/ogr/qgsogrsourceselect.h b/src/gui/providers/ogr/qgsogrsourceselect.h index d43964ba812..9c0a8cd4751 100644 --- a/src/gui/providers/ogr/qgsogrsourceselect.h +++ b/src/gui/providers/ogr/qgsogrsourceselect.h @@ -51,13 +51,13 @@ class QgsOgrSourceSelect : public QgsAbstractDataSourceWidget, private Ui::QgsOg public: QgsOgrSourceSelect( QWidget *parent = nullptr, Qt::WindowFlags fl = Qt::WindowFlags(), QgsProviderRegistry::WidgetMode widgetMode = QgsProviderRegistry::WidgetMode::None ); - //! Opens a dialog to select a file datasource*/ + //! Opens a dialog to select a file datasource QStringList openFile(); - //! Opens a dialog to select a directory datasource*/ + //! Opens a dialog to select a directory datasource QString openDirectory(); - //! Returns a list of selected datasources*/ + //! Returns a list of selected datasources QStringList dataSources(); - //! Returns the encoding selected for user*/ + //! Returns the encoding selected for user QString encoding(); //! Returns the connection type QString dataSourceType(); @@ -65,9 +65,9 @@ class QgsOgrSourceSelect : public QgsAbstractDataSourceWidget, private Ui::QgsOg bool isProtocolCloudType(); private: - //! Stores the file vector filters */ + //! Stores the file vector filters QString mVectorFileFilter; - //! Stores the selected datasources */ + //! Stores the selected datasources QStringList mDataSources; //! Stores the user selected encoding QString mEnc; diff --git a/src/gui/providers/qgspointcloudsourceselect.cpp b/src/gui/providers/qgspointcloudsourceselect.cpp index d5924bcae58..c9f2ce1c4ee 100644 --- a/src/gui/providers/qgspointcloudsourceselect.cpp +++ b/src/gui/providers/qgspointcloudsourceselect.cpp @@ -27,6 +27,13 @@ QgsPointCloudSourceSelect::QgsPointCloudSourceSelect( QWidget *parent, Qt::Windo setupUi( this ); setupButtons( buttonBox ); + connect( mRadioSrcFile, &QRadioButton::toggled, this, &QgsPointCloudSourceSelect::radioSrcFile_toggled ); + connect( mRadioSrcProtocol, &QRadioButton::toggled, this, &QgsPointCloudSourceSelect::radioSrcProtocol_toggled ); + connect( cmbProtocolTypes, &QComboBox::currentTextChanged, this, &QgsPointCloudSourceSelect::cmbProtocolTypes_currentIndexChanged ); + + radioSrcFile_toggled( true ); + setProtocolWidgetsVisibility(); + mFileWidget->setDialogTitle( tr( "Open Point Cloud Dataset" ) ); mFileWidget->setFilter( QgsProviderRegistry::instance()->filePointCloudFilters() ); mFileWidget->setStorageMode( QgsFileWidget::GetMultipleFiles ); @@ -35,27 +42,114 @@ QgsPointCloudSourceSelect::QgsPointCloudSourceSelect( QWidget *parent, Qt::Windo mPath = path; emit enableButtons( ! mPath.isEmpty() ); } ); + + connect( protocolURI, &QLineEdit::textChanged, this, [ = ]( const QString & path ) + { + mPath = path; + emit enableButtons( ! mPath.isEmpty() ); + } ); + + + QStringList protocolTypes = QStringLiteral( "HTTP/HTTPS/FTP,vsicurl" ).split( ';' ); + for ( int i = 0; i < protocolTypes.count(); i++ ) + { + QString protocolType = protocolTypes.at( i ); + if ( ( !protocolType.isEmpty() ) && ( !protocolType.isNull() ) ) + cmbProtocolTypes->addItem( protocolType.split( ',' ).at( 0 ) ); + } } void QgsPointCloudSourceSelect::addButtonClicked() { - if ( mPath.isEmpty() ) + qDebug() << __PRETTY_FUNCTION__ << mDataSourceType; + if ( mDataSourceType == QStringLiteral( "file" ) ) { - QMessageBox::information( this, - tr( "Add Point Cloud Layers" ), - tr( "No layers selected." ) ); - return; + qDebug() << __PRETTY_FUNCTION__ << " : " << mPath; + if ( mPath.isEmpty() ) + { + QMessageBox::information( this, + tr( "Add Point Cloud Layers" ), + tr( "No layers selected." ) ); + return; + } + + for ( const QString &path : QgsFileWidget::splitFilePaths( mPath ) ) + { + // auto determine preferred provider for each path + + const QList< QgsProviderRegistry::ProviderCandidateDetails > preferredProviders = QgsProviderRegistry::instance()->preferredProvidersForUri( mPath ); + // maybe we should raise an assert if preferredProviders size is 0 or >1? Play it safe for now... + if ( preferredProviders.empty() ) + continue; + qDebug() << "preferredProviders.at( 0 ).metadata()->key(): " << preferredProviders.at( 0 ).metadata()->key(); + emit addPointCloudLayer( path, QFileInfo( path ).baseName(), mDataSourceType, preferredProviders.at( 0 ).metadata()->key() ) ; + } } - - for ( const QString &path : QgsFileWidget::splitFilePaths( mPath ) ) + else if ( mDataSourceType == QStringLiteral( "remote" ) ) { - // auto determine preferred provider for each path + if ( mPath.isEmpty() ) + { + QMessageBox::information( this, + tr( "Add Point Cloud Layers" ), + tr( "No layers selected." ) ); + return; + } + // auto determine preferred provider for each path const QList< QgsProviderRegistry::ProviderCandidateDetails > preferredProviders = QgsProviderRegistry::instance()->preferredProvidersForUri( mPath ); // maybe we should raise an assert if preferredProviders size is 0 or >1? Play it safe for now... - if ( preferredProviders.empty() ) - continue; - - emit addPointCloudLayer( path, QFileInfo( path ).baseName(), preferredProviders.at( 0 ).metadata()->key() ) ; + if ( !preferredProviders.empty() ) + emit addPointCloudLayer( mPath, QFileInfo( mPath ).baseName(), mDataSourceType, preferredProviders.at( 0 ).metadata()->key() ) ; } } + +void QgsPointCloudSourceSelect::radioSrcFile_toggled( bool checked ) +{ + if ( checked ) + { + fileGroupBox->show(); + protocolGroupBox->hide(); + + mFileWidget->setDialogTitle( tr( "Open Point Cloud Dataset" ) ); + mFileWidget->setFilter( QgsProviderRegistry::instance()->filePointCloudFilters() ); + mFileWidget->setStorageMode( QgsFileWidget::GetMultipleFiles ); + + mDataSourceType = QStringLiteral( "file" ); + + emit enableButtons( ! mFileWidget->filePath().isEmpty() ); + } +} + +void QgsPointCloudSourceSelect::radioSrcProtocol_toggled( bool checked ) +{ + if ( checked ) + { + fileGroupBox->hide(); + protocolGroupBox->show(); + + mDataSourceType = QStringLiteral( "remote" ); + + setProtocolWidgetsVisibility(); + + emit enableButtons( ! protocolURI->text().isEmpty() ); + } +} + +void QgsPointCloudSourceSelect::cmbProtocolTypes_currentIndexChanged( const QString &text ) +{ + Q_UNUSED( text ) + setProtocolWidgetsVisibility(); +} + +void QgsPointCloudSourceSelect::setProtocolWidgetsVisibility() +{ + labelProtocolURI->show(); + protocolURI->show(); + mAuthGroupBox->show(); + labelBucket->hide(); + mBucket->hide(); + labelKey->hide(); + mKey->hide(); + mAuthWarning->hide(); +} + diff --git a/src/gui/providers/qgspointcloudsourceselect.h b/src/gui/providers/qgspointcloudsourceselect.h index bb1a040cd3c..48a5e376518 100644 --- a/src/gui/providers/qgspointcloudsourceselect.h +++ b/src/gui/providers/qgspointcloudsourceselect.h @@ -44,9 +44,16 @@ class QgsPointCloudSourceSelect : public QgsAbstractDataSourceWidget, private Ui //! Determines the tables the user selected and closes the dialog void addButtonClicked() override; + private slots: + void radioSrcProtocol_toggled( bool checked ); + void radioSrcFile_toggled( bool checked ); + void cmbProtocolTypes_currentIndexChanged( const QString &text ); + + //! Sets protocol-related widget visibility + void setProtocolWidgetsVisibility(); private: QString mPath; - + QString mDataSourceType; }; ///@endcond diff --git a/src/gui/qgisinterface.h b/src/gui/qgisinterface.h index 13fd74990f3..7bd7e0952f0 100644 --- a/src/gui/qgisinterface.h +++ b/src/gui/qgisinterface.h @@ -726,7 +726,7 @@ class GUI_EXPORT QgisInterface : public QObject * Adds a point cloud layer to the current project. * \since QGIS 3.18 */ - virtual QgsPointCloudLayer *addPointCloudLayer( const QString &url, const QString &baseName, const QString &providerKey ) = 0; + virtual QgsPointCloudLayer *addPointCloudLayer( const QString &url, const QString &dataSourceType, const QString &baseName, const QString &providerKey ) = 0; //! Adds (opens) a project virtual bool addProject( const QString &project ) = 0; diff --git a/src/gui/qgsabstractdatasourcewidget.h b/src/gui/qgsabstractdatasourcewidget.h index 7a1cee5afa7..7d333e1702e 100644 --- a/src/gui/qgsabstractdatasourcewidget.h +++ b/src/gui/qgsabstractdatasourcewidget.h @@ -131,7 +131,7 @@ class GUI_EXPORT QgsAbstractDataSourceWidget : public QDialog * Emitted when a point cloud layer has been selected for addition. * \since QGIS 3.18 */ - void addPointCloudLayer( const QString &url, const QString &baseName, const QString &providerKey ); + void addPointCloudLayer( const QString &url, const QString &baseName, const QString &dataSourceType, const QString &providerKey ); /** * Emitted when one or more OGR supported layers are selected for addition diff --git a/src/gui/qgsbrowserdockwidget_p.cpp b/src/gui/qgsbrowserdockwidget_p.cpp index b08c15c4d1b..1129ff9fc41 100644 --- a/src/gui/qgsbrowserdockwidget_p.cpp +++ b/src/gui/qgsbrowserdockwidget_p.cpp @@ -223,7 +223,10 @@ void QgsBrowserLayerProperties::setItem( QgsDataItem *item ) QgsDebugMsgLevel( QStringLiteral( "creating point cloud layer" ), 2 ); QgsPointCloudLayer::LayerOptions options { QgsProject::instance()->transformContext() }; options.skipCrsValidation = true; - mLayer = std::make_unique< QgsPointCloudLayer >( layerItem->uri(), layerItem->name(), layerItem->providerKey(), options ); + QString dataSourceType = QStringLiteral( "remote" ); + if ( QFileInfo::exists( layerItem->uri() ) ) + dataSourceType = QStringLiteral( "file" ); + mLayer = std::make_unique< QgsPointCloudLayer >( layerItem->uri(), layerItem->name(), dataSourceType, layerItem->providerKey(), options ); break; } diff --git a/src/gui/qgsdatasourcemanagerdialog.h b/src/gui/qgsdatasourcemanagerdialog.h index 6a75284c194..070da8ed6be 100644 --- a/src/gui/qgsdatasourcemanagerdialog.h +++ b/src/gui/qgsdatasourcemanagerdialog.h @@ -137,7 +137,7 @@ class GUI_EXPORT QgsDataSourceManagerDialog : public QgsOptionsDialogBase, priva * Emitted when a point cloud layer was selected for addition: for signal forwarding to QgisApp * \since QGIS 3.18 */ - void addPointCloudLayer( const QString &pointCloudLayerPath, const QString &baseName, const QString &providerKey ); + void addPointCloudLayer( const QString &pointCloudLayerPath, const QString &baseName, const QString &dataSourceType, const QString &providerKey ); //! Replace the selected layer by a vector layer defined by uri, layer name, data source uri void replaceSelectedVectorLayer( const QString &oldId, const QString &uri, const QString &layerName, const QString &provider ); diff --git a/src/ui/mesh/qgsmdalsourceselectbase.ui b/src/ui/mesh/qgsmdalsourceselectbase.ui index baca36d6b7a..88afb6e8e6b 100644 --- a/src/ui/mesh/qgsmdalsourceselectbase.ui +++ b/src/ui/mesh/qgsmdalsourceselectbase.ui @@ -81,6 +81,7 @@ QgsFileWidget QWidget
qgsfilewidget.h
+ 1 diff --git a/src/ui/pointcloud/qgspointcloudsourceselectbase.ui b/src/ui/pointcloud/qgspointcloudsourceselectbase.ui index a1a99b64b6f..36aac75bdfa 100644 --- a/src/ui/pointcloud/qgspointcloudsourceselectbase.ui +++ b/src/ui/pointcloud/qgspointcloudsourceselectbase.ui @@ -6,8 +6,8 @@ 0 0 - 351 - 119 + 584 + 495 @@ -23,37 +23,174 @@ true - - - - - - - - 0 - 0 - - - - Source - - - - - - Point cloud dataset - - - - - - - - - - + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Protocol + + + + + + + + + Type + + + + + + + &URI + + + protocolURI + + + + + + + + + + Bucket or container + + + mBucket + + + + + + + + + + Object key + + + mKey + + + + + + + + + + + + + true + + + true + + + + + + + Authentication + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + + 0 + 0 + + + + + + + + + - + + + + QDialogButtonBox::Help + + + + + + + + 0 + 0 + + + + Source Type + + + + + + F&ile + + + true + + + + + + + Protoco&l: HTTP(S), cloud, etc. + + + + + + + Qt::Horizontal + + + + 20 + 40 + + + + + + + + Qt::Vertical @@ -66,13 +203,34 @@ - - - - QDialogButtonBox::NoButton + + + + + 0 + 0 + + + Source + + + + + + Point cloud dataset(s) + + + + + + + + + + @@ -81,28 +239,15 @@ QgsFileWidget QWidget
qgsfilewidget.h
+ 1 + + + QgsAuthSettingsWidget + QWidget +
auth/qgsauthsettingswidget.h
+ 1
- - buttonBox - - - - buttonBox - rejected() - QgsPointCloudSourceSelectBase - reject() - - - 518 - 510 - - - 551 - 370 - - - - + diff --git a/tests/src/providers/testqgspdalprovider.cpp b/tests/src/providers/testqgspdalprovider.cpp index 3f1351d873e..e10d8c45fac 100644 --- a/tests/src/providers/testqgspdalprovider.cpp +++ b/tests/src/providers/testqgspdalprovider.cpp @@ -162,6 +162,7 @@ void TestQgsPdalProvider::brokenPath() std::unique_ptr< QgsPointCloudLayer > layer = std::make_unique< QgsPointCloudLayer >( QStringLiteral( "not valid" ), QStringLiteral( "layer" ), + QStringLiteral( "file" ), QStringLiteral( "pdal" ) ); QVERIFY( !layer->isValid() ); } @@ -174,6 +175,7 @@ void TestQgsPdalProvider::validLayer() std::unique_ptr< QgsPointCloudLayer > layer = std::make_unique< QgsPointCloudLayer >( mTestDataDir + QStringLiteral( "point_clouds/las/cloud.las" ), QStringLiteral( "layer" ), + QStringLiteral( "file" ), QStringLiteral( "pdal" ), options );